对于以下程序:

int bar(int c, int d)
{int e = c + d;return e;
}
int foo(int a, int b)
{return bar(a, b);
}
int main(void)
{foo(2, 3);return 0;
}

  在编译时加上-g选项,用objdump反汇编时可以把C代码和汇编代码穿插起来显示:

反汇编的结果很长以下是截取要分析的部分:

  整个程序的执行过程是main调用foo, foo调用bar, 用gdb跟踪程序的执行,直到bar函数中的int e = c + d;语句执行完毕准备返回时,这时在gdb中打印函数栈帧。

disassemble可以反汇编当前函数或者指定的函数,单独用disassemble是反汇编当前函数,如果disassemble后边跟函数名或地址则反汇编指定的函数。

s(step)命令可以一行代码一行代码的单步调试,而si命令可以一条指令一条指令的单步调试。bt 列出调用栈

info registers可以显示所有寄存器的当前值。在gdb中表示寄存器名时前面要加个$,例如p $esp命令查看esp寄存器的值(上图没有展示该命令),在上例中esp寄存器的值是0xbff1c3f4,所以x/20 $esp命令查看内存中从0xbff1c3f4地址开始的20个32位数。在执行程序时,操作系统为进程分配一块栈空间来存储函数栈帧,esp寄存器总是指向栈顶,,在x86平台上这个栈是从高地址向低地址增长的,每次调用一个函数都要分配一个栈帧来存储参数和局部变量,现在我们分析这些数据是怎么存储的,根据gdb的输出结果图示如下:

  途中每个小方格占4个字节,例如b:3这个方格的内存地址是0xbf822d20~0xbf822d23。我们从main函数的这里开始看起:

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

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

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

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

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

  现在看bar函数的指令:

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

  现在可以解释为什么在gdb中可以用bt命令和frame命令查看每个栈帧上的参数和局部变量了:如果我当前在bar函数中,我可以通过ebp找到bar函数的参数和局部变量,也可以找到foo函数的ebp保存在栈上的值,有个foo函数的ebp,又可以找到它的参数和局部变量,也可以找到main函数的ebp保存在栈上的值,因此各函数的栈帧通过保存在栈上的ebp的值串起来了。现在看bar函数的返回命令:

  bar函数有一个int型的返回值,这个返回值是通过eax寄存器传递的,所以首先把e的值读到eax寄存器中。然后执行leave指令,这个指令是函数开头的push %ebp和mov %esp, %ebp的逆操作:

    1. 把ebp的值赋给esp,现在esp的值是0xbf822d04。

    2. 现在esp所指向的栈顶保存着foo函数栈帧的ebp,把这个值恢复给ebp,同时esp增加4,现在esp的值是0xbf822d08。

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

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

    2. 修改了程序计数器eip,因此跳转到返回地址0x80483c2继续执行。

  地址0x80483c2处是foo函数的返回指令:

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

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

转载于:https://www.cnblogs.com/orlion/p/5775380.html

