- 第一章 CPU 简介
- 第二章 Hello,world!
- 第三章 函数开始和结束
- 第四章 栈
- Chapter 5 printf() 与参数处理
- Chapter 6 scanf()
- CHAPER7 访问传递参数
- Chapter 8 一个或者多个字的返回值
- Chapter 9 指针
- Chapter 10 条件跳转
- 第 11 章 选择结构 switch()/case/default
- 第 12 章 循环结构
- 第 13 章 strlen()
- Chapter 14 Division by 9
- chapter 15 用 FPU 工作
- Chapter 16 数组
- Chapter 17 位域
- 第 18 章 结构体
- 19 章 联合体
- 第二十章 函数指针
- 第 21 章 在 32 位环境中的 64 位值
- 第二十二章 SIMD
- 23 章 64 位化
- 24 章 使用 x64 下的 SIMD 来处理浮点数
- 25 章 温度转换
- 26 章 C99 的限制
- 27 章 内联函数
- 第 28 章 得到不正确反汇编结果
- 第 29 章 花指令
- 第 30 章 16 位 Windows
- 第 31 章 类
- 三十二 ostream
- 34.2.2 MSVC
- 34.2.3 C++ 11 std::forward_list
- 34.3 std::vector
- 34.4 std::map and std::set
2.2 x86-64
2.2.1 MSVC-x86-64
让我们来试试 64-bit 的 MSVC:
#!bash
$SG2989 DB ’hello, world’, 00H
main PROC
sub rsp, 40
lea rcx, OFFSET FLAT:$SG2923
call printf
xor eax, eax
add rsp, 40
ret 0
main ENDP
在 x86-64 里,所有被扩展到 64 位的寄存器都有 R-前缀。并且尽量不用栈来传递函数的参数了,大量使用寄存器来传递参数,非常类似于 fastcall。
在 win64 里,RCX,RDX,R8,R9 寄存器被用来传递函数参数,如果还有更多就使用栈,在这里我们可以看到 printf() 函数的参数没用通过栈来传递,而是使用了 rcx。 让我们针对 64 位来看,作为 64 位寄存器会有 R-前缀,并且这些寄存器向下兼容,32 位的部分使用 E-前缀。
如下图所示,这是 RAX/EAX/AX/AL 在 64 位 x86 兼容 cpu 里的情况?
在 main() 函数会返回一个 int 类型的值,在 64 位的程序里为了兼容和移植性,还是用 32 位的,所以可以看到 EAX(寄存器的低 32 位部分)在函数最后替代 RAX 被清空成 0。
2.2.2 GCC-x86-64
这次试试 GCC 在 64 位的 Linux 里:
#!bash
.string "hello, world"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0 ; "hello, world"
xor eax, eax ; number of vector registers passed
call printf
xor eax, eax
add rsp, 8
ret
在 Linux,*BSD 和 Mac OS X 里使用同一种方式来传递函数参数。头 6 个参数使用 RDI,RSI,RDX,RCX,R8,R9
来传递的,剩下的要靠栈。
所以在这个程序里,字串的指针被放到 EDI(RDI 的低 32 位部)。为什么不是 64 位寄存器 RDI 那?
这是一个重点,在 64 位模式下,对低 32 位进行操作的时候,会清空高 32 位的内容。比如 MOV EAX,011223344h 将会把值写到 RAX 里,并且清空 RAX 的高 32 位区域。 如果我们打开编译好的对象文件(object file(.o)),我们会看到所有的指令:
Listing 2.8:GCC 4.4.6 x64
#!bash
.text:00000000004004D0 main proc near
.text:00000000004004D0 48 83 EC 08 sub rsp, 8
.text:00000000004004D4 BF E8 05 40 00 mov edi, offset format ; "hello, world"
.text:00000000004004D9 31 C0 xor eax, eax
.text:00000000004004DB E8 D8 FE FF FF call _printf
.text:00000000004004E0 31 C0 xor eax, eax
.text:00000000004004E2 48 83 C4 08 add rsp, 8
.text:00000000004004E6 C3 retn
.text:00000000004004E6 main endp
就像看到的那样,在 04004d4 那行给 edi 写字串指针的那句花了 5 个 bytes。如果把这句换做给 rdi 写指针,会花掉 7 个 bytes.就是说 GCC 在试图节省空间,为此数据段(data segment) 中包含的字串不会被分配到高于 4GB 地址的空间上。
可以看到在 printf() 函数调用前 eax 被清空了,这样做事因为要 eax 被用作传递向量寄存器(vector registers) 的个数。
参考【21】 MichaelMatz/JanHubicka/AndreasJaeger/MarkMitchell. Systemvapplicationbinaryinterface.amdarchitecture processor supplement, . Also available as http://x86-64.org/documentation/abi.pdf .
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论