传参
有的是用寄存器传参,有的用push传参
我在MSVC编译测出来的是PUSH传参(debug模式),具体过程如下
long func(long a, long b, long c, long d,long e, long f, long g, long h) {long sum;sum = (a + b + c + d + e + f + g + h);return sum;
}int main() {long sum;sum = func(1, 2, 3, 4, 5, 6, 7, 8);std::cout << sum << std::endl;return 0;
}
调用方:
汇编
push 是将操作数压到栈顶,然后移动栈顶(esp)
push前:
push一次后:
call前:
进入函数func
汇编
分析:
- 前3条是经典的移动堆栈
移动后:
注意这个ebp = 00ff68c, 理论上它应该是等于call前的esp即00ff694,为何相差8。
参数和栈底ebp之间的这8个字节分别是两个地址,先是函数的返回地址,即call指令的下一条地址,call指令是先将下一条指令地址压入栈然后跳转;其次是函数调用方的ebp,由跳转后第一条指令push ebp写入。
具体栈信息如图:
返回值
可见是通过eax寄存器返回的。
总结
可以回答以下几个问题:
- 如何传参,通过push指令将参数压入栈,然后call
- 参数放在哪:因为调用是先push再call, 进入函数后参数在栈下面,也就是不在[ebp, esp)这个范围内,通过ebp+x来取得参数。
- 局部变量在哪:进入函数后的3条指令开辟了新的栈空间即[ebp, esp), 局部变量在栈空间内,通过ebp-x来取值。
- 函数执行完恢复调用方的堆栈信息:进入函数后先将调用方的ebp压入栈,然后移动堆栈,调用方的ebp就存在当前栈底,即ebp指向的就是,退出函数时有个mov esp, ebp;pop ebp 动作就是将栈底的值给ebp寄存器。就恢复了调用方的堆栈
- 返回值,返回值简单时通过寄存器返回。
- 为什么参数的入栈信息总是从右到左:栈是向上增长的,先入栈的参数处于高位地址,压栈完后从低地址往高地址看,参数顺序就是从左向右了。
- ret指令: 跳转到当前栈顶的地址,然后esp-4
- call指令:将下一条指令地址压入栈,然后esp+4
- 调用函数栈信息如下:
//func的栈
-esp
...
-ebp ----- 调用方的ebp
-ebp-4 返回地址
- ebp-8 参数1
- ...
- 参数 n