0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

透彻的理解机器代码层面的函数调用

Linux阅码场 来源:未知 作者:胡薇 2018-05-25 14:40 次阅读

我是CPU阿甘, 上次我给大家承诺过,要讲一讲函数调用的秘密, 这个确实有点复杂, 想透彻的理解机器代码层面的函数调用不容易。

我也是从无数的指令中悟出这个函数调用的秘密的, 所以慢慢来,不要急。 放松心情, 慢慢的品味,你可能需要多看几遍才能明白。

但是你一旦理解了,绝对物超所值,因为你会了解到汇编寄存器,指针,以及他们在一起到底是怎么工作的。

首先, 一个程序一条一条的指令都的老老实实的放在内存的一个地方,这个地方是Linux老大分配的, 我干涉不了, 但是这些指令都是我打电话给硬盘, 让他给运输到内存的。然后Linux老大就会告诉我程序的入口点, 其实就是第一条指令的存放地址, 我就打电话问内存要这个指令, 取到指令以后就开始执行。这些指令当中无非有这么几类:1. 把数据从内存加载我的寄存器里什么? 你不知道啥是寄存器? 寄存器就是我内部的一个临时的数据存储空间了2. 对寄存器的数据进行运算, 例如把两个寄存器的数加起来3. 把我寄存器的数据再写到内存里但是我一旦遇到像这样的指令。"把寄存器ebp的值压到栈里去“我就知道好戏要上场了, 函数调用就会开始。我们这些x86体系的机器有个特点,就是每个函数调用都会创建一个所谓的“帧”哈哈, 不要被这些术语吓坏, 其实帧也就是我哥们内存中的一段连续的空间而已。像这样:

现在这个指令来了:"把寄存器ebp的值压到栈里去“"把esp的值赋给ebp"

"把esp 的值减去24”

“把10放到ebp 减去4的地址” (其实就是796嘛)“把20放到ebp减去8的地址” (其实就是792嘛)

" 把地址796作为数据放到 esp指向的地址“ (其实就是776嘛)" 把地址792作为数据放到 esp+4指向的地址" (其实就是780嘛)

这其实就相当于把 x 的指针 &x和 y 的指针 &y ,放到了特定的地方, 准备着要做什么事情 , 可能要调用函数了。

所以,所谓的指针就是地址而已。

我猜程序员写的代码应该是这样:int x = 10;int y = 20;int sum= add(&x, &y);接下来的指令是这样:“调用函数 add”我看到这样的函数就需要特别小心, 因为我必须要找到 add函数返回以后的那条指令的地址, 把它也压到栈里去。int x = 10;int y = 20;int sum = add(&x, &y);printf("the sum is %d\n",sum);假设这条指令的地址是100

注意啊, 把函数调用结束的以后的返回地址100压入栈以后, esp 也发生变化了, 指向了772的位置我会找到函数Add 的指令,继续执行"把寄存器ebp的值压到栈里去“"把esp的值赋给ebp""把寄存器ebx的值压入栈”你看每个函数的开始指令都是这样, 我猜这应该是一种约定吧这里额外把ebx这个寄存器压入栈, 是因为ebx可能被上个函数使用, 但是在add函数中也会用 , 为了不破坏之前的值, 只有先委屈一下暂时放到内存里吧。

“把ebp 加8的数据取出来放到 edx 寄存器” (ebp+8 不就是地址776嘛, 其中存放的是&x的地址, 这就是取参数了)“把ebp 加12的数据取出来放到 ecx 寄存器” (ebp+12 不就是地址780嘛, 其中存放的是&y的地址)注意啊, 现在edx的值是796, ecx的值是792 , 但他们仍然不是真正的数据, 而是指针(地址)!“把edx 指向的内存地址(796)的数据取出来,放到ebx 寄存器”“把ecx 指向的内存地址(792)的数据取出来,放到eax寄存器”此时此刻, 终于取到了真正的值, ebx = 10, eax = 20你晕了没有? 如果你到此已经晕了, 建议你再读一遍。 我想源代码应该非常的简单,就是这样:int add(int *xp , int *yp){ int x = *xp; int y = *yp; ....}“把ebx 和 eax 的值加起来,放到 eax寄存器中”这个指令我最擅长做了。接下来的指令也很关键, add 函数已经调用完成, 准备返回了“把esp 指向的数据弹出的ebx寄存器”“把esp 指向的数据弹出到ebp寄存器”

"返回"我就会取出那个返回地址, 也就是 100, 去这里找指令接着执行其实就是这条语句:printf("the sum is %d\n",sum);问你一个问题, sum的值在那里保存着呢?对, 是在eax寄存器里 !搞定了,看着很复杂, 其实看透了也挺简单吧。 函数调用,关键就是(1)把参数和返回地址准备好,(2)然后大家都遵循约定, 每次新函数都要建立新的函数帧: "把寄存器ebp的值压到栈里去“ "把esp的值赋给ebp"(3) 函数调用完了, 重置 ebp 和esp ,让他们重新指向调用着的栈帧。好了,今天就到此为止 , 把我也累坏了, 主人又要关机了,留一个问题吧:C语言编译,链接以后直接就是机器码, 那函数调用的操作都是上面讲的。但是对于Python, Ruby 这样的解释型语言, 或者对于java 这样的有虚拟机的语言, 他们的函数调用是什么样的? 和上面讲的有什么关系?

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5308

    浏览量

    119980
  • 汇编
    +关注

    关注

    2

    文章

    214

    浏览量

    25898
  • 函数调用
    +关注

    关注

    0

    文章

    19

    浏览量

    2584

