ARM:链接寄存器和帧指针 [英] ARM: link register and frame pointer
问题描述
我试图了解链接寄存器和帧指针在 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(应用程序二进制接口).FP
在 APCS 标准中是必需的,而在较新的 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 是正确的.pc
和 lr
是相关的.一个是你在哪里",另一个是你在哪里".它们是函数的代码方面.
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()
.root 是 fp
,它指向一个堆栈帧(如 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
是堆栈所在的位置,很像 pc
和 lr
.每个旧的lr
(链接寄存器)都存储在旧的fp
(帧指针)中.sp
和 fp
是函数的数据方面.
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是活动的pc
和sp
.A点其实就是fp
和lr
;除非您调用另一个函数,然后编译器可能会准备好设置 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.
外卖应该是,
pc
和lr
是相关的code 寄存器.一个是你在哪里",另一个是你在哪里".sp
和fp
是相关的本地数据寄存器.
一个是本地数据在哪里",另一个是最后一个本地数据在哪里".- 与参数传递一起创建功能机械.
- 很难描述一般情况,因为我们希望编译器尽可能快,因此他们会使用所有可能的技巧.
pc
andlr
are related code registers. One is "Where you are", the other is "Where you were".sp
andfp
are related local data registers.
One is "Where local data is", the other is "Where the last local data is".- The work together along with parameter passing to create function machinery.
- 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屋!