64位x86的函数调用栈布局

简介: 作者:gfree.wind@gmail.com博客:blog.focus-linux.net    linuxfocus.blog.chinaunix.net在看本文之前,如果不了解x86的32位机的函数布局的话,建议先阅读一下前一篇文章《如何手工展开函数栈定位问题》—— http://blog.
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net    linuxfocus.blog.chinaunix.net

在看本文之前,如果不了解x86的32位机的函数布局的话,建议先阅读一下前一篇文章《如何手工展开函数栈定位问题》—— http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=3029639

为啥还要就64位的情况单开一篇文章呢,难道64位与32位不一样吗?

还是先看测试代码:
  1. #include stdlib.h>
  2. #include stdio.h>


  3. static void test(void *p1, void *p2, int p3)
  4. {
  5.     p1 = p1;
  6.     p2 = p2;
  7.     p3 = p3;
  8. }

  9. int main()
  10. {
  11.     void *p1 = (void*)1;
  12.     void *p2 = (void*)2;
  13.     int p3 = 3;

  14.     test(p1, p2, p3);

  15.     return 0;
  16. }
编译gcc -g -Wall test.c,调试进入test
  1. (gdb) bt
  2. #0 test (p1=0x1, p2=0x2, p3=3) at test.c:10
  3. #1 0x0000000000400488 in main () at test.c:18

查看寄存器bp
  1. (gdb) info registers rbp
  2. rbp 0x7fffab620d00 0x7fffab620d00

那么检查栈的内容
  1. (gdb) x /16xg 0x7fffab620d00
  2. 0x7fffab620d00: 0x00007fffab620d30 0x0000000000400488
  3. 0x7fffab620d10: 0x00000000004004a0 0x0000000000000002
  4. 0x7fffab620d20: 0x0000000000000001 0x0000000300000000
  5. 0x7fffab620d30: 0x0000000000000000 0x00007f93bbaa11c4
  6. 0x7fffab620d40: 0x0000000000400390 0x00007fffab620e18
  7. 0x7fffab620d50: 0x0000000100000000 0x0000000000400459
  8. 0x7fffab620d60: 0x00007f93bc002c00 0x85b4aff07d2e87c7
  9. 0x7fffab620d70: 0x0000000000000000 0x00007fffab620e10

开始分析栈的内容:
1. 0x00007fffab620d30:为test调用者main的BP内容,没有问题;
2. 0x0000000000400488:为test的返回地址,与前面的bt输出相符,没有问题;
3. 0x00000000004004a0:——这个是什么东东??!!
4. 0x0000000000000002, 0x0000000000000001, 0x0000000300000000:这里也有不少疑问啊?!
1.  这个0x00000003是第3个参数?因为是整数所以在64位的机器上,只使用栈的一个单元的一半空间?
2. 参数的顺序为什么是3,1,2呢?难道是因为前两个参数为指针,第三个参数为int有关?

我在工作中遇到了类似的问题,所以才特意写了上面的测试代码,就为了测试相同参数原型的函数调用栈的问题。看到这里,感觉很奇怪,对于上面两个问题很困惑啊。上网也没有找到64位的x86函数调用栈的特别的资料。

难道64位机与32位机有这么大的不同?!大家先想一下,答案马上揭晓。

当遇到疑难杂症时,汇编则是王道:
  1. (gdb) disassemble main
  2. Dump of assembler code for function main:
  3. 0x0000000000400459 : push %rbp
  4. 0x000000000040045a : mov %rsp,%rbp
  5. 0x000000000040045d : sub $0x20,%rsp
  6. 0x0000000000400461 : movq $0x1,-0x10(%rbp)
  7. 0x0000000000400469 : movq $0x2,-0x18(%rbp)
  8. 0x0000000000400471 : movl $0x3,-0x4(%rbp)
  9. 0x0000000000400478 : mov -0x4(%rbp),%edx
  10. 0x000000000040047b : mov -0x18(%rbp),%rsi
  11. 0x000000000040047f : mov -0x10(%rbp),%rdi
  12. 0x0000000000400483 : callq 0x400448
  13. 0x0000000000400488 : mov $0x0,%eax
  14. 0x000000000040048d : leaveq
  15. 0x000000000040048e : retq
  16. End of assembler dump.
