通过x86汇编再看内存中的函数调用过程

以前通过C语言的学习大概了解了一些函数调用的过程。即在进程的栈中存在栈指针和基指针两种指针,一直指向栈顶,而基指针则指向当前函数的返回值。通过改变栈指针和基指针来实现函数的调用和返回。自从接触汇编后才对这块儿有了更深的理解。

在x86平台的汇编中栈涉及到三个寄存器:
SS(段寄存器)
SP(栈指针)
BP(基指针)
SS寄存器一直指向栈段地址,SP指向栈段偏移地址,因为汇编中地址的表示方式是"XXXX:XXXX"(不过居然不存在栈底这个概念)。

当一个函数在执行的过程中需要调用另一个函数时则会使用call指令,call指令执行时会先将IP寄存器(代码段偏移地址)的值push入栈中然后再调用jmp指令进行跳转,跳转到调用函数的地址,然后开始执行调用的函数的代码。

在执行完成后再使用ret指令进行返回,ret指令会将栈顶元素pop到IP寄存器中从而继续执行之前函数的代码。

那么举个例子,先上C语言测试代码:

int cz(int a, int b) {
    return a + b;
}

int main() {
    int c = cz(7, 8);
    return 0;
}

内容很简单,先定义一个cz函数,将两个变量a和b相加并返回。之后在main函数里进行调用。编译成可执行文件后再用反汇编工具看到汇编代码如下:

        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_4: int32_t, -4
        ;    var_8: int32_t, -8


                     _cz:;cz函数入口
0000000100000f60         push       rbp    ;将BP指针推入栈,BP此时的内容是cz函数的返回栈地址
0000000100000f61         mov        rbp, rsp
0000000100000f64         mov        dword [rbp+var_4], edi;下面几步执行a和b的相加
0000000100000f67         mov        dword [rbp+var_8], esi
0000000100000f6a         mov        esi, dword [rbp+var_4];由于栈是向下生长,所以地址要做减法
0000000100000f6d         add        esi, dword [rbp+var_8]
0000000100000f70         mov        eax, esi
0000000100000f72         pop        rbp
0000000100000f73         ret;函数返回并更新IP指针让main函数继续执行
                        ; endp
0000000100000f74         align      128


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_4: int32_t, -4
        ;    var_8: int32_t, -8


                     _main:;mian函数入口
0000000100000f80         push       rbp
0000000100000f81         mov        rbp, rsp
0000000100000f84         sub        rsp, 0x10
0000000100000f88         mov        edi, 0x7                                    ; 参数a
0000000100000f8d         mov        esi, 0x8                                    ; 参数b
0000000100000f92         mov        dword [rbp+var_4], 0x0
0000000100000f99         call       _cz                                     ;执行cz函数
0000000100000f9e         xor        esi, esi
0000000100000fa0         mov        dword [rbp+var_8], eax
0000000100000fa3         mov        eax, esi
0000000100000fa5         add        rsp, 0x10
0000000100000fa9         pop        rbp
0000000100000faa         ret
                        ; endp
0000000100000fab         db  0x90 ; '.'

关键的地方加了注释,由于我的电脑是64位机,所以寄存器前面加了“r”表示取64位的值。

PS:当时在索尼现场搞bug的时候经常会看到的PC指针应该就是高通平台的代码段片偏移地址,当时看着底层的各种crash栈信息真是一脸懵逼。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,972评论 1 19
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 8,137评论 0 27
  • 从本篇开始,我们讨论一些高级语言中的基础设施:堆栈,函数调用,变量生命周期等等话题。因为这里本身会涉及到比较多的汇...
    SlayerNux阅读 13,421评论 1 28
  • 首先寄存器使用惯例:eip :指令地址寄存器,保存程序计数器的值,当前执行的指令的下一条指令的地址值,16位中为i...
    扎Zn了老Fe阅读 2,104评论 0 0
  • (一) 1 我第一次见到大卫是两年前的冬天,我穿过琨拧东路的银杏林路上全是疯了一样的所谓...
    米罗卫阅读 740评论 0 0

友情链接更多精彩内容