函数调用本质

文章来源:CoderHong 的博客

从反汇编角度窥探平时开发调用的函数或者方法的本质。平时我们编写的高级语言最终通过编译器、链接生成机CPU执行的机器指令。 不同的CPU对应着不同着机器指令,并且每一条机器指令对应着一条汇编。

先看一个最简单的C语言函数,这里主要通过C++来反编译分析汇编指令。

1.png

可以通过反汇编看到调用func函数的汇编指令,当前环境是8086汇编。

2.png

通过最终的汇编指令可以看出,在执行调用一个函数:本质就是通过call指令调用函数在代码段的地址进行直接调用。

注意:在上面的汇编指令可以看到当函数执行完毕,执行ret汇编指令退出函数。其实一个完整的函数调用必定包含callret指令。

那么只有了解了callret才能彻底从最根本了解函数的调用过程。

call 标号
1.将下一条指令的偏移地址入栈
2.转到标号出执行指令
ret
将栈顶的值出栈,赋值给IP

下面通过汇编代码调用 printf 函数标号打印 HelloWorld 执行验证上面的结论。

3.png

在即将执行执行 printf 函数之前栈顶指针SP指向内存单元的数据。

4.png

上面说到执行函数前会将下一条指令的偏移地址入栈,上图可以看出的下一条CPU执行的指令偏移地址IP为:000D。开始执行,看下栈顶指针SP的指向和指向内存单元的数据

5.png

函数 printf 执行完毕后,执行 ret 指令,栈顶偏移地址出栈赋值给 IP 中,栈顶指针向上移动两个字节。

6.png

不管什么开发语言最终都会转成二进制汇编指令,对应着相应的汇编指令,本质都是一致的。这里是通过C++反汇编窥探函数调用本质。

上述介绍只是最简单函数调用,一说到函数首先就会想到函数的三要素,函数的返回值函数的参数、局部变量**。

返回值

如果调用函数想拿到函数返回值,就得有容器来存放返回值,我们可以想到用栈、数据区、寄存器来保存。

首先栈段不可以的,如下图,函数内部push返回值,栈顶存储的是CPU函数执行完毕后的IP的偏移地址。

7.png

可以考虑将返回值放入数据段,这个需要与调用者约好协议,比如约定好将返回值放在ds:[0]

8.png

这样侧面证明了数据段里的数据是全局,全局区的数据是作用域是全局的。上面的实例代码好比下面的C++代码。

9.png

在实际中,大多数平台,windows、linux、Android等通常的做法是将方法返回值放在寄存器ax。其实这样的效率比上面返回值放在全局区效率高,CPU从寄存器中读取数据要快,放在全局区需要从内存先读取到寄存器。

10.png

下面在X86环境下写一段代码看下汇编指令

11.png

参数

同样我们先考虑将参数放入数据段来实现一个求和的函数。

12.png

放在数据段是可以的,在我们概念中形参的作用于是数据函数内部,函数执行完毕形参所占用的内存空间会被回收。这样就很明显了,通常,形参是放在栈中的。

13.png

注意:在函数调用完毕后,一定要保证栈平衡,否者会导致栈的空间会被用完,通常保持栈平衡有两种方式:内平栈和外平栈。

上面的案例是使用了外平栈方式,也就是在函数调用完毕后,对栈顶指针进行回复到函数调用前的位置。

14.png

对于函数的封装性跟人觉的栈内平衡的方式会好一些,让函数调用者不用关心内部细节。函数的形参本质了解后,接下来窥探最后一个函数的局部变量本质,这个相对复杂一些。

局部变量

函数的内部需要定义局部变量,C语言特别简单,那么在汇编中怎么分配内存空间给局部变量呢,局部变量的作用域只是当前函数,函数执行完毕后局部所栈中的空间被回收,因此局部变量空间分配还是通过栈来实现。

15.png

上面开始没有问题,唯一缺陷是在函数内部调用函数时,由于我们没有对bp进行恢复,一旦对函数内部在调用函数就会存存在问题, 因此需要对bp进行记录和恢复。

16.png
17.png
18.png

函数的调用流程总结

1  push参数,参数入栈
2  将函数的返回地址(下一条指令的地址)入栈
3  保护sp,将sp赋值给bp
4  分配一定的空间给函数的局部变量使用(让sp减去该空间大小),为了安全,用CC填充(int 3h)
5  保护寄存器, 因为在函数执行过程中会修改寄存器的值,所以在修改之前保存一下之前的值,后面再还原
6  具体的业务代码
7  恢复寄存器的值,跟第5步相反
8  将bp赋值给sp,恢复bp
9  返回(ret)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352