作者: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的默认行为。

64位x86的函数调用栈布局相关推荐

  1. c语言与64位windows不兼容_微软发布可模拟 64 位 x86 程序的 ARM 版 Windows 10

    微软今天宣布推出可以在 ARM 架构 PC 上模拟 64 位 x86 程序的新版 Windows 10.这意味着,拥有 ARM PC 的用户,比如 Surface Pro X 可以安装 64 位 x8 ...

  2. 一文入门64位x86汇编

    本文我试图用学习一个普通编程语言的思路讲述x86_64汇编. 本文所有汇编代码均在linux系统写成,并且使用了很多linux系统调用. 需要C语言基础. 持续更新中. 目录 〇.汇编语言的选择 (一 ...

  3. 浅淡Windows7 32位与64位/x86与x64的区别

    看到有很多会员问到底是选Windows7 x86,还是选x64.这里简单的谈一下这这两种系统的区别. 简单的说x86代表32位操作系统  x64代表64位操作系统. 简单的判断电脑是否支持64位操作系 ...

  4. 不要再被误导了,64位X86 CPU是没有64位寻址能力的!

    本文转载于:http://itbbs.pconline.com.cn/9769891.html 最近这几天在CPU超频版发了个悬赏贴,特意看看有多少人认为CPU的位宽意味着寻址能力,结果发现也有相当一 ...

  5. [分享]Win7 32位与64位/x86与x64的区别

    看到有很多会员问到底是选Windows7 x86,还是选x64.这里简单的谈一下这这两种 系统 的区别. 简单的说x86代表32位操作系统  x64代表64位操作系统. 如果你的 CPU 是双核以上, ...

  6. TensorFlow Serving安装笔记(仅限64位x86)

    官方文档: https://tensorflow.google.cn/serving/setup 几个要点 翻墙下载公钥 https://storage.googleapis.com/tensorfl ...

  7. 关于x86_64和x32和x86和-386和32位还是64位的区分 指令集的学习

    这里写自定义目录标题 一,查到的知识 查看linux内核信息 查看linux版本信息 查看当前的系统位数 其他搜索到的信息 指令集和指令集架构的区分 关于x86_64和x32和x86和-386和32位 ...

  8. C 语言 函数调用栈

    From:https://www.cnblogs.com/clover-toeic/p/3755401.html    https://www.cnblogs.com/clover-toeic/p/3 ...

  9. C语言函数调用栈(一)

    以下全文转载自:C语言函数调用栈(一) 程序的执行过程可看作连续的函数调用.当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行.函数调用过程通常使用堆栈实现,每个用户态 ...

最新文章

  1. python怎么处理数据_python panda怎么处理数据
  2. 【Python】判断列表 list 是否为空
  3. VS 中配置使用Visual SVN系列 一:SVN Server下载和安装
  4. Socket请求获取数据
  5. Python3实现最小栈
  6. python network script
  7. spark的java源码,Spark源码包的编译
  8. python多维列表索引越界怎么处理_Python中remove漏删和索引越界问题的解决
  9. linux环境下grep的相关含义
  10. 在电脑上安装python-如何在自己的电脑上安装python的idle版 - 卡饭网
  11. 数据结构简单模拟银行排队系统
  12. Flutter开发:使用SafeArea(安全区域)
  13. 2016首次CCCC总结
  14. 计算机电路图解,简单逆变器电路图和工作原理图解大全
  15. 67 2020:我的总结和给圈友的话【2020-12-31 0003】
  16. 关于CM3/CM4位带操作的总结
  17. 选购云服务器时云盘该如何选择?(高性能/SSD/增强型/急速型)
  18. SuperMap iDesktop 之 BIM优化流程——建筑篇
  19. 软件开发综合实践实习小结
  20. 智能浇花系统(ESP8266+APP Inventor+DHT11)

热门文章

  1. python webserver模块,python探索之BaseHTTPServer-实现Web服务器介绍
  2. 订阅发布可靠吗_华辉人力资源可靠吗?发布的招聘信息是否可靠?
  3. 数据建模_浅谈数据仓库建设中的数据建模方法
  4. 二叉搜索树的删除_LeetCode109.有序链表转换二叉搜索树
  5. python scrapy框架 抓取的图片路径打不开图片_Python中Scrapy爬虫图片处理详解
  6. Vue父组件访问子组件属性和方法、父子组件双向绑定(两种方法)
  7. Java企业介绍,Hunson 介绍
  8. java赋值兼容原则,多态问题抛出(赋值兼容性原则遇上父类与子类同名函数的时候)...
  9. SCPPO(二十七):技术盛宴—报表交流会
  10. 前端那些事之Nuxt.js