问题一:main函数调用sum,sum执行完以后,怎么知道回到哪个函数中?
问题二:sum函数执行完,回到main以后,怎么知道从哪一行指令继续运行的?
#includeusing namespace std; int sum(int a, int b) { int temp = 0; temp = a + b; return temp; } int main() { int a = 10; int b = 20; int ret = sum(a, b); cout << "ret:" << ret << endl; return 0; }
函数运行的时候,操作系统会在栈帧上给其分配空间:
- ebp是栈底指针,esp是栈顶指针;
- 上面是低 地址,下面是高地址;
- 栈底是高地址,栈顶是低地址
然后main函数开始执行第一条指令:
然后执行第二条指令:
然后是ret的定义,同理:
- ret的值现在还不知道,先放在这里:
调用sum函数了!
一个函数的调用:调用方首先压参数(压栈),从右向左压!!!
将b压到栈顶,再压入a;(都是sum函数形参变量ab的内存)
函数调用过程中,形参内存的开辟是在调用方开辟号的
将值取出,通过push进行压栈。
注意:esp永远指向栈顶!
函数调用参数压完了,接下来是调用call指令:
call sum做的第1件事情是:把当前行指令的下一行指令的地址压栈(call本身就有这样一个功能)
第2件事情是: 进入到sum函数里面。
在函数的大括号的左括号和第一行代码之间是有指令生成的(sum函数栈帧的开辟和初始化)
- 1、push ebp,把ebp的地址压栈,push后,esp栈顶指针是会更新的哦!
2、mov ebp,esp:把esp(栈顶)赋值给ebp(栈底)
现在程序的执行流程来到了sum函数,ebp应该指向sum的栈底。
3.sub esp,4ch:把esp栈底指针-4ch,就是向上开辟栈帧空间
然后执行循环rep stos(相当于一个for循环的命令),用0xCCCC填充初始化栈帧内存(Windows的vs编译器做的操作)
在栈上定义变量,未初始化,打印出来就是0CCCCCCC,即-858993460
然后我们执行下面代码:
上面的左括号的汇编指令结束了,到代码了:
然后执行a+b,然后把a+b的值赋值给temp:
最后执行return temp;把temp的值保存在eax寄存器中。(temp是出不去的)
然后接下去就是执行函数右括号}对应的指令了:
1、mov esp,ebp:把ebp赋值给esp,栈顶指针esp掉到栈底了,即栈帧回退,归还给操作系统。
- 回退栈帧。栈空间交还给系统了,但是并没有对栈上的空间进行清理,但是其他线程进程可能对其覆盖初始化。
举例:
- func函数运行结束,栈帧回退,这块函数的栈帧空间已经交还给系统了,再返回其局部变量的地址,不安全。
- 但是在打印*p时,是可以打印出来的,因为栈帧回退,并没有对数据进行清理,虽然是非法访问内存,但是栈上的数据还在;不安全!!!(其他函数可能对这个空间操作,现在这个空间你本来就是非法访问)
继续:
接下来执行:
2、pop ebp
出栈,现在栈顶放的值是0x0018ff40,把值赋值给ebp,这样ebp回到main函数的栈底了。
3、执行ret指令
ret有2步:
- 1.出栈,esp自动更新,下移。
- 2.把出栈的内容放入CPU的PC寄存器里面(存放CPU执行下一条指令的地址)
CPU执行完ret指令,然后就根据PC寄存器的地址,直接跳到下一行指令了。
执行add esp,8:把esp栈顶指针+8,就是向下移动8个字节。(将ab两个形参变量的内存交还给系统)
这样esp和ebp都归位了;
这样函数就调用完了,刚才sum函数return的时候,将值放在eax寄存器中;
关于将亡值的返回,我们要注意:



