基于OS、编译器,函数调用栈有不同的表现
栈帧:是函数的”活动记录”,从高地址处,不断向地址处”活动”
帧指针:EBP,指向该函数活动时的栈底/栈基(高地址处)
栈顶指针:ESP,指向该函数活动时的栈顶(低地址处)
找到参数一般通过 EBP+x,找到局部变量一般通过EBP-x;
主要就是函数嵌套另一个函数时,当外层函数准备调用另一个函数时,外层函数(调用者)要做准备工作:
ESP-参数字节长度:为了给予它参数(逆序),push EIP:记录返回地址(原现场的下一条指令的地址—一般就是add ESP 刚才因为参数减小的绝对值),push EBP: 保存现场(push当前函数的栈底)。剩下的就交给即将要执行的函数(被调用者)咯
另一个函数拿到执行权后,先创建新的ESP和EBP,并让新EBP指向这个新ESP
利用 x(%EBP) 就可以拿到对应的参数,剩下的就是新的局部变量分配:利用 EBP-N 字节,一次性先分配好空间,然后ESP继续根据其他指令指向栈顶。
在这个函数执行完毕准备ret时,先pop完局部变量,然后add ESP,N字节(回到了一开始存储外部EBP位置);pop EBP(就可以拿到外层EBP了啊);再ret;就回到了EIP,然后再 add ESP+那时参数的字节长度;这样,此时ESP,EBP就是外层调用内层函数前的状态了。ok
调用者一般还可以用:eax, ecx, edx等寄存器存储局部变量
被调用者一般还可以用:ebx,edi,esi等寄存器存储局部变量
ps:(%EBP) 意味着:把括号里的东西当作地址,取出对应地址里存储的内容