ARM GCC 生成的函数序言

问题描述 投票:0回答:1

我提到 ARM 工具链可以生成不同的函数序言。实际上,我看到两个 obj 文件(vmlinux)具有完全不同的函数序言:

第一个案例如下:

push {some registers maybe, fp, lr} (lr ommited in leaf function)

第二种情况如下:

push {some registers maybe, fp, sp, lr, pc} (i can confuse the order)

据我所知,第二个额外推动了 pc 和 sp。另外,我在崩溃实用程序(kdump 项目)中看到了一些评论,其中指出,内核堆栈帧应该具有格式 {..., fp, sp, lr, pc} 这让我更加困惑,因为我发现在某些情况下它不是是的。

1.) 我关于在函数序言中额外推送 pc 和 sp 需要一些 gcc 额外标志的说法正确吗?如果是,它们是什么?.

2.) 这是做什么用的?基本上,据我了解,我只能使用 FP 和 LR 展开堆栈,为什么我需要这个附加值?

3.)如果这件事与编译标志无关 - 我如何强制生成此扩展函数序言,目的是什么?

谢谢你。

c gcc assembly stack arm
1个回答
3
投票

1.) 我关于在函数序言中额外推送 pc 和 sp 需要一些 gcc 额外标志的说法正确吗?如果是,它们是什么?.

有许多 gcc 选项会影响堆栈帧(

-march
-mtune
等可能会影响例如使用的指令)。就你而言,它是
-mapcs-frame
。此外,
-fomit-frame-pointer
将从叶函数中删除框架。多个静态函数可以合并到一个生成函数中,进一步减少帧数。 APCS 可能会导致代码稍微慢一些,但对于 simple 堆栈跟踪来说是必需的。

2.) 这是做什么用的?基本上,据我了解,我只能使用 FP 和 LR 展开堆栈,为什么我需要这个附加值?

所有非参数的寄存器(r0-r3)都需要保存,因为它们在返回给调用者时需要恢复。编译器将在堆栈上分配额外的局部变量,因此当

sp
更改时,
fp
几乎总是会更改。有关存储
pc
的原因,请参阅下文。

3.)如果这件事与编译标志无关 - 我如何强制生成此扩展函数序言,目的是什么?

正如您所猜测的那样,它是编译器标志。

; Prologue - setup
mov     ip, sp                 ; get a copy of sp.
stm     sp!, {fp, ip, lr, pc}  ; Save the frame on the stack. See Addendum
sub     fp, ip, #4             ; Set the new frame pointer.
    ...
; Epilogue - return
ldm     sp, {fp, sp, lr}       ; restore stack, frame pointer and old link.
    ...                        ; maybe more stuff here.
bx      lr                     ; return.

典型的保存是

stm sp!, {fp, ip, lr, pc}
,恢复是
ldm sp, {fp, sp, lr}
。如果您检查 ABI/APCS 文档,这是正确的。请注意,没有“!”尝试修复堆栈。它是从存储的
ip
值显式加载的。

此外,保存的

pc
不会在尾声中使用。它只是堆栈上丢弃的数据。那么为什么要这样做呢?异常处理程序(中断、信号或 C++ 异常)和其他堆栈跟踪机制想要知道谁保存了帧。 ARM 始终只有一个函数prologue(一个入口点)。然而,有多个出口。在某些情况下,像
return function();
这样的返回实际上可能会变成
b function
中的 也许更多的东西在这里。这称为尾调用。此外,当在例程中间调用叶子函数并发生异常时,它将看到
PC
范围的叶子,但叶子可能没有调用框架。通过保存
pc
,可以在leaf发生异常时检查调用帧,以了解谁真正保存了堆栈。
pc
与析构函数等的表可能会被存储以允许释放 objects 或弄清楚如何调用信号处理程序。额外的
pc
在跟踪堆栈时非常好,并且由于管道衬里,操作几乎是免费的。

另请参阅:ARM 链接和帧寄存器有关编译器如何使用这些寄存器的问题。

© www.soinside.com 2019 - 2024. All rights reserved.