在尝试分析通过msp430-gcc生成的简单汇编文件时,我偶然发现了一些我不理解处理帧指针和MSP430堆栈指针的指令。
C计划:
#include "msp430g2553.h"
int main()
{
int i;
for(i = 0; i < 3; i++);
}
装配减指令:
main:
mov r1, r4 ;Stores address of stack pointer in r4(frame pointer)
add #2, r4 ; ?
sub #2, r1 ; subtract 2 to allocate int i
mov #0, -4(r4) ; assign 0 to i
jmp .L2 ; start loop
.L3:
add #1, -4(r4) ; Adds one to int i
.L2:
cmp #3, -4(r4) ; Compare to #3
jl .L3 ; jump to .L3 if true
add #2, r1 ; deallocate int i
.Lfe1:
.size main,.Lfe1-main
我尝试评论代码以跟踪程序的执行情况,但我不理解行add #2, r4
。这里究竟发生了什么,为什么int i
引用了-4(r4)
?
答案 0 :(得分:2)
通常,您在函数中要做的第一件事是:
push r4
将当前帧指针保存在堆栈中,这样您就可以为要调用的函数设置新的帧指针,然后再恢复旧的指针。 push
将自动将堆栈指针递减2,所以当你执行:
mov r1, r4
推送到r4
的地址将高于您刚刚在堆栈上推送的值(此处的“上方”是堆栈向下扩展的意义 - 它实际上低于数字内存地址)。您希望帧指针实际上将指向下面您刚刚推入堆栈的值,因此您将其递增2以实现此目的:
add #2, r4
由于main()
是第一个执行的函数,因此您没有要保存的现有帧指针,因此您在此处看到的是mov
和add
没有push
。
当你进行实际的函数调用时会更有意义,你会看到整个事情:
push r4
mov r1, r4
add #2, r4
完成此操作后,-2(r4)
将引用刚刚推入堆栈的帧指针的前一个值,并且由于您没有将两个值添加到堆栈指针的值,{ {1}}也将与之相等。
当你为局部变量-2(r4)
分配16位时,你必须从堆栈指针中减去i
为它腾出空间,2
的地址将会因此是i
。
例如,假设堆栈指针包含-4(r4)
且帧指针包含0x200
,然后您想调用一个函数。你从这样的堆栈开始:
0x202
从函数返回后,您将要恢复帧指针的当前值,因此您要做的第一件事就是将其推入堆栈以保存它。在 r4 --> 0x202 ---------------------
<empty>
r1 --> 0x200 ---------------------
之后,值push r4
因此被推送到内存位置0x202
(即0x200
所指向的堆栈顶部),并且堆栈指针递减{ {1}}为它腾出空间,所以你得到:
r1
将帧指针的先前值压入堆栈后,您现在想要将帧指针的当前值设置为当前堆栈帧的基数,因此您可以通过将新堆栈指针移动到{来启动它{1}},你得到:
2
帧指针的旧值是当前堆栈帧中的第一个值,因此您希望 r4 --> 0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
在此之前指向,而不是在它之后指向。为此,您可以将r4
添加到 0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 == r4 --> 0x1FE ---------------------
,然后获得:
r4
您现在所处的位置是2
指向堆栈框架的底部,r4
指向它的顶部,这是您想要的位置。此时当前堆栈帧中实际上唯一的事情是在函数开始时将其推到它上面的帧指针的前一个值。
然后你将堆栈指针递减 0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
以为你的新本地变量腾出空间,最后得到:
r4
您可以看到r1
存储在2
,即 0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
0x1FE ---------------------
<uninitialized i>
r1 --> 0x1FC ---------------------
。您再次处于i
指向堆栈框架底部的位置,0x1FC
指向其顶部,但现在您在当前堆栈框架中有两个16位值,所以两个指针分开4个字节。
当您准备好在函数结束时返回时,您将-4(r4)
“释放”本地变量r4
的内存。这会给你:
r1
然后你将add #2, r1
,它将从堆栈中弹出最后一个值(现在是i
,帧指针的原始值),将其放入 0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
,并将堆栈指针递增pop r4
以反映该值已从堆栈中删除,这将使您离开:
0x202
这是你开始的地方,你在函数调用后已经完全清理了堆栈。
注意这稍微简化了,因为当你进行函数调用时,程序计数器也会被推入堆栈,然后在你返回时再次弹出和恢复,而上面的例子没有显示,但那里发生了同样的事情。