1.5. HELLO, WORLD!
Byte number:
7th 6th 5th 4th 3rd 2nd 1st 0th
RAXx64
EAX
AX
AH AL
Themain()function returns anint-typed value, which in C/C++ is still 32-bit, for better backward com-
patibility and portability, so that is why theEAXregister is cleared at the function end (i.e., the 32-bit part
of the register) instead of withRAX. There are also 40 bytes allocated in the local stack. This is called the
“shadow space”, which we’ll talk about later:1.10.2 on page 100.
GCC: x86-64
Let’s also try GCC in 64-bit Linux:
Listing 1.22: GCC 4.4.6 x64
.string "hello, world\n"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0 ; "hello, world\n"
xor eax, eax ; number of vector registers passed
call printf
xor eax, eax
add rsp, 8
ret
Linux, *BSD and Mac OS X also use a method to pass function arguments in registers. [Michael Matz,
Jan Hubicka, Andreas Jaeger, Mark Mitchell,System V Application Binary Interface. AMD64 Architecture
Processor Supplement, (2013)]^24.
The first 6 arguments are passed in theRDI,RSI,RDX,RCX,R8, andR9registers, and the rest—via the
stack.
So the pointer to the string is passed inEDI(the 32-bit part of the register). Why doesn’t it use the 64-bit
part,RDI?
It is important to keep in mind that allMOVinstructions in 64-bit mode that write something into the lower
32-bit register part also clear the higher 32-bits (as stated in Intel manuals:12.1.4 on page 1013).
I.e., theMOV EAX, 011223344hwrites a value intoRAXcorrectly, since the higher bits will be cleared.
If we open the compiled object file (.o), we can also see all the instructions’ opcodes^25 :
Listing 1.23: GCC 4.4.6 x64
.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\n"
.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
As we can see, the instruction that writes intoEDIat0x4004D4occupies 5 bytes. The same instruction
writing a 64-bit value intoRDIoccupies 7 bytes. Apparently, GCC is trying to save some space. Besides,
it can be sure that the data segment containing the string will not be allocated at the addresses higher
than 4GiB.
WealsoseethattheEAXregisterhasbeenclearedbeforetheprintf()functioncall. Thisisdonebecause
according toABI^26 standard mentioned above, the number of used vector registers is to be passed inEAX
in *NIX systems on x86-64.
(^24) Also available ashttps://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
(^25) This must be enabled inOptions→Disassembly→Number of opcode bytes
(^26) Application Binary Interface