ARM:链接寄存器和帧指针

我想了解如何链接寄存器和帧指针在ARM中工作。 我去过几个地方,我想确认我的理解。

假设我有以下代码:

int foo(void) { // .. bar(); // (A) // .. } int bar(void) { // (B) int b1; // .. // (C) baz(); // (D) } int baz(void) { // (E) int a; int b; // (F) } 

我叫foo()。 链接寄存器是否包含点(A)的代码地址,而帧指针是否包含点(B)的代码地址? 在所有的locals被声明之后,栈指针可能会在bar()里面的任何地方。

增加另一个函数调用baz()

一些注册调用约定依赖于ABI (应用程序二进制接口)。 FPAPCS标准要求的FP ,而不是新的AAPCS (2003)。 对于AAPCS (GCC 5.0+) FP不一定要用,但肯定可以; debugging信息使用堆栈和帧指针进行注释,用于堆栈跟踪和使用AAPCS展开代码。 如果一个函数是static ,编译器实际上不必遵守任何约定。

通常所有的ARM寄存器都是通用的lr (链接寄存器,也是R14)和pc (程序计数器也是R15)是特殊的,并包含在指令集中。 你是正确的, lr会指向A。 pclr是相关的。 一个是“你在哪里”,另一个是“你在哪里”。 它们是函数的代码方面。

通常,我们有sp (堆栈指针,R13)和fp ( 帧指针 ,R11)。 这两个也有关系。 这个微软的布局做了很好的描述。 该堆栈用于在您的function中存储临时数据或本地数据。 foo()bar()任何variables都存储在堆栈或可用寄存器中。 fp跟踪从function到function的variables。 它是该function的堆栈或图片窗口。 ABI定义了这个框架的布局。 通常, lr和其他寄存器在编译器的后台保存在这里,以及fp的前一个值。 这使得一个堆栈帧的链表 ,如果你想,你可以追溯到main()fp ,它指向一个堆栈框架(像一个struct ), struct中的一个variables是前一个fp 。 你可以沿着列表直到最终的fp ,通常是NULL

所以sp是栈的位置, fp是栈的位置,很像pclr 。 每个旧的lr (链接寄存器)都存储在旧的fp (帧指针)中。 spfp是函数的数据方面。

你的B点是活动pcspA点实际上是fplr ; 除非你调用另一个函数,然后编译器可能已经准备好将fp设置为指向B中的数据。

以下是一些ARM汇编器,可能会演示如何工作。 这取决于编译器如何优化会有所不同,但它应该给出一个想法,

 ; Prologue - setup mov ip, sp ; get a copy of sp. stmdb sp!, {fp, ip, lr, pc} ; Save the frame on the stack. See Addendum sub fp, ip, #4 ; Set the new frame pointer. ... ; Maybe other functions called here. 
; Older caller return
lr stored in stack frame. bl baz ... ; Epilogue - return ldm sp, {fp, sp, lr} ; restore stack, frame pointer and old link. ... ; maybe more stuff here. bx lr ; return.

这就是foo()样子。 如果你不调用bar() ,那么编译器会进行叶子优化 ,不需要保存 ; 只需要bx lr 。 很可能这也许就是为什么你被networking例子弄糊涂了。 它并不总是一样的。

外卖应该是,

  1. pclr是相关的代码寄存器。 一个是“你在哪里”,另一个是“你在哪里”。
  2. spfp是相关的本地数据寄存器。
    一个是“本地数据在哪里”,另一个是“本地数据在哪里”。
  3. 与parameter passing一起共同创造function机械。
  4. 由于我们希望编译器尽可能 ,所以很难描述一般情况,所以他们尽可能地利用每一个技巧。

这些概念对于所有的CPU和编译语言都是通用的,尽pipe细节可能有所不同。 使用链接寄存器帧指针是函数序言和结尾的一部分,如果你理解了所有的东西,你就知道堆栈溢出在ARM上是如何工作的。

另请参阅: ARM调用约定 。
MSDN ARM堆栈文章
剑桥大学APCS概述
ARM堆栈跟踪博客
苹果ABI链接

基本的框架布局是,

  • fp [-0]保存了pc ,我们在这里存储了这个帧。
  • fp [-1]保存了lr ,这个函数的返回地址。
  • fp [-2]之前的sp ,在此function之前堆栈。
  • fp [-3]前面的fp ,最后一个堆栈帧
  • 许多可选寄存器…

ABI可能会使用其他值,但以上是大多数设置的典型值。

附录:这在汇编程序中不是错误的。 这是正常的。 一个解释是在ARM生成的序言问题。