Finding main from __libc_start_main

I’m working on reversing a stripped binary and currently trying to find the location of main. I dropped into the entry point and printed the first 15 instructions from the $rip and found this:

=> 0x4006a0:    xor    ebp,ebp
   0x4006a2:    mov    r9,rdx
   0x4006a5:    pop    rsi
   0x4006a6:    mov    rdx,rsp
   0x4006a9:    and    rsp,0xfffffffffffffff0
   0x4006ad:    push   rax
   0x4006ae:    push   rsp
   0x4006af:    mov    r8,0x400a50
   0x4006b6:    mov    rcx,0x4009e0
   0x4006bd:    mov    rdi,0x40085d
   0x4006c4:    call   0x400610 <__libc_start_main@plt>
   0x4006c9:    hlt    
   0x4006ca:    nop    WORD PTR [rax+rax*1+0x0]
   0x4006d0:    mov    eax,0x60107f
   0x4006d5:    push   rbp

Great, yay we see the call to libc_main_start. Reading this article here: Working With Stripped Binaries in GDB | by Faisal | Medium it seems so suggest the address for main is always specified in the instruction directly above the function call.

Why is that? Should it not be within the function call, and if it is then where is it? Stepping into the call shows:

→   0x4006c4                  call   0x400610 <__libc_start_main@plt>                                                      
   ↳    0x400610 <__libc_start_main@plt+0> jmp    QWORD PTR [rip+0x200a12]        # 0x601028 <__libc_start_main@got.plt>    
        0x400616 <__libc_start_main@plt+6> push   0x2                                                                       
        0x40061b <__libc_start_main@plt+11> jmp    0x4005e0                                                                 
        0x400620 <srand@plt+0>    jmp    QWORD PTR [rip+0x200a0a]        # 0x601030 <srand@got.plt>                         
        0x400626 <srand@plt+6>    push   0x3                  
        0x40062b <srand@plt+11>   jmp    0x4005e0 

With no mention to the address at $rdi

As discussed, I am not sure I am the best person to answer this - hopefully bumping it will draw a bit more attention though.

So just to check, rdi normally contains the first argument - have I remembered that correctly?

Why would the address in rdi be in main?

Type your comment> @TazWake said:

As discussed, I am not sure I am the best person to answer this - hopefully bumping it will draw a bit more attention though.

So just to check, rdi normally contains the first argument - have I remembered that correctly?

Why would the address in rdi be in main?

Okay so the address for main is being stored into $rdi. Setting 0x40085d as a breakpoint and continuing drops you into main.

Edit: ohh so rdi contains the first arg? I didnt know that but it would make sense and answer my question. Thanks!

@LMAY75 said:

@TazWake said:

As discussed, I am not sure I am the best person to answer this - hopefully bumping it will draw a bit more attention though.

So just to check, rdi normally contains the first argument - have I remembered that correctly?

Why would the address in rdi be in main?

Okay so the address for main is being stored into $rdi. Setting 0x40085d as a breakpoint and continuing drops you into main.

Edit: ohh so rdi contains the first arg? I didnt know that but it would make sense and answer my question. Thanks!

Indeed. The calling convention (aka ABI) on x64 Linux is as follows (for “integer arguments”):

function($rdi, $rsi, $rdx, $rcx, $r8, $r9)

In cases where you need more parameters, they will be pushed to the stack.

And __libc_start_main is defined as:

int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end));

Type your comment> @HomeSen said:

@LMAY75 said:

(Quote)
Indeed. The calling convention (aka ABI) on x64 Linux is as follows (for “integer arguments”):

function($rdi, $rsi, $rdx, $rcx, $r8, $r9)

In cases where you need more parameters, they will be pushed to the stack.

And __libc_start_main is defined as:

int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (rtld_fini) (void), void ( stack_end));

Fantastic, thanks this helped a lot!