Note, this question already has similar answers here, which I want to point out to:
However this question is asking more about the return formats of them and how they relate to each other (which I don't think is entirely covered in the above questions).
What are the differences between _start
and main
? It seems to me like ld
uses _start
, but that gcc
uses main
as the entry point. The other difference that I've noticed is that main
seems to return the value in %rax
, whereas _start
returns the value in %rbx
The following is an example of the two ways I'm seeing this:
.globl _start
_start:
mov $1, %rax
mov $2, %rbx
int $0x80
And to run it:
$ as script.s -o script.o; ld script.o -o script; ./script; echo $?
# 2
And the other way:
.globl main
main:
mov $3, %rax
ret
And to run it:
$ gcc script.s -o script; ./script; echo $?
3
What is the difference between these two methods? Does main
automatically invoke _start
somewhere, or how do they relate to each other? Why does one return their value in rbx
whereas the other one returns it in rax
?
TL:DR: function return values and system-call arguments use separate registers because they're completely unrelated.
When you compile with gcc
, it links CRT startup code that defines a _start
. That _start
(indirectly) calls main
, and passes main
's return value (which main leaves in EAX) to the exit()
library function. (Which eventually makes an exit system call, after doing any necessary libc cleanup like flushing stdio buffers.)
See also Return vs Exit from main function in C - this is exactly analogous to what you're doing, except you're using _exit()
which bypasses libc cleanup, instead of exit()
. Syscall implementation of exit()
An int $0x80
system call takes its argument in EBX, as per the 32-bit system-call ABI (which you shouldn't be using in 64-bit code). It's not a return value from a function, it's the process exit status. See Hello, world in assembly language with Linux system calls? for more about system calls.
Note that _start
is not a function; it can't return in that sense because there's no return address on the stack. You're taking a casual description like "return to the OS" and conflating that with a function's "return value". You can call exit
from main
if you want, but you can't ret
from _start
.
EAX is the return-value register for int
-sized values in the function-calling convention. (The high 32 bits of RAX are ignored because main
returns int
. But also, $?
exit status can only get the low 8 bits of the value passed to exit()
.)
Related:
syscall
, and shows some of the kernel side of what happens inside the kernel after a system call.