看红色部分的汇编代码,为调用test时的处理,原来64位机器上,调用test时,根本没有对参数进行压栈,所以上面对于栈内容的分析有误。后面的内存中存放的根本不是test的参数。看到汇编代码,我突然想起,由于64位cpu的寄存器比32位cpu的寄存器要多,所以gcc会尽量使用寄存器来传递参数来提高效率。

让我们重新运行程序,再次在test下查看寄存器内容:
  1. (gdb) info registers
  2. rax 0x7f141fea1a60 139724411509344
  3. rbx 0x7f14200c2c00 139724413742080
  4. rcx 0x4004a0 4195488
  5. rdx 0x3 3
  6. rsi 0x2 2
  7. rdi 0x1 1
  8. rbp 0x7fff9c08d380 0x7fff9c08d380
  9. rsp 0x7fff9c08d380 0x7fff9c08d380
这里rdx,rsi和rdi清晰的显示了三个参数的值,分别为3,2,1与前面的反汇编代码相符。

而前面被当做参数的0x0000000000000002, 0x0000000000000001和0x00000003,其实为main中的局部变量p2, p1和p3的定义。如前面反汇编代码中的蓝色代码,这三个局部变量在栈上的定义顺序为p3, p1和p2,与栈的内容相符。


我写本文的目的,主要是为了与大家分享一下64位机器上调试时需要注意的一个问题:函数调用时,编译器会尽量使用寄存器来传递参数,这点与32位机有很大不同。在我们的调试中,要特别注意这点。


注:关于压栈顺序,参数的传递方式等等,都可以通过编译选项来指定或者禁止的。本文的情况为GCC的默认行为。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
JavaScript 前端开发
layui实现点击按钮全屏
layui实现点击按钮全屏
580 0
|
8月前
|
人工智能 安全 数据库
AiCodeAudit-基于Ai大模型的自动代码审计工具
本文介绍了基于OpenAI大模型的自动化代码安全审计工具AiCodeAudit,通过图结构构建项目依赖关系,提高代码审计准确性。文章涵盖概要、整体架构流程、技术名词解释及效果演示,详细说明了工具的工作原理和使用方法。未来,AI大模型有望成为代码审计的重要工具,助力软件安全。项目地址:[GitHub](https://github.com/xy200303/AiCodeAudit)。
|
JSON 前端开发 JavaScript
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识
该文章提供了Webpack的基础入门指南,涵盖安装配置、基本使用、加载器(Loaders)、插件(Plugins)的应用,以及如何通过Webpack优化前端项目的打包构建流程。
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识
|
存储 人工智能 搜索推荐
AI 网关基于 IP 地理位置,增强 Prompt 修饰能力
本⽂对 Prompt 的使用方式进行了简单介绍,让大家了解到 Prompt 对于 LLM 的重要性。并尝试在 Prompt 中结合用户 Geo IP 信息,实现 LLM 的个性化回复,提升问答的准确度。
643 106
|
虚拟化 对象存储
虚拟机使用pc摄像头
虚拟机使用pc摄像头
1024 0
虚拟机使用pc摄像头
|
JavaScript 前端开发
vue2【详解】生命周期(含父子组件的生命周期顺序)
vue2【详解】生命周期(含父子组件的生命周期顺序)
1679 0
|
存储 监控 数据安全/隐私保护
云上智能视频分析:重塑视觉监控与决策的未来
云上智能视频分析平台注重数据安全和隐私保护。通过采用多种技术手段确保视频数据在传输、存储和处理过程中的安全性和隐私性,保护用户隐私和商业秘密不受侵犯。 四、未来发展趋势 4.1 边缘计算与云计算融合 随着物联网设备数量的不断增加和数据量的快速增长,边缘计算将成为云上智能视频分析的重要发展方向。通过将计算能力和数据处理能力下沉到边缘侧,可以减少数据传输延迟和带宽消耗,提高系统的响应速度和稳定性。 4.2 跨领域融合与创新 云上智能视频分析技术将与物联网
418 7
|
存储 网络协议 Linux
NFS(Network File System 网络文件服务)
NFS(Network File System 网络文件服务)
|
消息中间件 网络安全 数据安全/隐私保护
MQ产品使用合集之在MQTT中,出现了 NO HEART 错误如何解决
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
415 2