返回介绍

2.2 x86-64

发布于 2025-02-22 14:00:41 字数 2684 浏览 0 评论 0 收藏 0

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 里的情况?

enter image description here

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文