Working on Linux, x86_64.
I'm learning assembly programming, and having a hard time finding appropriate documentation and discerning which documentation is applicable.
Using nasm
as my assembler, and ld
as my linker, I produced this program to exit the program with a code equal to the number of program arguments.
nasm -felf64 hello.s && ld hello.o && ./a.out "$@"
echo $?
section .text
global _start
_start:
mov rax, 60
syscall
This is based on the idea that the syscall takes the exit code from rdi
, and the argc
value also seems to be placed in rdi
, based on some experiments with Godbolt Compiler Explorer.
This doesn't work, always producing exit code 0
.
However, when I change _start
to main
and use gcc
instead of ld
, the program works.
nasm -felf64 hello.s && gcc -o a.out hello.o && ./a.out "$@"
echo $?
section .text
global main
main:
mov rax, 60
syscall
This produces an exit code equal to the number of shell arguments.
After much trial and error and wandering Google, I was finally able to get this working, but my problem is that I don't know why it works. I'm really struggling to know which documentation I need to be reading. Currently I'm just trying everything I can find and piecing together the bits that work.
CodePudding user response:
main
is a function that takes argc
as its first argument, so when main
is entered, the value of argc
will be in rdi
, as you expected. _start
is not a function, so registers and the stack will not be set up as they'd be if it were. According to the AMD64 System V ABI, _start
will instead find argc
on top of the stack (i.e., where the return address would be if it were a function). To make your program work with it, do mov rdi, [rsp]
before syscall
(it doesn't matter whether it's before or after mov rax, 60
).
When you use gcc
instead of ld
, code for _start
from glibc gets linked into your program that calls main
with the standard function calling convention, which is why it works then.