0

I am learning x86 assembly. I am trying to understand how "exiting program" works on x86. We have a code :

push ebp
mov ebp,esp
//Some stuff here
mov esp, ebp
pop ebp
ret

When processor executes instruction "ret" :

EIP will have value, which is popped from stack, in other words 0. so processor will go to 0 address and will try to execute instructions ... which doesn't contain program code/executable code. So, what is really going on with processor? Are there condition check, for example, if EIP = 0 -> exit program? Or if ESP out of bounds -> exit program? `How processor understands that this RET instruction is the end of the program?

duplode
  • 33,731
  • 7
  • 79
  • 150
user3719859
  • 25
  • 1
  • 1
  • 5
  • 7
    The notion of "program" and "exiting" are only meaningful in the context of an *operating system*. Consult your operating system's manual on how to access those concepts programmatically. (On a typical desktop OS, there would be system calls for creating and exiting a process.) – Kerrek SB Sep 05 '15 at 16:34
  • 2
    ".. popped from stack, in other words 0." How did you come to this conclusion? Is your program called "from address 0"? – Jongware Sep 05 '15 at 23:13

2 Answers2

8

main() is called from the normal C runtime initialization functions. Writing main in any language, including asm, is no different from writing any other function.

Execution begins at _start. If you write your own _start, it has nothing to return to, so you need to make an _exit(2) or exit_group(2) system call.

(Or else segfault when execution falls off the end of your code, or if you try to ret it will pop a value off the stack into the program counter (EIP), and probably segfault on code-fetch from that probably-invalid address.)

When you compile + link with a C compiler, it links in CRT (C RunTime) startup code that provides a _start which initializes libc then calls main. After your main returns, the CRT code that called it runs atexit functions and then passes main's return value to an exit system call.

_start isn't a function, it's the process entry point. Under Linux for example, on entry to _start ESP points at argc, not a return address. (See the i386 System V ABI.)


This question comes at the question from a different angle, but my answer to another recent question goes into more detail.

As always, single-stepping with a debugger is a good way to see what's going on and test your understanding.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
3

The process gets terminated by the operating system. If you see a return instruction there is a ("hidden") function before your function, which is then given back execution.

One way of terminating the process on a Linux (32-bit) environment is by calling a specific system-call: sys_exit

mov     eax, 0 // interrupt code
mov     ebx, 0 // argument, in this case: return value
int     0x80

This is probably the thing the function before your function does. Above code hands command back to the operating system, which then quits your process.

qwertz
  • 14,614
  • 10
  • 34
  • 46
  • 5
    `eax=0` / `int 0x80` is `sys_restart_syscall`. **You want `mov eax, 1` for `sys_exit` in the 32-bit `int 0x80` ABI**, or `eax=60` in the x86-64 `syscall` ABI. (or `eax=231` / `syscall` for `sys_exit_group`, to exit all threads, [like libc's `exit(3)` uses](https://stackoverflow.com/questions/46903180/syscall-implementation-of-exit) after flushing stdio buffers and other cleanup.) And of course `xor ebx,ebx` is the recommended way to zero a register. – Peter Cordes Feb 15 '18 at 11:06