汇编中函数调用过程中,栈到底是怎样变化的?call、ret、指令分别有什么样的作用?

文章详细分析了栈帧的结构,包括保存的寄存器、本地变量等,并解释了call指令如何转移控制权到子程序,ret指令如何恢复程序计数器并返回,以及leaveq指令如何关闭栈帧。通过C语言代码示例`test2.c`的反汇编,展示了这些指令在函数调用过程中的实际应用。
摘要由CSDN通过智能技术生成

1、栈帧的结构:栈帧主要包括三个部分:被保存的%bp,被保存的寄存器、本地变量等,参数区域
2、call指令的作用:

  • 将程序下一条指令的位置的IP压入堆栈中;
  • 转移到调用的子程序

3、ret指令的作用:

  • pop PC,即将栈中%rsp的8个字节数据保存到程序计数器中,并且rsp -= 8(64位机器中)

4、leaveq指令的作用:用来关闭栈帧

  • mov rbp, rsp:令rsp = rbp
  • pop 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值