IA-32函数调用栈帧
//hello.c
int add(int x,int y){
return x+y;
}
int main(void){
int s=11;
int b=23;
int c=add(b,s);
return 0;
}
反汇编代码
m@ubuntu:~$ gcc -S -m32 hello.c -o hello.s -fno-asynchronous-unwind-tables
m@ubuntu:~$ cat hello.s
.file "hello.c"
.text
.globl add
.type add, @function
add:
pushl %ebp
movl %esp, %ebp
call __x86.get_pc_thunk.ax
addl $_GLOBAL_OFFSET_TABLE_, %eax
movl 8(%ebp), %edx
movl 12(%ebp), %eax
addl %edx, %eax
popl %ebp
ret
.size add, .-add
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
call __x86.get_pc_thunk.ax
addl $_GLOBAL_OFFSET_TABLE_, %eax
movl $11, -12(%ebp)
movl $23, -8(%ebp)
pushl -12(%ebp)
pushl -8(%ebp)
call add
addl $8, %esp
movl %eax, -4(%ebp)
movl $0, %eax
leave
ret
.size main, .-main
.section .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
.globl __x86.get_pc_thunk.ax
.hidden __x86.get_pc_thunk.ax
.type __x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
movl (%esp), %eax
ret
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
分析一下汇编代码,main主要压进了这样的数
空
23
11
空
11
23
这个时候看一下objdump出来的数据
gcc -m32 -g hello.s -o hello.out
objdump -d ./hello.out > hello.dmp
得到
000004ed <add>:
4ed: 55 push %ebp
4ee: 89 e5 mov %esp,%ebp
4f0: e8 45 00 00 00 call 53a <__x86.get_pc_thunk.ax>
4f5: 05 e7 1a 00 00 add $0x1ae7,%eax
4fa: 8b 55 08 mov 0x8(%ebp),%edx
4fd: 8b 45 0c mov 0xc(%ebp),%eax
500: 01 d0 add %edx,%eax
502: 5d pop %ebp
503: c3 ret
00000504 <main>:
504: 55 push %ebp
505: 89 e5 mov %esp,%ebp
507: 83 ec 10 sub $0x10,%esp
50a: e8 2b 00 00 00 call 53a <__x86.get_pc_thunk.ax>
50f: 05 cd 1a 00 00 add $0x1acd,%eax
514: c7 45 f4 0b 00 00 00 movl $0xb,-0xc(%ebp)
51b: c7 45 f8 17 00 00 00 movl $0x17,-0x8(%ebp)
522: ff 75 f4 pushl -0xc(%ebp)
525: ff 75 f8 pushl -0x8(%ebp)
528: e8 c0 ff ff ff call 4ed <add>
52d: 83 c4 08 add $0x8,%esp
530: 89 45 fc mov %eax,-0x4(%ebp)
533: b8 00 00 00 00 mov $0x0,%eax
538: c9 leave
539: c3 ret
call 4ed <add>
其中4ed就是add的地址,call的下一条指令地址52d会被push到栈
这时候栈帧变成
0xc(%ebp) 11
0x8(%ebp) 23
0x4(%ebp) 52d
0x0(%ebp) 旧的ebp
---%ebp
于是add的mov 0x8(%ebp),%edx
和mov 0xc(%ebp),%eax
分别将edx赋值为23,将eax赋值为11。add %edx,%eax
指令和刚才寄存器edx和寄存器eax的参数值副本执行加法运算,计算后的结果将保存到eax寄存器。因为eax是作为返回值参数传递的。
这里发现add的栈帧是0大小,所以pop %ebp
就直接将原来main的旧ebp保存到ebp寄存器里,相当于切换回main的栈帧。此时,esp也会自动变大,因为弹出东西了,指向为main栈帧的顶部。
ret就是把返回地址52d弹回到eip中。从而继续main的下一条指令。
这个时候,main中add 0x8,%esp
相当于收回了用于传参的两个内存空间,mov %eax,-0x4(%ebp)
相当于将add的结果保存在栈底那个位置,最后内存布局如下
---ebp
34
23
11
---esp
end