I've written a program using AT&T syntax for use with GNU assembler:
.data
format: .ascii "%d\n"
.text
.global main
main:
mov $format, %rbx
mov (%rbx), %rdi
mov $1, %rsi
call printf
ret
I use GCC to assemble and link with:
gcc -o main main.s
I run it with this command:
./main
When I run the program I get a seg fault. By using gdb, it says printf
not found. I have tried ".extern printf", which does not work. Someone suggested I should store the stack pointer before calling printf
and restore before RET, How do I do that?
You can look at assembly code generated from an equivalent c file.
Running
gcc -o - -S -fno-asynchronous-unwind-tables test.c
with test.cThis output the assembly code:
This give you a sample of an assembly code calling printf that you can then modify.
Comparing with your code, you should modify 2 things:
mov $format, %rdi
mov $0, %eax
Applying these modifications will give something like :
And then running it print :
There are a number of issues with this code. The AMD64 System V ABI calling convention used by Linux requires a few things. It requires that just before a CALL that the stack be at least 16-byte (or 32-byte) aligned:
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 that AL contain the number of vector registers used for a variable argument function:
printf
is a variable argument function, so AL needs to be set. In this case you don't pass any parameters in a vector register so you can set AL to 0.You also dereference the $format pointer when it is already an address. So this is wrong:
This takes the address of format and places it 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:
This uses RIP Relative Addressing.
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:
Other Recommendations/Suggestions
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. Functionmain
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.data
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.
It is possible your executable is being compiled as position independent code. You may receive an error similar to:
To fix this you'll have to call the external function
printf
this way:This calls the external library function via the Procedure Linkage Table (PLT)