概述
寄存器
-
寄存器
eax;ebx;ecx;edx
特别的 esp;ebp
-
esp,ebp这两个寄存器是用来存放地址,来维护函数开辟的栈帧
-
函数每一次的调用都会在栈区开辟新的空间
调用逻辑
整体调用的逻辑
这里以一个简单程序为例子
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%dn", c);
return 0;
}
找到调用 堆栈能够清晰的看出整个程序函数调用的逻辑
不难发现整个程序的运行
_tmainCRTStartup()调用了mainCRTStartup()
mainCRTStartup()调用了main()
main()调用Add
main()函数的栈帧
首先在文章的开头我说过esp,ebp这两个寄存器是存放地址的用来维护函数开辟的栈帧的,那调用main函数的之前其实_tmainCRTStartup()和mainCRTStartup()的空间其实应该已经开辟好了,并且esp,ebp也是维mainCRTStartup()创建的空间
push ebp
push为压栈pop为出栈,在栈区数据的创建都是从上往下的,同样的删除数据也是从最顶上的先删除。
每次push的时候我们的esp就会自动跳转到顶上
那事实上是不是这样呢?我们可以通过监视来查看
esp的地址0x0077FEBC
ebp的地址0x0077FF08
当我们进行push操作的时候发现,在栈顶上开辟了新的空间(4字节)并且将esp的地址0x0077FEBC(4字节)存放了进去,同时esp变量现在的0x0077FEB8(栈区是先先使用高地址再使用低地址)所以此时esp的位置应该往上
esp的地址0x0077FEB8
ebp的地址0x0077FF08
move ebp,esp
move ebp,esp意思为将esp赋值给ebp
esp=0x0077FEB8
ebp=0x0077FEB8
这里其实也看出了ebp从原来的0x0077FF08变成了和esp一样的0x0077FEB8
sub esp,0E4H
esp的值减去0e4h
esp=0x0077FDD4
ebp=0x0077FEB8
因为栈区的地址是先用高地址再用低地址,所以地址越小就越靠经顶部。
esp+0e4h=0x0077FEB8
也就是说esp,ebp现在维护着0e4h个空间,这个新开辟出的空间就是为main函数开辟的栈帧
push ebx /esi/ edi
这里三次push操作和最开始的一样
push ebx
esp=0x0077FDD0
ebp=0x0077FEB8
ebx=0x008E2000
esp由原来的0x0077FDD4变成0x0077FDD0,比原先多了4字节的空间且多的空间存放的是ebx的值
同理push esi
esp=0x0077FDCC
ebp=0x0077FEB8
esi=0x0067110E
同理push edi
esp=0x0077FDC8
ebp=0x0077FEB8
edi=0x0067110E
lea edi,[ebp-0E4h]
lea为加载有效地址其实就是ebp-0e4h的这个地址存放到寄存器edi中
特别的ebp-0e4h的地址其实就main函数栈顶地址
就是说现在的edi=0x0077FDD4 指向的其实是图中绿色esp的位置
mov ecx,39h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
从edi里地址的位置开始向下的ecx(39h)次这么多双子的数据都赋值eax的值
main函数中变量的初始化
mov dword ptr [ebp-8],0Ah
将0Ah就是10放到ebp-8的位置
mov dword ptr [ebp-14h],14h
mov dword ptr [ebp-20h],0
调用add函数栈帧变化
mov eax,dword ptr [ebp-14h]
将ebp-14h中的值赋值给eax
也就是说将b的值20赋值给了eax
push eax
同样进行压栈操作在栈顶将eax的值放入并且esp维护栈顶
mov ecx,dword ptr [ebp-8]
同样的道理这两部就是创建空间将a的值创建进去
call
call指令其实是将一下条指令的地址进行压栈
进入Add函数
首先还是push ebp这里不在解释直接截图了
mov ebp,esp
sub esp,0CCh
三次压栈不过多赘述
lea edi,[ebp-0CCh]
ebp-0CCh其实是粉红色esp的位置
将edi里的地址向下ecx次(33h)的双字赋值0CCCCCCCCh
mov dword ptr [ebp-8],0
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0Ch]
ebp+8其实就是变量a传的参数
ebp+12其实就是变量b传的参数
将a的值放入eax ,再将b的值加到eax也就是10+20=30
mov dword ptr [ebp-8],eax
eax存放的是30,将30放到ebp-8的位置就是z的位置
这里当出了函数,变量就应该销毁所有要把返回值再次放入寄存器中
pop edi
发现esp的值变大也就是说栈区在变小 edi这块空间消失了
pop esi和pop ebx同理
mov esp,ebp
这里其实就看出为add函数开辟的栈帧正在销毁
pop ebx
到这个时候esp ebp由回到了main函数了
call汇编指令结束
call汇编指令结束,此时esp指向得空间存放的是call下面一条指令的地址
add esp,8
mov dword ptr [ebp-20h],eax
剩下的栈帧的销毁其实都是一样的就不多赘述了
最后
以上就是傻傻皮卡丘为你收集整理的又菜又爱玩的函数栈帧的创建与销毁寄存器调用逻辑调用add函数栈帧变化的全部内容,希望文章能够帮你解决又菜又爱玩的函数栈帧的创建与销毁寄存器调用逻辑调用add函数栈帧变化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复