I am diving into x86_64 assembly on macOS 10.12.
I am trying to call libc exit()
function:
.section __TEXT,__text
.globl _main
_main:
pushq %rbp
movq %rsp, %rbp
movl $5, %edi
callq _exit
And compile it with:
as exit2.s -o exit2.o
ld exit2.o -e _main -lc -o exit
The result is:
Segmentation fault: 11
Why is it so?
Edit: the issue is in linking libc and calling conventions.
@fuz is almost certainly correct: you crash because you didn't initialize libc. There's probably a NULL pointer somewhere in the data structures that exit(3)
checks before actually exiting. e.g. it flushes stdout
if needed, and it runs any functions registered with atexit(3)
.
If you don't want it to do all that work, then either make the sys_exit
system call directly with a syscall
instruction, or call
the thin _exit(2)
libc wrapper function for it. (The basics of the situation will be the same as on Linux, because exit(3)
vs. _exit(2)
are standardized by POSIX: see Syscall implementation of exit().
I think the tutorial you're following mostly looks good, but perhaps some older version of OS X allowed libc functions (including printf
?!?) to be used without calling any libc init functions. Or else they didn't test their code after an edit to the build commands. (Assuming they tested at all, maybe it was with dynamic linking, which would work.)
OS X prefixes symbol names in assembly with an _
, so use call __exit
(two underscores) to call _exit()
. (e.g. call _printf
calls the C printf
function).
_exit(2)
probably won't crash if you call it without initializing libc, but it's still a bad idea to call any libc functions without having called libc init functions first. Better to make the system call directly (see later in the tutorial), or even better, build it with gcc hello_asm.S -o hello_asm
to make sure libc is initialized. Then you can follow the rest of the tutorial, including the printf
.
Don't call your Mach-O entry point _main
or main
in a static executable. CRT startup code hasn't run yet. The usual convention is to call it _start
for the process entry point.
(Note that OS X puts the CRT start code in the dynamic linker, so the "entry point" in a dynamically-linked executable is the C main
function, unlike in Linux where dynamic executables can avoid the CRT startup code.
libc would be initialized for you if you linked with gcc exit2.o -o exit
instead of ld
, which you're using to do the equivalent of gcc -static -nostartfiles
.)