关键词搜索

源码搜索 ×
×

C语言的本质(31)——C语言与汇编之函数调用的本质

发布2014-07-24浏览1994次

详情内容

我们一段代码来研究函数调用的过程。首先我们写一段简单的小程序:

  1. int sum(int c, int d)
  2. {
  3. inte = c + d;
  4. returne;
  5. }
  6. int func(int a, int b)
  7. {
  8. returnsum(a, b);
  9. }
  10. int main(void)
  11. {
  12. func(2,3);
  13. return0;
  14. }

通过gcc编译。在编译命令中要加上-g选项,这样在使用objdump反汇编时可以把C代码和汇编代码穿插起来显示,这样C代码和汇编代码的对应关系看得更清楚。

要查看编译后的汇编代码,其实还有一种办法是gcc -S main.c,这样只生成汇编代码main.s,而不生成二进制的目标文件。

反汇编的结果比较长,以下只列出主要的部分。

 

 

整个程序的执行过程是main调用func,func调用sum。我们从main函数的这里开始看起:

 

要调用函数func先要把参数准备好,第二个参数保存在esp+4所指向的内存位置,第一个参数保存在esp所指向的内存位置,可见参数是从右向左依次压栈的。然后执行call指令,这个指令有两个作用:

 

1、func函数调用完之后要返回call的下一条指令继续执行,所以把call的下一条指令的地址0x80483e9压栈,同时把esp的值减4,esp的值现在是0xbf822d18。

2、修改程序计数器eip,跳转到func函数的开头执行。

 

现在看func函数的汇编代码:

 

 

首先将ebp寄存器的值压栈,同时把esp的值再减4,esp的值现在是0xbf822d14,然后把这个值传送给ebp寄存器。换句话说就是把原来ebp的值保存在栈上,然后又给ebp赋了新值。在每个函数的栈帧中,ebp指向栈底,而esp指向栈顶,在函数执行过程中esp随着压栈和出栈操作随时变化,而ebp是不动的,函数的参数和局部变量都是通过ebp的值加上一个偏移量来访问的,例如func函数的参数a和b分别通过ebp+8和ebp+12来访问,所以下面的指令把参数a和b再次压栈,为调用sum函数做准备,然后把返回地址压栈,调用sum函数:

 

现在看sum函数的指令:

 

这次又把func函数的ebp压栈保存,然后给ebp赋了新值,指向sum函数栈帧的栈底,通过ebp+8和ebp+12分别可以访问参数c和d。sum函数还有一个局部变量e,可以通过ebp-4来访问。所以后面几条指令的意思是把参数c和d取出来存在寄存器中做加法,add指令的计算结果保存在eax寄存器中,再把eax寄存器存回局部变量e的内存单元。

如果我当前在bar函数中,我可以通过ebp找到bar函数的参数和局部变量,也可以找到func函数的ebp保存在栈上的值,有了func函数的ebp,又可以找到它的参数和局部变量,也可以找到main函数的ebp保存在栈上的值,因此各函数的栈帧通过保存在栈上的ebp的值串起来了。

 

现在看sum函数的返回指令:


sum函数有一个int型的返回值,这个返回值是通过eax寄存器传递的,所以首先把e的值读到eax寄存器中。然后执行leave指令,这个指令是函数开头的push %ebp和mov %esp,%ebp的逆操作:把ebp的值赋给esp。现在esp所指向的栈顶保存着func函数栈帧的ebp,把这个值恢复给ebp,同时esp增加4。

最后是ret指令,它是call指令的逆操作:

现在esp所指向的栈顶保存着返回地址,把这个值恢复给eip,同时esp增加4。

 

重复同样的过程,就又返回到了main函数。注意函数调用和返回过程中的这些规则:

参数压栈传递,并且是从右向左依次压栈。

ebp总是指向栈帧的栈底。

返回值通过eax寄存器传递。

 

这些规则并不是体系结构所强加的,ebp寄存器并不是必须这么用,函数的参数和返回值也不是必须这么传,只是操作系统和编译器选择了以这样的方式实现C代码中的函数调用,这称为Calling Convention,除了Calling Convention之外,操作系统还需要规定许多C代码和二进制指令之间的接口规范,统称为ABI(Application Binary Interface)。

 

相关技术文章

最新源码

下载排行榜

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载