本文来讨论一下汇编中函数的实现。我的环境:ubuntu 18.04,gcc 7.5.0
我们首先看下我们的 c/c++ 函数代码,编译器会给我们生成什么样的汇编函数代码。
1 | void func() { |
我们通过编译,并且反汇编来查看汇编代码
1 | gcc main.c -g -m32 -o main |
如下是汇编代码:
1 | 0000054b <__x86.get_pc_thunk.ax>: |
我们来解释一下,我们知道生成的二进制执行过程前,会做很多初始化工作,之后才会跳到 main 函数执行。
- ebp 是栈底指针,
push ebp
先将栈底指针存储到栈中,此时 esp 会减 4,我们目前是 32 位机器架构,所以栈的每一个 slot 是 4 字节。 mov ebp,esp
将栈顶指针 esp 寄存器的值存入 ebp 中。- 接下来是
call 54b <__x86.get_pc_thunk.ax>
和add eax,0x1aa2
这两句汇编,我们先不看 call 4ed <func>
就是在调用 func 函数,call 指令会将 IP 寄存器保存到栈中,并跳到 func 地址出执行 func 函数- func 函数中也是先将 ebp 寄存器压栈,然后将 esp 寄存器的内容写入到 ebp 中。
sub esp, 0x10
是给 func 函数开辟一段栈内存用于函数内部使用mov dword ptr [ebp-0x04], 0xa
,这句代码就是int a = 10
,编译器给 a 在栈内存[ebp-0x04]
处分配了内存,并且标明了是 dword 4 字节大小- leave 的意思为
mov esp, ebp
和pop ebp
这两句代码 - 最后 ret 的意思是
pop IP
,从栈中取出数据给 IP 寄存器赋值,相当于跳到刚开始 call 为止的下一句代码 - 在 main 函数中,继续
pop ebp
和ret
即可退出 main 函数的栈