gcc has a language extension attribute called __noreturn__ . In your <stdlib.h> you probably have an ad like:
extern void exit (int __status) __THROW __attribute__ ((__noreturn__));
This attribute is a promise that the function will never return in the usual way. (Perhaps it terminates the process, or maybe he longjmp s, or perhaps it generates C ++ exception or may have an infinite loop ...)
Thus, when compiling the code that calls exit , gcc can make some optimizations, for example, maybe not bother setting and / or clear the stack pointers so that you can return to the calling function.
For example, here is a simple function that calls a library function and then builds it on x86 without the -O flag.
void func1(void) { srand(1); } .globl func1 .type func1,@function func1: pushl %ebp movl %esp, %ebp subl $8, %esp subl $12, %esp pushl $1 call srand addl $16, %esp leave ret
And an almost identical function that calls exit :
void func2(void) { exit(0); } .globl func2 .type func2,@function func2: pushl %ebp movl %esp, %ebp subl $8, %esp subl $12, %esp pushl $0 call exit
It starts with the same thing, but then just skips the addl , leave and ret instructions that in func1 tell the processor how to return to executing any function called func1 .
When you covertly replace exit and break that promise, the instruction pointer may go somewhere that does not have reasonable code, or the stack structure may be invalid, etc. In the func2 function example, the function assembly actually ended when exit called, so when you return and the instruction pointer returns, it will indicate what data will then be in the program image, and not even the executable code at all.