汇编中函数调用过程中,栈到底是怎样变化的?call、ret、指令分别有什么样的作用?
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 %rbp112e: 48 89 e5 mov %rsp,%rbp1131: 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),%edx113a: 89 d0 mov %edx,%eax113c: c1 e0 02 shl $0x2,%eax113f: 01 d0 add %edx,%eax1141: 01 c0 add %eax,%eax1143: 89 45 f4 mov %eax,-0xc(%rbp)int y = b *5;1146: 8b 55 e8 mov -0x18(%rbp),%edx1149: 89 d0 mov %edx,%eax114b: c1 e0 02 shl $0x2,%eax114e: 01 d0 add %edx,%eax1150: 89 45 f8 mov %eax,-0x8(%rbp)int z = x * y;1153: 8b 45 f4 mov -0xc(%rbp),%eax1156: 0f af 45 f8 imul -0x8(%rbp),%eax115a: 89 45 fc mov %eax,-0x4(%rbp)return z;115d: 8b 45 fc mov -0x4(%rbp),%eax
}1160: 5d pop %rbp1161: c3 retq 0000000000001162 <foo>:
void foo(int a, int b)
{1162: f3 0f 1e fa endbr64 1166: 55 push %rbp1167: 48 89 e5 mov %rsp,%rbp116a: 48 83 ec 18 sub $0x18,%rsp116e: 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),%eax1177: 83 c0 02 add $0x2,%eax117a: 89 45 f4 mov %eax,-0xc(%rbp)int y = b + 1;117d: 8b 45 e8 mov -0x18(%rbp),%eax1180: 83 c0 01 add $0x1,%eax1183: 89 45 f8 mov %eax,-0x8(%rbp)int z = x * y;1186: 8b 45 f4 mov -0xc(%rbp),%eax1189: 0f af 45 f8 imul -0x8(%rbp),%eax118d: 89 45 fc mov %eax,-0x4(%rbp)addNum(a, b);1190: 8b 55 e8 mov -0x18(%rbp),%edx1193: 8b 45 ec mov -0x14(%rbp),%eax1196: 89 d6 mov %edx,%esi1198: 89 c7 mov %eax,%edi119a: e8 8a ff ff ff callq 1129 <addNum>
}119f: 90 nop11a0: c9 leaveq 11a1: c3 retq 00000000000011a2 <main>:int main()
{c: f3 0f 1e fa endbr64 # rsp = 0x7fffffffdf2811a6: 55 push %rbp # rsp = 0x7fffffffdf20,rbp=0x011a7: 48 89 e5 mov %rsp,%rbp11aa: 48 83 ec 10 sub $0x10,%rsp # rsp = 0x7fffffffdf10int 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),%edx11bf: 8b 45 f8 mov -0x8(%rbp),%eax11c2: 89 d6 mov %edx,%esi11c4: 89 c7 mov %eax,%edi11c6: 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
汇编中函数调用过程中,栈到底是怎样变化的?call、ret、指令分别有什么样的作用?相关推荐
- C++ 函数调用过程中栈区的变化——(栈帧、esp、ebp)
C++ 函数调用过程中栈区的变化 1.C++ 函数调用过程中栈区的变化 1.1.程序的内存分布 1.2.函数调用过程中栈的变化解析 参考 1.C++ 函数调用过程中栈区的变化 1.1.程序的内存分布 ...
- 函数调用过程中的栈帧结构及其变化
前言:本文旨在从汇编代码的角度出发,分析函数调用过程中栈帧的变化. 栈帧的简单介绍: 当某个函数运行时,机器需要分配一定的内存去进行函数内的各种操作,这个过程中分配的那部分栈称为栈帧.下图描述了栈帧的 ...
- 函数调用过程中函数栈详解
当进程被加载到内存时,会被分成很多段 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写,如果发生写操作则会提示segmentation fault 数据段:保存初始化的全局变量和静 ...
- 从函数调用过程中的堆栈变化理解缓冲区溢出
一.说明 本来是想直接写一个缓冲区溢出的例子,但是一是当前编译器和操作系统有溢出的保护措施没有完全弄清怎么取消,二是strcpy等遇到00会截断需要进行编码这比较难搞,所以最终没有实现. 但已经双看了 ...
- C语言编程宏定义的优缺点,C语言重要知识点总结(二)--内存结构、函数调用过程(栈帧)、宏的优缺点以及##和#的使用...
一.内存结构 内存大致可以分为四个部分:代码段,静态存储区,堆,栈. 具体划分如下图所示: 栈:在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束后会自动释放内存.栈内存的分配运算 ...
- c语言函数调用过程中栈的工作原理理解
差不多每个程序员都知道,函数调用过程,就是层层入栈出栈的过程. 那么这个过程中的详细的细节是什么样子的呢? 阅读了以下几篇文章之后,对整个过程基本理解了: C函数调用过程原理及函数栈帧分析 阅读经典- ...
- C语言的函数调用过程(栈帧的创建与销毁)
从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: 1 int Add(int x,int y)2 {3 int sum = 0;4 sum = x + y;5 return sum;6 ...
- 深入浅出根据函数调用过程谈栈回溯原理
通过分析函数调用过程的堆栈变化,可以看出在被调函数的EBP寄存器地址存放的是调用函数的EBP寄存器地址,EBP地址+4存放的是函数调用完成后的下一条指令存放地址,该指令的前一条 ...
- 函数调用过程以及栈帧详解
函数的调用是一个过程,那么在函数的调用过程中要开辟栈空间,用来对本次函数的调用中需要的临时变量保存.这块空间叫栈帧.这个过程调用包括将数据和控制从代码的一部分传递到另一部分.过程调用的任务:为过程的局 ...
最新文章
- 高德地图自定义点标记大小_Vue:如何在地图上添加自定义覆盖物(点)
- C#——《C#语言程序设计》实验报告——继承与多态——银行ATM程序
- java8日期转时间戳_Java 8日期和时间
- mysql使用裸设备_请教dd清空裸设备问题
- 03-18 OpenSTF-手机设备管理平台
- LW_OOPC介绍(转载)
- 浅谈微服务下异常处理
- node mysql 事件循环_nodejs事件和事件循环详解
- SD卡无法格式化怎么办?恢复SD卡这样做
- android mp3 lrc歌词文件utf-8歌词显示为乱码,Android访问Tomcat错误以及mp3player项目乱码问题解决...
- WEB入门.九 导航菜单
- WIN10解决“任务管理器被系统管理员禁用”问题
- unity显示no camera rendering
- oracle 4098,ORA-04098错误解决方法-数据库专栏,ORACLE
- SCI论文论点陈述经验分享
- Layabox开发微信小游戏好友排行榜功能流程
- 【图像超分辨率】Accurate Image Super-Resolution Using Very Deep Convolutional Networks
- 涉密计算机的硬盘需要销毁,销毁电脑硬盘的办法
- 免费好用的录屏软件-kk录像机
- 解密方舟编译器和EMUI未来四大演进方向
热门文章
- python用with读文件的好处_python小课堂39 - 用 with 优雅的读写文件
- 新手必备webstorm安装教程
- HTML 基本标签大全
- 微信公众号消息免打扰开启与关闭
- FPGA NCO+LPM_MULT+FIRip核 实现乘法+低通滤波 使用及仿真(quartusii 13.1+modelsimse 10.5)
- R包rdist、Python sklearn计算pairwise distance
- c语言递归求差分方程,关于差分方程
- 如何让小米手机其中一个应用一直保持wifi连接设置
- DES的雪崩效应分析
- Python实现因子分析(附案例实战)