2.2.2 GCC-x86-64

这次试试GCC在64位的Linux里:

  1. .string "hello, world"
  2. main:
  3. sub rsp, 8
  4. mov edi, OFFSET FLAT:.LC0 ; "hello, world"
  5. xor eax, eax ; number of vector registers passed
  6. call printf
  7. xor eax, eax
  8. add rsp, 8
  9. 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

  1. .text:00000000004004D0 main proc near
  2. .text:00000000004004D0 48 83 EC 08 sub rsp, 8
  3. .text:00000000004004D4 BF E8 05 40 00 mov edi, offset format ;"hello, world"
  4. .text:00000000004004D9 31 C0 xor eax, eax
  5. .text:00000000004004DB E8 D8 FE FF FF call _printf
  6. .text:00000000004004E0 31 C0 xor eax, eax
  7. .text:00000000004004E2 48 83 C4 08 add rsp, 8
  8. .text:00000000004004E6 C3 retn
  9. .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.