[汇编与C语言关系]1.函数调用相关推荐

  1. [汇编与C语言关系]2. main函数与启动例程

    为什么汇编程序的入口是_start,而C程序的入口是main函数呢?以下就来解释这个问题 在<x86汇编程序基础(AT&T语法)>一文中我们汇编和链接的步骤是: $ as hell ...

  2. C语言和汇编语言函数调用

    C语言和汇编语言函数调用关系 1.汇编语言函数调用 X86结构中,cs寄存器和rip寄存器共同控制着CPU要执行的下一条指令(当前在不同的模式中控制方式不同,如:实地址2模式和保护模式,长模式等),一 ...

  3. C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质...

    C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质 事情的经过是这种,博主在用C写一个简单的业务时使用递归,因为粗心而忘了写return.结果发现返回的结 ...

  4. vhdl语言入门_从当初汇编、C语言入手,到如今FPGA开发已然十年,总结出“三多”!...

    从大学时代第一次接触FPGA至今已有10多年的时间,至今记得当初第一次在EDA实验平台上完成数字秒表.抢答器.密码锁等实验时那个兴奋劲.当时由于没有接触到HDL硬件描述语言,设计都是在MAX+plus ...

  5. 51汇编与c语言混合编程,C51与汇编混合编程详解

    C51与汇编混合编程详解 0750long | 2009-07-09 12:45:42    阅读:1257 发布文章 C51与汇编混合编程详解 C51和汇编混合编程(1)-C语言中嵌入汇编 1.在 ...

  6. c语言中调用函数fn,C语言常见的函数调用

    C语言常见的函数调用 isatty,函数名,主要功能是检查设备类型,判断文件描述词是否为终端机. 函数名: isatty 用 法: int isatty(int desc); 返回值:如果参数desc ...

  7. 「30天制作操作系统系列」1~4天从汇编到C语言

    目录 背景 30制作操作系统?这个听起来像天方夜谭,但是真有一本书就叫做<30天自制操作系统>,当然,自制操作系统的目的不是要去对标Windows.Linux.MacOS等流行的现代化操作 ...

  8. c语言汇编6,C语言是如何转换成汇编语言的6个步骤带你解析

    大家都知道计算机只能处理和识别二进制指令,而我们利用各种高级编程语言所编写的程序,要经过一些列的处理步骤,最终转变为汇编指令,再最后转变为机器指令. C语言 以上这些转变是如何发生的就属于大名鼎鼎的& ...

  9. C语言和C++语言关系

    C语言和C++语言关系 C语言是在实践的过程中逐步完善起来的 C语言的目标是高效 C语言和C++并不是对立的竞争关系 C语言是在实践的过程中逐步完善起来的 没有深思熟虑的设计过程 使用时存在很多&qu ...

最新文章

  1. python爬虫scrapy框架教程_Python爬虫教程-30-Scrapy 爬虫框架介绍
  2. OCR文字识别软件的快速任务功能如何用
  3. 用startSmoothScroll实现RecyclerView滚动到指定位置并置顶,含有动画。
  4. 用户数据报协议是啥?看完这文就懂了!| 技术头条
  5. 下载python教程-零基础Python教程全集下载.pdf
  6. 汇编学习--7.10--循环
  7. android经典动态壁纸,Android动态壁纸解析
  8. 8脚 tja1050t_TJA1050芯片手册.pdf
  9. emoji语言抽象话大全_当抽象话也成为一种暗语
  10. app瘦身值图片压缩
  11. loj 1224 - DNA Prefix
  12. 出现Illegal invocation的报错
  13. keyshot渲染玻璃打光_KeyShot渲染,打光这么打,效果倍儿棒!
  14. 火龙果的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  15. 与华为SIP硬终端(TE40)对接注意事项
  16. 条形码识别(1)——基础知识
  17. 淘宝线上线下“出淘”欲打造零售业航母
  18. 纯HTML编写仿淘宝粉丝福利购页面-优惠券、商品列表、图片悬浮等布局
  19. Python 棋子移动游戏
  20. PV + MPPT +均匀辐射

热门文章

  1. linux 版本号 加号,如何去除Linux Kernel版本号后面的加号?
  2. 十二生肖配对表查询_天蝎座:分手后最容易复合的星座配对,一生分不开,最终重新走到一起...
  3. Web 端 js 导出csv文件(使用a标签)
  4. php与tp5,PHP开发(33)-ThinkPHP5.0(5)命名空间与TP5-PhpStorm
  5. Python中的pip包管理工具被删除重新进行安装
  6. MySql字符串与时间日期之间的的转换
  7. linux 关闭rsync服务器,linux下配置rsync服务器和实时同步
  8. java基础的知识_Java基础知识点(一)
  9. keil5调试如何选择晶振_答题攻略:晶振电路问题解答及国庆中秋放假通知
  10. Vue Element-UI使用icon图标(第三方)--在线版