0

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.

2
  • 2
    _start is not called by the kernel, you cannot return from it. Commented Sep 18, 2016 at 6:48
  • Could you please elaborate more? I thought that it was ordinary routine. How should one properly "return/exit" from it? Via exit system call? Write as an answer. Commented Sep 18, 2016 at 6:56

2 Answers 2

6

As per ABI1 the stack at the entry on _start is

Stack at entry on _start

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.

Sign up to request clarification or add additional context in comments.

1 Comment

That answers my question.
4

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.

6 Comments

Yes, before asking question I used gdb and show 6 leave (gdb) n _start() at main.s:7 7 ret (gdb) n 0x0000000000000001 in ?? () Did not post because do not know what "??" means.
I also think about setting rip to unmapped location in memory, however, it would be nice to know accurately.
It means that the address is not in a valid module. Since GDB doesn't know what module it belongs to, it prints "??". The key thing to notice here is that 0x0000000000000001 is not a valid, executable address. When RIP points to an invalid or non-executable address, it is usually caused by either a RET to a bad address or an indirect JMP or CALL to a bad address. If a RET, as is the case here, the 8 bytes below the top of stack will match RIP. If a JMP or CALL, sometimes a register will match RIP.
Indeed you'd expect the first thing on the stack would be the value 1 as this is where Linux passes the argc value and a program invoked without any arguments has an argc of 1.
Useful tip about arguments. So to get argc and argv one should use ebp + 8 and ebp + 16 stack offsets respectively?
|