实例
由上可见{}在汇编指令中发挥的作用可不少,函数之间的随意调用和顺利收场都离不开它们。
红色水位线是:寄存器esp的值,用来标识:栈顶的内存地址
蓝色基准线是:寄存器ebp的值,用来标识:main函数的:栈帧基地址
从func()函数开始:
push将epb寄存器的值压入栈顶,栈顶水位线升高,至此main函数的栈帧保护工作完成,然后通过mov指令更新栈帧基准线,与栈顶水位线齐平。
然后通过sub指令把红色水位线提升8个字节,用来给两个临时变量分配:堆栈内存
之后就是对临时变量ab赋值,可见ab相对于蓝色基准线的偏移分别是4和8,刚好用完函数的栈帧。
函数执行完毕,该恢复main函数的栈帧了
mov指令将红色水位线降低到蓝色基准线的位置,然后通过pop指令把原先压入栈顶的ebp值返还给寄存器ebp,这样蓝色基准线就恢复到了最开始的位置,随着栈顶的下降,红色水位线也随之下降。至此红蓝两条线都恢复到了最开始的位置,main函数在栈帧恢复完成。
不准确的说,函数的栈帧就是红蓝两条线之间的内存块,它用来存放函数的临时变量,参数和返回地址。所谓的保护栈帧恢复栈帧,不过是在保存和恢复寄存器esp和ebp的值。
至于return address是用来做:函数返回的。(详见函数调用文章)
总结
1
cpu提供两个寄存器esp和ebp,用来标识当前函数对堆栈的使用情况。
随着函数的调用,函数的栈帧会逐层堆叠,但互不重合。
随着函数的逐层返回函数的栈帧会被就地放弃,但不会清理内存。
2
正括号{用来保护上层主调函数(main)的栈帧,并设置被调函数(func)的栈帧,反括号}用来放弃被调函数的栈帧,同时恢复主调函数的栈帧,这样被调函数执行完后,主调函数就能正常执行。
3
ebp寄存器作为当前函数的:栈帧基地址,配合一定的偏移就可以读写函数体里的:临时变量。
如果一个变量是通过ebp寄存器,间接访问的,那么它往往是临时变量,也叫栈变量。
4
不同编译器对栈帧的实现方法略有不同,但思路一致。