1、栈帧的结构:栈帧主要包括三个部分:被保存的%bp
,被保存的寄存器、本地变量等,参数区域
2、call指令的作用:
- 将程序下一条指令的位置的IP压入堆栈中;
- 转移到调用的子程序
3、ret指令的作用:
- pop PC,即将栈中
%rsp
的8个字节数据保存到程序计数器中,并且rsp -= 8
(64位机器中)
4、leaveq指令的作用:用来关闭栈帧
mov rbp, rsp
:令rsp = rbppop rbp
:恢复之前的rbq
4、实际举例:
//test2.c
#include <stdio.h>
int addNum(int a, int b){
int x = a * 10;
int y = b *5;
int z = x * y;
return z;
}
void foo(int a, int b)
{
int x = a + 2;
int y = b + 1;
int z = x * y;
addNum(a, b);
}
int main()
{
int x = 1, y = 2;
foo(x, y);
return 0;
}
编译并反汇编
gcc -g -o test2 test2.c
objdump -S test2 > test2.asm
反汇编文件:
//test2.asm
0000000000001129 <addNum>:
#include <stdio.h>
int addNum(int a, int b){
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: 89 7d ec mov %edi,-0x14(%rbp)
1134: 89 75 e8 mov %esi,-0x18(%rbp)
int x = a * 10;
1137: 8b 55 ec mov -0x14(%rbp),%edx
113a: 89 d0 mov %edx,%eax
113c: c1 e0 02 shl $0x2,%eax
113f: 01 d0 add %edx,%eax
1141: 01 c0 add %eax,%eax
1143: 89 45 f4 mov %eax,-0xc(%rbp)
int y = b *5;
1146: 8b 55 e8 mov -0x18(%rbp),%edx
1149: 89 d0 mov %edx,%eax
114b: c1 e0 02 shl $0x2,%eax
114e: 01 d0 add %edx,%eax
1150: 89 45 f8 mov %eax,-0x8(%rbp)
int z = x * y;
1153: 8b 45 f4 mov -0xc(%rbp),%eax
1156: 0f af 45 f8 imul -0x8(%rbp),%eax
115a: 89 45 fc mov %eax,-0x4(%rbp)
return z;
115d: 8b 45 fc mov -0x4(%rbp),%eax
}
1160: 5d pop %rbp
1161: c3 retq
0000000000001162 <foo>:
void foo(int a, int b)
{
1162: f3 0f 1e fa endbr64
1166: 55 push %rbp
1167: 48 89 e5 mov %rsp,%rbp
116a: 48 83 ec 18 sub $0x18,%rsp
116e: 89 7d ec mov %edi,-0x14(%rbp)
1171: 89 75 e8 mov %esi,-0x18(%rbp)
int x = a + 2;
1174: 8b 45 ec mov -0x14(%rbp),%eax
1177: 83 c0 02 add $0x2,%eax
117a: 89 45 f4 mov %eax,-0xc(%rbp)
int y = b + 1;
117d: 8b 45 e8 mov -0x18(%rbp),%eax
1180: 83 c0 01 add $0x1,%eax
1183: 89 45 f8 mov %eax,-0x8(%rbp)
int z = x * y;
1186: 8b 45 f4 mov -0xc(%rbp),%eax
1189: 0f af 45 f8 imul -0x8(%rbp),%eax
118d: 89 45 fc mov %eax,-0x4(%rbp)
addNum(a, b);
1190: 8b 55 e8 mov -0x18(%rbp),%edx
1193: 8b 45 ec mov -0x14(%rbp),%eax
1196: 89 d6 mov %edx,%esi
1198: 89 c7 mov %eax,%edi
119a: e8 8a ff ff ff callq 1129 <addNum>
}
119f: 90 nop
11a0: c9 leaveq
11a1: c3 retq
00000000000011a2 <main>:
int main()
{
c: f3 0f 1e fa endbr64 # rsp = 0x7fffffffdf28
11a6: 55 push %rbp # rsp = 0x7fffffffdf20,rbp=0x0
11a7: 48 89 e5 mov %rsp,%rbp
11aa: 48 83 ec 10 sub $0x10,%rsp # rsp = 0x7fffffffdf10
int x = 1, y = 2;
11ae: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
11b5: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
foo(x, y);
11bc: 8b 55 fc mov -0x4(%rbp),%edx
11bf: 8b 45 f8 mov -0x8(%rbp),%eax
11c2: 89 d6 mov %edx,%esi
11c4: 89 c7 mov %eax,%edi
11c6: e8 97 ff ff ff callq 1162 <foo>
return 0;
11cb: b8 00 00 00 00 mov $0x0,%eax
}
11d0: c9 leaveq
11d1: c3 retq
11d2: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
11d9: 00 00 00
11dc: 0f 1f 40 00 nopl 0x0(%rax)
接着打印关键节点的栈帧情况:
1、11a2: mian函数还没开始运行之前
%rsp = 0x7fffffffdf28
0x7fffffffdf28: 0x00007ffff7dbe0b3 0x0000000000000050
0x7fffffffdf38: 0x00007fffffffe018 0x00000001f7f827a0
0x7fffffffdf48: 0x00005555555551a2 0x00005555555551e0
0x7fffffffdf58: 0x81bfdda5e3d667e4 0x0000555555555040
0x7fffffffdf68: 0x00007fffffffe010 0x0000000000000000
2、11c4: main.c中进入foo函数之前,即callq 1162 <foo>
之前
0x7fffffffdf10: 0x00007fffffffe010 0x0000000200000001
0x7fffffffdf20: 0x0000000000000000 0x00007ffff7dbe0b3
0x7fffffffdf30: 0x0000000000000050 0x00007fffffffe018
0x7fffffffdf40: 0x00000001f7f827a0 0x00005555555551a2
0x7fffffffdf50: 0x00005555555551e0 0x81bfdda5e3d667e4
3、1162: 刚进入foo函数:由于call指令,将call指令的下一条指令地址压入栈中即0x00005555555551cb
0x7fffffffdf08: 0x00005555555551cb 0x00007fffffffe010
0x7fffffffdf18: 0x0000000200000001 0x0000000000000000
0x7fffffffdf28: 0x00007ffff7dbe0b3 0x0000000000000050
0x7fffffffdf38: 0x00007fffffffe018 0x00000001f7f827a0
0x7fffffffdf48: 0x00005555555551a2 0x00005555555551e0
4、119a: 进入add函数之前:
0x7fffffffdee8: 0x0000000100000002 0x00000003f7f8b2e8
0x7fffffffdef8: 0x0000000900000003 0x00007fffffffdf20
0x7fffffffdf08: 0x00005555555551cb 0x00007fffffffe010
0x7fffffffdf18: 0x0000000200000001 0x0000000000000000
0x7fffffffdf28: 0x00007ffff7dbe0b3 0x0000000000000050
5、1160: pop之前:
0x7fffffffded8: 0x00007fffffffdf00 0x000055555555519f//原来的rbp, 返回地址
0x7fffffffdee8: 0x0000000100000002 0x00000003f7f8b2e8
0x7fffffffdef8: 0x0000000900000003 0x00007fffffffdf20
0x7fffffffdf08: 0x00005555555551cb 0x00007fffffffe010
0x7fffffffdf18: 0x0000000200000001 0x0000000000000000
6、1161: pop之后,ret之前:将8byte数据弹出
0x7fffffffdee0: 0x000055555555519f 0x0000000100000002
0x7fffffffdef0: 0x00000003f7f8b2e8 0x0000000900000003
0x7fffffffdf00: 0x00007fffffffdf20 0x00005555555551cb
0x7fffffffdf10: 0x00007fffffffe010 0x0000000200000001
0x7fffffffdf20: 0x0000000000000000 0x00007ffff7dbe0b3
7、119f: ret之后,程序由addNum函数返回到foo函数
0x7fffffffdee8: 0x0000000100000002 0x00000003f7f8b2e8
0x7fffffffdef8: 0x0000000900000003 0x00007fffffffdf20
0x7fffffffdf08: 0x00005555555551cb 0x00007fffffffe010
0x7fffffffdf18: 0x0000000200000001 0x0000000000000000
0x7fffffffdf28: 0x00007ffff7dbe0b3 0x0000000000000050
8、11d0: leaveq之后:
0x7fffffffdf08: 0x00005555555551cb 0x00007fffffffe010
0x7fffffffdf18: 0x0000000200000001 0x0000000000000000
0x7fffffffdf28: 0x00007ffff7dbe0b3 0x0000000000000050
0x7fffffffdf38: 0x00007fffffffe018 0x00000001f7f827a0
0x7fffffffdf48: 0x00005555555551a2 0x00005555555551e0
9、11cb: foo函数返回之后:
0x7fffffffdf10: 0x00007fffffffe010 0x0000000200000001
0x7fffffffdf20: 0x0000000000000000 0x00007ffff7dbe0b3
0x7fffffffdf30: 0x0000000000000050 0x00007fffffffe018
0x7fffffffdf40: 0x00000001f7f827a0 0x00005555555551a2
0x7fffffffdf50: 0x00005555555551e0 0x60c5e5ac768c82ff