C ARM:链接寄存器和帧指针

C ARM:链接寄存器和帧指针,c,arm,C,Arm,我试图理解链接寄存器和帧指针在ARM中是如何工作的。我去过几个网站,我想确认我的理解 假设我有以下代码: int foo(void) { // .. bar(); // (A) // .. } int bar(void) { // (B) int b1; // .. // (C) baz(); // (D) } int baz(void) { // (E) int a; int b;

我试图理解链接寄存器和帧指针在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)处代码的地址?声明所有局部变量后,堆栈指针可以是bar()内的任何位置?

一些寄存器调用约定依赖于(应用程序二进制接口)。
FP
APCS标准中是必需的,而在较新的AAPCS(2003)中不是必需的。对于AAPC(GCC 5.0+)而言,
FP
没有可供使用,但肯定可以使用;带有堆栈和帧指针,用于堆栈跟踪和使用AAPCS展开代码。如果函数是静态的,编译器实际上不必遵守任何约定

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

通常,我们有
sp
(堆栈指针,R13)和
fp
(,R11)。这两者也有关联。这 描述事物做得很好。堆栈用于在函数中存储临时数据或局部变量。
foo()
bar()
中的任何变量都存储在堆栈或可用寄存器中。
fp
跟踪函数之间的变量。它是堆栈上用于该函数的帧或图片窗口。ABI定义了此框架的布局。通常,编译器将
lr
和其他寄存器以及先前的
fp
值保存在后台。这将生成堆栈帧的链接列表,如果需要,可以一直跟踪到
main()
。根是
fp
,它指向一个堆栈帧(如
struct
),其中
struct
中的一个变量是前一个
fp
。您可以沿着列表一直到最后一个
fp
,通常为
NULL

因此,
sp
是堆栈所在的位置,
fp
是堆栈所在的位置,非常类似于
pc
lr
。每个旧的
lr
(链接寄存器)存储在旧的
fp
(帧指针)中。
sp
fp
是功能的数据方面

您的点B是活动的
pc
sp
。点A实际上是
fp
lr
;除非您调用另一个函数,然后编译器可能准备设置
fp
以指向B中的数据

下面是一些ARM汇编程序,可以演示这一切是如何工作的。这将根据编译器的优化方式有所不同,但它应该给出一个想法

;序言-设置
mov-ip,sp;获取sp的副本。
stmdb sp!,{fp,ip,lr,pc};将帧保存在堆栈上。见附录
副fp,ip,#4;设置新的帧指针。
...
; 可能在这里调用了其他函数。
; 较旧的调用方返回存储在堆栈帧中的
lr
。 比尔巴兹 ... ; 结语-返回 ldm-sp,{fp,sp,lr};还原堆栈、帧指针和旧链接。 ... ; 也许这里有更多的东西。 bx-lr;返回。
这就是
foo()
的样子。如果不调用
bar()
,那么编译器将执行叶优化,并且不需要保存帧;只需要
bx lr
。很可能这就是为什么你会被网络示例弄糊涂的原因。它并不总是一样的

外卖应该是,

  • pc
    lr
    是相关的code寄存器。一个是“你在哪里”,另一个是“你在哪里”
  • sp
    fp
    是相关的本地数据寄存器。
    一个是“本地数据在哪里”,另一个是“最后一个本地数据在哪里”
  • 这些机器一起工作,创造出功能强大的机器
  • 很难描述一般情况,因为我们希望编译器尽可能快,所以他们会尽可能地使用各种技巧
  • 这些概念对于所有CPU和编译语言都是通用的,尽管细节可能有所不同。链接寄存器、帧指针的使用是和结语的一部分,如果您了解了所有内容,您就会知道堆栈溢出如何在ARM上工作

    另请参见:。




    基本框架布局是

    • fp[-0]保存了
      pc
      ,我们将此帧存储在此处
    • fp[-1]保存了此函数的返回地址
      lr
    • fp[-2]在该函数吃掉堆栈之前,上一个
      sp
    • fp[-3]上一个
      fp
      ,最后一个堆栈帧
    • 许多可选寄存器
    ABI可以使用其他值,但以上是大多数设置的典型值。上面的索引用于32位值,因为所有ARM寄存器都是32位的。如果以字节为中心,则乘以4。帧还与至少四个字节对齐


    附录:这不是汇编程序中的错误;这是正常的。问题中有一个解释。

    免责声明:我认为这大致正确;请根据需要更正

    如本文件其他部分所述