Disclaimer: This answer does not cite C. standards.
TL DR
Both methods go into GLibC code , and to know exactly what this code does or which one is faster or more efficient, you need to read them. If you want to know more about GLibC, you should check out the sources for GCC and GLibC. There are links at the end.
Syscalls, wrappers and GLibC
First: there is a difference between exit (3) and _ exit (2) . The first one is the GLibC shell , which is a call system. The one we use in our program also requires the inclusion of stdlib.h exit(3) - the GLibC shell, not a system call.
Now programs are not just your simple instructions. They contain heavy loads of GLibC native instructions. These GLibC functions perform several tasks related to loading and providing the library functions that you use. For this, GLibC must be "inside" your program.
So how is GLibC inside your program? Well, it is placed there through your compiler (it installs some static code and intercepts some in a dynamic library) - most likely you are using gcc .
'return 0;' Method
I suppose you know that the frames are stacked , so I wonβt explain what they are. The great thing to notice is that main() itself has its own stack stack. And this stack stack comes back somewhere , and it should come back ... But, to where ?
Let's compile the following:
int main(void) { return 0; }
And compile and debug it with
$ gcc -o main main.c $ gdb main (gdb) disass main Dump of assembler code for function main: 0x00000000004005e8 <+0>: push %rbp 0x00000000004005e9 <+1>: mov %rsp,%rbp 0x00000000004005ec <+4>: mov $0x0,%eax 0x00000000004005f1 <+9>: pop %rbp 0x00000000004005f2 <+10>: retq End of assembler dump. (gdb) break main (gdb) run Breakpoint 1, 0x00000000004005ec in main () (gdb) stepi ...
Now stepi will do for the fun part. This will say one command at a time, so it is perfect for making function calls. After you first press stepi , just keep your finger on ENTER until you get tired.
What you should observe is the sequence in which functions are called using this method. You see, ret is a jump instruction ( edit: after David Hoelzer , I see that calling ret simple jump is an over-generalization): after we pop rbp , ret itself put the return pointer from the stack and move to it. So, if GLibC built this stack frame, retq does our return 0; C directly into GLibC native code! How smart!
The order of function calls began like this:
__libc_start_main exit __run_exit_handlers _dl_fini rtld_lock_default_lock_recursive _dl_fini _dl_sort_fini
'exit (0);' Method
Compilation:
#include <stdlib.h> int main(void) { exit(0); }
And compiling and debugging ...
$ gcc -o exit exit.c $ gdb exit (gdb) disass main Dump of assembler code for function main: 0x0000000000400628 <+0>: push %rbp 0x0000000000400629 <+1>: mov %rsp,%rbp 0x000000000040062c <+4>: mov $0x0,%edi 0x0000000000400631 <+9>: callq 0x4004d0 < exit@plt > End of assembler dump. (gdb) break main (gdb) run Breakpoint 1, 0x000000000040062c in main () (gdb) stepi ...
And the resulting sequence of functions:
exit@plt ?? _dl_runtime_resolve _dl_fixup _dl_lookup_symbol_x do_lookup_x check_match _dl_name_match strcmp
List of objects Symbols
There is a cool tool for printing characters defined in binary format. This is nm . I suggest you look into it, as this will give you an idea of ββhow many βcrapβ has been added in a simple program like the one above.
To use it in its simplest form:
$ nm main $ nm exit
This will print a list of characters in the file. Please note that there are no links in this list that will perform these functions. Therefore, if a given function in this list calls another function, the other probably will not be in the list.
Conclusion
This depends heavily on how GLibC wants to handle the simple stack stack from main and how it implements the exit wrapper. As a result, the _exit(2) system call will be called, and you will exit the process.
Finally , to really answer your question: both methods go into GLibC code and know exactly what this code does, you need to read it. If you want to know more about GLibC, you should check out the sources for GCC and GLibC.
References
- GLibC Source Repository : Browse
stdlib/exit.c and stdlib/exit.h for implementations. - Defining Linux kernel output : see
kernel/exit.c for the _exit(2) system call and include/syscalls.h for the preprocessor magic behind it. - GCC Sources : I do not know the sources of
gcc (compiler, not set), and would appreciate if anyone could indicate where the execution sequence is defined.