ARM:链接寄存器和帧指针 [英] ARM: link register and frame pointer

查看:34
本文介绍了ARM:链接寄存器和帧指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解链接寄存器和帧指针在 ARM 中是如何工作的.我去过几个网站,我想确认一下我的理解.

I'm trying to understand how the link register and the frame pointer work in ARM. I've been to a couple of sites, and I wanted to confirm my understanding.

假设我有以下代码:

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() 内的任何位置?

and I call foo(). Would the link register contain the address for the code at point (A) and the frame pointer contain the address at the code at point (B)? And the stack pointer would could be any where inside bar(), after all the locals have been declared?

推荐答案

某些寄存器调用约定依赖于 ABI(应用程序二进制接口).FPAPCS 标准中是必需的,而在较新的 AAPCS (2003) 中则不需要.对于AAPCS (GCC 5.0+),FP 没有必须使用,但肯定可以使用;调试信息使用堆栈和帧指针进行注释用于使用 AAPCS 进行堆栈跟踪和展开代码.如果一个函数是static,编译器真的不需要遵守任何约定.

Some register calling conventions are dependent on the ABI (Application Binary Interface). The FP is required in the APCS standard and not in the newer AAPCS (2003). For the AAPCS (GCC 5.0+) the FP does not have to be used but certainly can be; debug info is annotated with stack and frame pointer use for stack tracing and unwinding code with the AAPCS. If a function is static, a compiler really doesn't have to adhere to any conventions.

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

Generally all ARM registers are general purpose. The lr (link register, also R14) and pc (program counter also R15) are special and enshrine in the instruction set. You are correct that the lr would point to A. The pc and lr are related. One is "where you are" and the other is "where you were". They are the code aspect of a function.

通常,我们有 sp(堆栈指针,R13)和 fp(帧指针,R11).这两者也有关系.这个微软布局在描述方面做得很好事物.stack 用于在您的函数中存储临时数据或locals.foo()bar() 中的任何变量都存储在这里、堆栈 或可用寄存器中.fp 跟踪从函数到函数的变量.它是该函数堆栈上的 frame 或图片窗口.ABI 定义了这个 frame 的布局.通常编译器在后台保存lr 和其他寄存器以及fp 的先前值.这构成了一个链表的堆栈帧,如果你愿意,你可以一直追溯到main().rootfp,它指向一个堆栈帧(如 struct),struct 中有一个变量是之前的 fp.您可以沿着列表前进,直到最后的 fp 通常是 NULL.

Typically, we have the sp (stack pointer, R13) and the fp (frame pointer, R11). These two are also related. This Microsoft layout does a good job describing things. The stack is used to store temporary data or locals in your function. Any variables in foo() and bar(), are stored here, on the stack or in available registers. The fp keeps track of the variables from function to function. It is a frame or picture window on the stack for that function. The ABI defines a layout of this frame. Typically the lr and other registers are saved here behind the scenes by the compiler as well as the previous value of fp. This makes a linked list of stack frames and if you want you can trace it all the way back to main(). The root is fp, which points to one stack frame (like a struct) with one variable in the struct being the previous fp. You can go along the list until the final fp which is normally NULL.

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

So the sp is where the stack is and the fp is where the stack was, a lot like the pc and lr. Each old lr (link register) is stored in the old fp (frame pointer). The sp and fp are a data aspect of functions.

你的观点B是活动的pcsp.A点其实就是fplr;除非您调用另一个函数,然后编译器可能会准备好设置 fp 以指向 B 中的数据.

Your point B is the active pc and sp. Point A is actually the fp and lr; unless you call yet another function and then the compiler might get ready to setup the fp to point to the data in B.

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

Following is some ARM assembler that might demonstrate how this all works. This will be different depending on how the compiler optimizes, but it should give an idea,

; 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(),那么编译器会做一个叶子优化并且不需要保存frame;只需要 bx lr.很可能这可能是您对网络示例感到困惑的原因.它并不总是相同的.

This is what foo() would look like. If you don't call bar(), then the compiler does a leaf optimization and doesn't need to save the frame; only the bx lr is needed. Most likely this maybe why you are confused by web examples. It is not always the same.

外卖应该是,

  1. pclr 是相关的code 寄存器.一个是你在哪里",另一个是你在哪里".
  2. spfp 是相关的本地数据寄存器.
    一个是本地数据在哪里",另一个是最后一个本地数据在哪里".
  3. 参数传递一起创建功能机械.
  4. 很难描述一般情况,因为我们希望编译器尽可能,因此他们会使用所有可能的技巧.
  1. pc and lr are related code registers. One is "Where you are", the other is "Where you were".
  2. sp and fp are related local data registers.
    One is "Where local data is", the other is "Where the last local data is".
  3. The work together along with parameter passing to create function machinery.
  4. It is hard to describe a general case because we want compilers to be as fast as possible, so they use every trick they can.

这些概念适用于所有 CPU 和编译语言,但细节可能有所不同.链接寄存器帧指针的使用是函数序言和结语,如果你理解了一切,你就会知道堆栈溢出如何在 ARM 上工作.

These concepts are generic to all CPUs and compiled languages, although the details can vary. The use of the link register, frame pointer are part of the function prologue and epilogue, and if you understood everything, you know how a stack overflow works on an ARM.

另见:ARM 调用约定.
MSDN ARM 堆栈文章
剑桥大学 APCS 概览
ARM 堆栈跟踪博客
Apple ABI 链接

基本的框架布局是,

  • fp[-0] 保存了 pc,我们在其中存储了这个帧.
  • fp[-1] 保存了lr,这个函数的返回地址.
  • fp[-2] 前一个sp,在这个函数栈之前.
  • fp[-3] 前一个fp,最后一个栈帧.
  • 许多可选的寄存器...
  • fp[-0] saved pc, where we stored this frame.
  • fp[-1] saved lr, the return address for this function.
  • fp[-2] previous sp, before this function eats stack.
  • fp[-3] previous fp, the last stack frame.
  • many optional registers...

ABI 可以使用其他值,但以上是大多数设置的典型值.上述索引适用于 32 位值,因为所有 ARM 寄存器都是 32 位.如果您以字节为中心,请乘以四.帧也至少对齐到四个字节.

An ABI may use other values, but the above are typical for most setups. The indexes above are for 32 bit values as all ARM registers are 32 bits. If you are byte-centric, multiply by four. The frame is also aligned to at least four bytes.

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

Addendum: This is not an error in the assembler; it is normal. An explanation is in the ARM generated prologs question.

这篇关于ARM:链接寄存器和帧指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