汇编指令push,mov,call,pop,leave,ret建立与释放栈的过程
相比高级语言中的栈结构,在汇编指令层面栈的任务更加复杂—生命周期必须与汇编指令行的切换正确对应。
https://blog.csdn.net/liu_if_else/article/details/72794199
图片说明:内存地址,汇编指令都为简写,用的十进制,栈空间1个格子大小是4*8=32位(对应32位操作系统),指令行长度都简化为1字节。为了突出建栈与撤栈的过程示意,函数都没有参数。栈空间也简化了,局部变量和参数都没有在其中。 实际执行顺序一列中对汇编指令行作用的进一步解释,左边为寄存器或栈空间地址,右边为其中的值。
寄存器%esp既是栈指针,总是指向最下方第一个空着的栈空间地址。当栈栈指针向上移动后,下方的栈空间就相当于被释放掉了。
%ebp寄存器在此机制中相当重要,它存储着当前函数在栈中所被分配的局部空间的起始地址。在释放栈的过程中,leave指令将会移动栈指针至%ebp存储的内存地址的位置,再反过来将此起始位置中存储的值,既上一层调用函数的起始地址,存到%ebp中,为下一次进一步释放做准备,并进一步向上移动栈指针,为ret命令做准备。在此机制中,当前函数在栈中所被分配的局部空间的起始地址的前一格内存(既是+32bit)中存储的是上一层调用函数指令call的下一行指令的内存位置,leave之后的ret指令将会控制指令行流水线跳到此处。
一些指令的简单说明:
push为入栈命令。
pop为出栈命令。
栈命令都跟伴随栈指针的移动。
mov在此过程中将一个寄存器中的值移到另一个寄存器。
call可以理解为goto。并将它的下一行指令地址存到栈中。
leave为mov+pop。为退出栈机制的重要一环。
ret也可以理解为goto。并移动栈指针。
汇编指令行对应的源码应为:
void main(){ //一些计算 f1(); //继续计算 } void f1(){ //一些计算 f2(); //继续计算 return; } void f2(){ //一些计算 return; }
感觉这东西有点烧脑,花了一下午时间终于整个捋顺了整个流程。
想理解好此过程,理解每个指令的作用,必须结合指令行地址,栈地址和寄存器一起来分析,否则很容易被绕晕。