0

I am studying asm language by compiling c source code into asm langugae. I ready known the call instruction is equivalent of

push ret_address
jmp my_func

But it seem that calling a main function is quite different from calling a noraml function. For example:

// c source code. ret is a normal function.
long ret(long i)
{
    return i++;
}

int main()
{
    long i=0;
    i = ret(i);
    return i;
}

After compiled into asm, the asm code for main and ret function are:

Dump of assembler code for function main():
   0x0000000100401096 <+0>: push   %rbp
   0x0000000100401097 <+1>: mov    %rsp,%rbp
   0x000000010040109a <+4>: sub    $0x30,%rsp
   0x000000010040109e <+8>: callq  0x100401160 <__main>
=> 0x00000001004010a3 <+13>:    movq   $0x0,-0x8(%rbp)
   0x00000001004010ab <+21>:    mov    -0x8(%rbp),%rax
   0x00000001004010af <+25>:    mov    %rax,%rcx
   0x00000001004010b2 <+28>:    callq  0x100401080 <ret(long)>
   0x00000001004010b7 <+33>:    mov    %rax,-0x8(%rbp)
   0x00000001004010bb <+37>:    mov    -0x8(%rbp),%rax
   0x00000001004010bf <+41>:    add    $0x30,%rsp
   0x00000001004010c3 <+45>:    pop    %rbp
   0x00000001004010c4 <+46>:    retq   
Dump of assembler code for function ret(long):
   0x0000000100401080 <+0>: push   %rbp
   0x0000000100401081 <+1>: mov    %rsp,%rbp
   0x0000000100401084 <+4>: mov    %rcx,0x10(%rbp)
=> 0x0000000100401088 <+8>: mov    0x10(%rbp),%rax
   0x000000010040108c <+12>:    lea    0x1(%rax),%rdx
   0x0000000100401090 <+16>:    mov    %rdx,0x10(%rbp)
   0x0000000100401094 <+20>:    pop    %rbp
   0x0000000100401095 <+21>:    retq  

When calling main(), three instructions push %rbp, mov %rsp,%rbp,sub $0x30,%rsp has already be executed. Thus the call instruction will push the previous instruction stored in rip register into the position (-$0x30 + %rsp), as sub $0x30,%rsp has already exectuted. This is not how function calling stack works.

When calling ret(), these three instructions are executed after call instruction, which is quite normal.

So is there something special for calling main()? Why the asm code behaviors differently?

Machi
  • 403
  • 2
  • 14
  • 2
    You're looking at the code that implements main, not the code for *calling* main (in CRT startup). Also, you're looking at where the debug info (generated by the compiler) tells GDB to set a breakpoint after the prologue, which for `main` in MinGW and/or cygwin includes calling an init function. It's nothing special about *calling* main, it's that MinGW makes `main` itself special. If you actually set a breakpoint on the symbol address, like `b *0x0000000100401080`, GDB would stop there, and you could single-step instructions with `stepi` (`si`) or `nexti` (`ni`). – Peter Cordes Jan 28 '22 at 05:49
  • 2
    Also, `ret()` seems a poor choice of name for experiments; it's easy to confuse with a call that goes directly a a `ret` instruction (like you'd get if you compiled with optimization, for an empty function other than `main`: https://godbolt.org/z/8j8WrMjY5) – Peter Cordes Jan 28 '22 at 06:16
  • @PeterCordes I almost get your point. The webstie is very helpful. Thanks. – Machi Jan 28 '22 at 07:44

0 Answers0