I tried to put code not in the main function, but directly into _start
:
segment .text
global _start
_start:
push rbp
mov rbp, rsp
; ... program logic ...
leave
ret
Compile:
yasm -f elf64 main.s
ld -o main main.o
Run:
./main
Segmentation fault(core dumped)
I read, leave is
mov esp,ebp
pop ebp
But why is it that such an epilogue to the pop stack frame and the set base frame pointer to a previous frame's base results in a segmentation fault?
Indeed, making an exit system call exits gracefully.
As per ABI1 the stack at the entry on _start
is
There is no "return address".
The only way to exit a process is through SYS_EXIT
xorl %edi, %edi ;Error code
movl $60, %eax ;SYS_EXIT
syscall
1 Section 3.4.1 Initial Stack and Register State.
The LEAVE
instruction is defined to not cause any exceptions, so it cannot be the source of your fault. You should be using GDB. Debuggers are invaluable in solving these sorts of problems.
This is what happens:
$ gdb ./main
[...]
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000001 in ?? ()
(gdb) x /gx $rsp-8
0x7fffffffe650: 0x0000000000000001
So, most likely your program ran to completion, but the first thing on the stack that 0x0000000000000001. RET
popped that into the RIP
register, and then it segfaulted because that address is not mapped.
I don't write a lot of code on Linux, but I would bet that _start
is required to use the exit system call. The only way you could possibly return to a useful address is if the kernel put a function somewhere that would do this for you.