C语言 ARM:链接寄存器和帧指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15752188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ARM: link register and frame pointer
提问by user2233706
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.
我试图了解链接寄存器和帧指针在 ARM 中是如何工作的。我去过几个网站,我想确认一下我的理解。
Suppose I had the following code:
假设我有以下代码:
int foo(void)
{
// ..
bar();
// (A)
// ..
}
int bar(void)
{
// (B)
int b1;
// ..
// (C)
baz();
// (D)
}
int baz(void)
{
// (E)
int a;
int b;
// (F)
}
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?
我调用 foo()。链接寄存器是否包含点 (A) 处代码的地址而帧指针包含点 (B) 处代码的地址?在声明了所有本地变量之后,堆栈指针可以是 bar() 内的任何位置?
回答by artless noise
Some register calling conventions are dependent on the ABI(Application Binary Interface). The FPis required in the APCSstandard and not in the newer AAPCS(2003). For the AAPCS(GCC 5.0+) the FPdoes not haveto be used but certainly can be; debug info is annotatedwith 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.
一些寄存器调用约定依赖于ABI(应用程序二进制接口)。该FP要求在APCS标准,而不是在新的AAPCS(2003)。对于AAPCS(GCC 5.0以上)在FP不具有被使用,但可以肯定是,调试信息使用堆栈和帧指针进行注释,用于堆栈跟踪和使用AAPCS展开代码。如果一个函数是static,编译器真的不需要遵守任何约定。
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 lrwould point to A. The pcand lrare related. One is "where you are" and the other is "where you were". They are the codeaspect of a function.
通常所有 ARM 寄存器都是通用的。该lr(链接寄存器,也R14)和pc(程序计数器也R15)是特殊的,供奉在指令集。你是正确的,lr将指向A。该pc和lr是相关的。一个是“你在哪里”,另一个是“你在哪里”。它们是函数的代码方面。
Typically, we have the sp(stack pointer, R13) and the fp(frame pointer, R11). These two are also related. This
Microsoft layoutdoes a good job describing things. The stackis used to store temporary data or localsin your function. Any variables in foo()and bar(), are stored here, on the stackor in available registers. The fpkeeps track of the variables from function to function. It is a frameor picture window on the stack for that function. The ABIdefines a layout of this frame. Typically the lrand other registers are saved here behind the scenes by the compiler as well as the previous value of fp. This makes a linked listof stack frames and if you want you can trace it all the way back to main(). The rootis fp, which points to one stack frame (like a struct) with one variable in the structbeing the previous fp. You can go along the list until the final fpwhich is normally NULL.
通常,我们有sp(堆栈指针,R13)和fp(帧指针,R11)。这两者也有关系。这个
Microsoft 布局在描述事物方面做得很好。该堆栈用于存储临时数据或当地人在你的函数。在任何变量foo()和bar(),都存储在这里,在堆栈或可用寄存器。该fp跟踪变量从功能功能。它是该函数堆栈上的框架或图片窗口。在ABI定义此的布局框。通常情况下lr和其他寄存器被编译器在幕后保存在这里,以及之前的值fp。这将生成一个堆栈帧的链接列表,如果您愿意,您可以将其一直追溯到main(). 该根是fp,它指向一个堆栈帧(如struct)与一个变量在struct为前fp。您可以按照列表进行操作,直到fp通常为NULL.
So the spis where the stack is and the fpis where the stack was, a lot like the pcand lr. Each old lr(link register) is stored in the old fp(frame pointer). The spand fpare a dataaspect of functions.
所以 thesp是堆栈所在的位置,thefp是堆栈所在的位置,很像pcand lr。每个旧的lr(链接寄存器)都存储在旧的fp(帧指针)中。的sp和fp是一个数据的功能方面。
Your point Bis the active pcand sp. Point Ais actually the fpand lr; unless you call yet another function and then the compiler might get ready to setup the fpto point to the data in B.
您的B点是活动的pc和sp。A点实际上是fp和lr; 除非您调用另一个函数,然后编译器可能会准备好设置fp指向B 中的数据。
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,
以下是一些 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()foo()样子。如果您不调用bar()bar(),则编译器会执行leaf optimization叶优化,不需要保存frame框架; 只有bx lrbx lr是需要的。很可能这可能是您对网络示例感到困惑的原因。它并不总是相同的。The take-away should be,
外卖应该是,
pcandlrare related coderegisters. One is "Where you are", the other is "Where you were".spandfpare related local dataregisters.
One is "Where local data is", the other is "Where the last local data is".- The work together along with parameter passingto create functionmachinery.
- It is hard to describe a general case because we want compilers to be as fastas possible, so they use every trick they can.
pc和lr是相关的代码寄存器。一个是“你在哪里”,另一个是“你在哪里”。sp和fp是相关的本地数据寄存器。
一个是“本地数据在哪里”,另一个是“最后一个本地数据在哪里”。- 与参数传递一起工作以创建功能机器。
- 很难描述一般情况,因为我们希望编译器尽可能快,所以他们会使用他们可以使用的所有技巧。
These concepts are generic to all CPUs and compiled languages, although the details can vary. The use of the link register, frame pointerare part of the function prologueand epilogue, and if you understood everything, you know how a stack overflowworks on an ARM.
这些概念适用于所有 CPU 和编译语言,但细节可能有所不同。使用的链接寄存器,帧指针是部分功能的序幕和尾声,如果你什么都明白了,你知道如何堆栈溢出工作在ARM。
See also: ARM calling convention.
MSDN ARM stack article
University of Cambridge APCS overview
ARM stack trace blog
Apple ABI link
另请参阅:ARM 调用约定。
MSDN ARM 堆栈文章
剑桥大学 APCS 概述
ARM 堆栈跟踪博客
Apple ABI 链接
The basic frame layout is,
基本的框架布局是,
- 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 eatsstack. - fp[-3] previous
fp, the last stack frame. - many optional registers...
- fp[-0] 已保存
pc,我们在其中存储了此帧。 - fp[-1] 已保存
lr,此函数的返回地址。 - fp[-2] previous
sp,在这个函数吃栈之前。 - fp[-3] previous
fp,最后一个栈帧。 - 许多可选寄存器...
An ABImay 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.
一个ABI可以使用其它的值,但以上是典型的大多数设置。上述索引适用于 32 位值,因为所有 ARM 寄存器都是 32 位。如果您以字节为中心,请乘以四。该帧也至少对齐到四个字节。
Addendum:This is not an error in the assembler; it is normal. An explanation is in the ARM generated prologsquestion.
附录:这不是汇编程序中的错误;这是正常的。ARM 生成的序言问题中有一个解释。
回答by Charles Bergren
Disclaimer: I think this is roughly right; please correct as needed.
免责声明:我认为这是大致正确的;请根据需要更正。
As indicated elsewhere in this Q&A, be aware that the compiler may not be required to generate (ABI) code that uses frame pointers. Frames on the call stack can often require useless information to be put there.
如本问答中的其他地方所述,请注意编译器可能不需要生成使用帧指针的 (ABI) 代码。调用堆栈上的帧通常需要放置无用的信息。
If the compiler options call for 'no frames' (a pseudo option flag), then the compiler can generate smaller code that keeps call stack data smaller. The calling function is compiled to only store the needed calling info on the stack, and the called function is compiled to only pop the needed calling information from the stack.
如果编译器选项调用“无帧”(伪选项标志),则编译器可以生成更小的代码,使调用堆栈数据更小。调用函数被编译为只在栈上存储需要的调用信息,被调用函数被编译为只从栈中弹出需要的调用信息。
This saves execution time and stack space - but it makes tracing backwards in the calling code extremely hard (I gave up trying to...)
这节省了执行时间和堆栈空间 - 但它使得在调用代码中向后跟踪非常困难(我放弃了尝试......)
Info about the size and shape of the calling information on the stack is only known by the compiler and that info was thrown away after compile time.
有关堆栈上调用信息的大小和形状的信息只有编译器知道,并且该信息在编译后被丢弃。

