2

So before we come to the main function, it looks we are in a function with a name __libc_start_main

and before we come to our main function we obviously push EBP value in the stack ( the value of EBP before main function, in the __libc_start_main function)

so first off is this value important? after the execution of main function, will the __libc_start_main function need this value?

is there any function that gets called before __libc_start_main therefore making this EBP value important?

i tested it and even if i overwrite this value(in the stack) with overflowing, the program finishes with no problem but i don't get how and why? shouldn't it result in error or page fault?

1
  • All general-purpose registers are zeroed by the Linux loader before the program starts executing, which gets passed to __libc_start_main I believe
    – user464014
    Commented Oct 10, 2018 at 5:39

1 Answer 1

3

__libc_start_main is called by the entry point code (usually in a file called crt0.S or similar) and that code usually sets up the initial EBP value (usually to 0, to denote end of the call stack for the debuggers). Here's a sample entry point code from a random ELF binary:

_start:
     xor     ebp, ebp
     pop     esi
     mov     ecx, esp
     and     esp, 0FFFFFFF0h
     push    eax
     push    esp             ; stack_end
     push    edx             ; rtld_fini
     push    offset __libc_csu_fini ; fini
     push    offset __libc_csu_init ; init
     push    ecx             ; ubp_av
     push    esi             ; argc
     push    offset main     ; main
     call    ___libc_start_main
     hlt

While __libc_start_main itself could initialize EBP to 0, this would make it appear as if it's the first code in the binary when looking at the call stack, which is not correct (the execution begins at _start, the entry point).

Besides, __libc_start_main can be (and often is) written in C instead of assembly, and it's easier for the compiler to generate the standard prolog code for it instead of making some kind of special case exception.

As for overwriting the value, you don't get a crash because:

  1. ebp is not actually used by the caller of ___libc_start_main (the entry point code)
  2. ___libc_start_main does not actually return, since it calls exit() with the return value of main() to directly exit to the OS, so your corrupted ebp does not matter

In the unlikely event that ___libc_start_main does return (e.g. due to a bug in libc), the entry point code will execute the hlt instruction which is privileged so the process will be killed by the OS.

3
  • One question, why in the wikipedia of crt0, in the code for x86_64 the entry code directly calls main instead of __libc_start_main? en.wikipedia.org/wiki/Crt0
    – John P
    Commented Oct 8, 2018 at 19:30
  • It depends on the specific compiler/toolchain/libc used. __libc_start_main can perform additional tasks before or after calling main, but in some cases you don’t really need them
    – Igor Skochinsky
    Commented Oct 8, 2018 at 19:59
  • so overall if we have _libc_start_main, the order of execution is crt0 -> _libc_start_main -> main, correct?
    – John P
    Commented Oct 8, 2018 at 20:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.