函数调用全过程分析

一.    函数调用过程:

1.         32位传参是通过栈来完成的 在调用之前 将参数放在栈顶 然后再执行call指令.

2.         相对于32位传参, 对于x86_64位寄存器, 字母”r”用作名称前缀,即指示各寄存器大小为64位。形数是通过寄存器来传递 一般使用寄存器 esi edi,  此时,编译器无须再为形参分配栈空间。

3.         返回值都是用eax来传递。

4.         示例一个有意思的小程序:

程序代码请查阅funPointer2.c

基本思路: 在main函数中调用plus函数,正常情况下,plus函数执行完成之后就会通过return返回到主函数中去(这里的return实质是leav ret两步来完成,其中leav做了如下内容:A. mov ebp,esp B. pop ebp(当然 这里esp也就自加了1))。这里我们通过对栈做一些小的修改,使我们的plus不再返回到主函数中去,而去执行test函数。如果只这样做的话,还不够,再通过修改test中栈的内容,使我们程序在test当中返回到main函数中去。

5.   通过以上的例子,ebp 和 esp的配合对栈的操作使我们函数的调用得以无误执行。当然这里的修改已经使原来的栈减小,因为相对正常的调用,少了一次压栈过程或者说多了一次弹栈的过程。

二. gdb的使用观察:

1.  gcc  -g  funPointer2.c -0 funPointer2  ;加入调试信息

2.  gdbfunPointer2      ;正式开始调试



这里停到了下一个函数调用前。

 

main函数中程序执行到这,实际已经完成了对所有变量的栈空间的分配,在一个函数体内,编译器对栈空间的分配是一次性完成的。我们记录一下在main函数当中栈顶esp的值:

info registers eip esp

得到 esp : 0xbffff1e0

当然,我这里顺便把eip值看了一下,也就知道了在main函数中执行调用plus之后的下一条指令的地址,0x0804843a

 



发现了没有, 我们定义的两个全局变量的地址分别为:

p: 0x804a020

pOfaddr: 0x804a024



保存了下一条指令地址入栈,程序由main函数转移到plus函数中时,首先将ebp入栈。此时我们的esp已经减小两次4字节,把上次mian中的栈顶+4字节的地址即保存了返回main地址。这里打印看了一下,是0x0804843a



将返回main的地址保存在了全局变量p中,以便我们修改test中的栈,使test执行完返回到main中。



leav ret一执行 我们修改的地址就被弹出,从而执行我们的修改的地址,程序转到test中去。



在这里做的操作是:首先是将esp打印了一下,可以证实 栈的内容已经恢复到了main调用之前的情况。

test函数中在此基础上,对栈做了如下操作:

首先明确,由于我们不是通过函数调用call来转到的test函数中去,因此地址并未压入到栈中去。

其次,和其他函数调用相同,首先将ebp压栈,然后分配栈空间。当然,因为这是最后一个被调用的函数,程序无需再返回,栈顶完全可以不再减小(分配栈空间一般是指栈顶减小,作为最后一个返回函数情况特殊在这一点)。

 

 


这里我们已经将保存在全局变量p中main的返回地址赋给了test函数返回时要弹出的栈。因此当test执行完,返回时,栈中地址被弹出,程序转回到main程序中,接着执行。这里修改的栈地址是:0xbffff1e0 = 0x0804843a;




对比一下与正常情况下栈的不同,在main中的栈空间小了一个,即栈顶地址高了4。但由于栈空间减小的是传参的参数,因此不会对main函数造成任何的影响(当然这里是在32位下!)。


源代码参考在下一篇文章中。



说明:以上内容均为原创,转载请标明来源或联系作者。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值