# global data #
format: .ascii "%d\n"
mov $format, %rbx
mov (%rbx), %rdi
mov $1, %rsi
gcc -o main main.s
There are a number of issues with this code. The 64-bit Linux System V ABI calling convention requires a few things. It requires that just before a CALL that the stack be at least 16-byte (or 32-byte) aligned:
The end of the input argument area shall be aligned on a 16 (32, if __m256 is passed on stack) byte boundary.
After the C runtime calls your
main function the stack is misaligned by 8 because the return pointer was placed on the stack by CALL. To realign to 16-byte boundary you can simply PUSH any general purpose register onto the stack and POP it off at the end.
The calling convention also requires the RAX contains the number of vector registers used for a variable argument function:
%rax is used to indicate the number of vector arguments passed to a function requiring a variable number of arguments
printf is a variable argument function, so RAX needs to be set. In this case you don't pass any parameters in a vector register so you can set RAX to 0.
You also dereference the $format pointer when it is already an address. So this is wrong:
mov $format, %rbx mov (%rbx), %rdi
This takes the address of format and places in in RBX. Then you take the 8 bytes at that address in RBX and place them in RDI. RDI needs to be a pointer to a string of characters, not the characters themselves. The two lines could be replaced with:
lea format(%rip), %rdi
This uses RIP Relative Addressing however in this case you could also use:
mov $format, %rdi
You should also NUL terminate your strings. Rather than use
.ascii you can use
.asciz on the x86 platform.
A working version of your program could look like:
# global data # .data format: .asciz "%d\n" .text .global main main: push %rbx lea format(%rip), %rdi mov $1, %esi # Writing to ESI zero extends to RSI. xor %eax, %eax # Writing to EAX zero extends to RAX. call printf pop %rbx ret
You should also be aware from the 64-bit Linux ABI, that the calling convention also requires functions you write to honor the preservation of certain registers. The list of registers and whether they should preserved is as follows:
Any register that says
Yes in the Preserved across
Register column are ones you must ensure are preserved across your function. Function
main is like any other C function.
If you have strings/data that you know will be read only you can place them in the
rodata section with
.section rodata rather than
In 64-bit mode: if you have a destination operand that is a 32-bit register, the CPU will zero extend the register across the entire 64-bit register. This can save bytes on the instruction encoding.