我有一个函数,它有一些局部变量,它调用一个接受两个参数的不同函数。我是汇编的新手,我不确定局部变量是否会为新函数搞砸我的堆栈。它们在技术上是不是就像1和2的值是我稍后推出的?我是否必须将本地人保存在其他地方并添加esp,4然后按下1和2的值,从堆栈中删除本地人?
my_function:
push ebp
mov ebp, esp
sub esp, 4
mov dword [ebp - 4], 10
push dword ptr 1
push dword ptr 2
call other_function
mov esp, ebp
pop ebp
答案 0 :(得分:0)
这是堆栈的整个想法,这些本地项被推入堆栈,堆栈指针被移动以为它们腾出空间,堆栈指针的下一个函数是它们开始将东西放在堆栈上的位置。等等。当一个函数清理它时,它会将堆栈指针放在它找到的位置,所以前面的人不会看到它改变,他们可以引用它们的内部事物,然后将它们放回去。
所以如果函数A添加3个东西,调用函数B添加1个东西,调用函数C添加4个东西,然后函数C清理掉四个东西并返回。 B清理1件事并返回。 A清理3件事并返回。
向堆栈添加内容可以是堆栈指针的简单数学修改(减去4),也可以是单独的推送指令(无论指令集使用什么)。同样在返回时,根据你想要对堆栈数据做什么,你可以简单地修改指针把它放回去,或者你可以弹出东西,比如你希望在函数期间保留先前状态的寄存器(以及它的所有兄弟姐妹) )。
例如,采取这个精心设计的功能
unsigned int more_fun (unsigned int x, unsigned int y);
unsigned int fun ( unsigned int x, unsigned int y )
{
return(x+(y<<more_fun(x+3,y+4)));
}
让编译器处理它。
00000000 <fun>:
0: e92d4070 push {r4, r5, r6, lr}
4: e1a04000 mov r4, r0
8: e1a05001 mov r5, r1
c: e2800003 add r0, r0, #3
10: e2811004 add r1, r1, #4
14: ebfffffe bl 0 <more_fun>
18: e0840015 add r0, r4, r5, lsl r0
1c: e8bd4070 pop {r4, r5, r6, lr}
20: e12fff1e bx lr
所以,我本身没有任何局部变量,但我已经做到这样,传入的参数想要在嵌套函数的调用中存活下来。我们还必须保存我们的返回地址(故意使用不同的指令集,无论如何这个都很容易阅读)。
所以在这个指令集中我们的返回地址是r14或lr(链接寄存器)我们的第一个参数是在r0中,我们的第二个参数在r1中,我们的返回在r0中返回。
所以他们可以在堆栈上保存这两个参数但是为了性能,他们在堆栈上保存了两个其他寄存器(这里的调用约定不会被下一个函数破坏)并将输入参数保存在那些注册该功能的持续时间。
返回地址也必须保留,所以我们知道如何从乐趣中返回,对more_fun的调用会将返回到有趣的地址,因此我们必须在调用之前保存lr。此约定坚持堆栈是64位对齐的,因此r6严格用于此目的以使堆栈对齐,否则不需要保留。
我们设置并调用该函数。我们在r0中得到返回值,我们准备通过做最后一点数学返回,然后把我们放在堆栈上的所有项目取下来。然后返回。
为了对齐堆栈,它可能已经推动了r4,r5和lr,然后做了sp = sp-4然后在返回时执行sp = sp + 4然后弹出三个寄存器但效率较低。
因此,如果在进入此函数时堆栈指针位于0x1000,则在推送后堆栈指针位于0x0FF0(4项,每个4字节,taht为16字节或0x10),因此当输入more_fun时,sp为0xFF0如果它是pushe stuff然后堆栈指针调整,当它返回时需要确保sp是0xFF0,这样我们就可以弹出我们的四个项目并获得正确的四个项目,然后我们返回sp设置为0x1000。 / p>
当我们返回时,这四个值仍然在0xFF0和0x1000之间的内存中,但是下一个被调用的函数没问题,或者至少某些函数会写入具有本地数据的函数。
你必须在其他地方保存当地人吗?没有堆栈是正确的位置,或者正如编译器在此处所做的那样,需要将适量的数据保存到堆栈中,以便可以保存其他参数。这是一个优化,未经优化的传入参数将被保存到堆栈中的位置&#34;已分配&#34;通过这个函数(通过推送或通过数学对堆栈指针)然后如果这些项需要稍后读取它们可以从堆栈中读取,如果它们需要在调用期间保存,它们已经保存在堆栈中