原文标题:CPU阿甘:函数调用的秘密

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C函数调用机制与栈帧原理详解

    当一个C函数调用时,函数的参数如何传递、堆栈指针如何变化、栈帧是如何被建立以及如何被消除的,一直缺乏系统性的理解,因此决定花时间学习下函数
    发表于 06-08 10:49 1211次阅读
    C<b class='flag-5'>函数</b><b class='flag-5'>调用</b>机制与栈帧原理详解

    如何查看及更改函数/函数块的调用环境

    模块化设计的思想是把一些相似的功能(比如电机控制、阀控制)设计成函数函数块,这样就可以反复调用。其优点是:使程序架构更加清晰,避免重复编写相似功能的代码。不过可能会产生一个疑惑:既然
    的头像 发表于 11-17 09:08 882次阅读
    如何查看及更改<b class='flag-5'>函数</b>/<b class='flag-5'>函数</b>块的<b class='flag-5'>调用</b>环境

    时钟软件层面和硬件层面的问题解释

    前言说实话,我刚开始学的时候也没咋的学明白,都是拿着别人的代码抄一抄。那时我连软件层面和硬件层面有时候都会搞混,所以我还是建议初学者多做笔记,多看看手册。没事也可以翻翻我的博客,如果在169芯片遇到
    发表于 11-29 07:08

    硬件层面的堆和栈基本介绍

    堆和栈!基本介绍在嵌入式和单片机开发领域中,堆和栈是非常重要的基础知识,但对于许多开发者来说,对这方面的概念还是非常的模糊,甚至基本没了解过。栈:基本上可以理解为,函数的局部变量都是存放...
    发表于 03-01 07:40

    CodeViz--一款分析C/C++源代码函数调用关系的调用

    程序开发中,有时候需要阅读别人的代码,这时理解代码的组织结构就显得非常重要。CodeViz是一款分析C/C++函数调用关系的
    发表于 04-04 20:50 85次下载
    CodeViz--一款分析C/C++源<b class='flag-5'>代码</b>中<b class='flag-5'>函数</b><b class='flag-5'>调用</b>关系的<b class='flag-5'>调用</b>

    如何在函数库中调用指令?

    函数是一段可复用的代码。我们通常把重复的代码放进函数中并且在不同的地方去调用它。库是函数的集合。
    的头像 发表于 08-31 15:51 3781次阅读

    关于DSP中fft函数调用方法

    以下主要是通过代码调用ftf函数
    发表于 01-01 08:35 8210次阅读

    C代码与javaScript函数的相互调用问题应该如何解决

    本文档的主要内容详细介绍的是C代码与javaScript函数的相互调用问题应该如何解决。
    发表于 03-05 11:47 17次下载

    系统调用与普通的函数调用之间的区别

    函数之间是可以相互调用的,这很简单很happy有没有。 要知道是代码、是函数就可以相互调用,不管你用什么语言写的。
    的头像 发表于 02-15 11:47 3391次阅读
    系统<b class='flag-5'>调用</b>与普通的<b class='flag-5'>函数</b><b class='flag-5'>调用</b>之间的区别

    嵌入式软件架构设计之函数调用

    函数调用很好理解,即使刚学没多久的朋友也知道函数调用是怎么实现的,即调用一个已经封装好的
    的头像 发表于 02-15 14:48 1060次阅读
    嵌入式软件架构设计之<b class='flag-5'>函数</b><b class='flag-5'>调用</b>

    函数调用时底层会发生什么

    如果你懂得用箱子打包东西,你就能明白函数调用是怎么一回事。 原来,在程序运行时每个被调用函数都有自己的一个箱子,假设这段代码是这样写
    的头像 发表于 02-17 14:47 629次阅读
    <b class='flag-5'>函数</b><b class='flag-5'>调用</b>时底层会发生什么

    什么是函数调用

    函数调用,就是使用我们已经定义好的函数,或者C语言自带的库函数
    的头像 发表于 04-04 17:21 5643次阅读

    SCL中调用函数的示例

    在此,可插入函数 (FC) 调用函数块 (FB) 调用函数块可作为单实例、多重实例或参数实例进行调用
    的头像 发表于 06-06 10:18 2080次阅读

    python函数函数之间的调用

    函数函数之间的调用 3.1 第一种情况 程序代码如下: def x ( f ): def y (): print ( 1 ) return y def f (): print ( 2
    的头像 发表于 10-04 17:17 564次阅读

    linux用gdb调试遇到函数调用怎么办?

    linux用gdb调试遇到函数调用怎么办? 在Linux上使用GDB调试时,遇到函数调用是一个常见的情况。函数
    的头像 发表于 01-31 10:33 677次阅读