0

I'm trying to understand how a function call works in assembly language. My understanding is, after calling a function, the callee function first uses a function prologue to create a new stack frame for itself.

Function prologue:

push   %rbp
mov    %rsp,%rbp

However, I have the following example code,

void printNumber(int nbr){
   printf("%d\n", nbr);
}

void myFunction(void (*f)(int)){
   for(int i = 0; i < 5; i++){
      (*f)(i);
   }
}

int main(void){
   myFunction(printNumber);
   return (0); 
}

Disassembly of all the function is :

>>> disass main
Dump of assembler code for function main:
0x000000000040057b <+0>:    sub    $0x8,%rsp
0x000000000040057f <+4>:    mov    $0x400526,%edi
0x0000000000400584 <+9>:    callq  0x400549 <myFunction>
0x0000000000400589 <+14>:   mov    $0x0,%eax
0x000000000040058e <+19>:   add    $0x8,%rsp
0x0000000000400592 <+23>:   retq   
End of assembler dump.

>>> disass myFunction
Dump of assembler code for function myFunction:
0x0000000000400549 <+0>:    sub    $0x28,%rsp
0x000000000040054d <+4>:    mov    %rdi,0x8(%rsp)
0x0000000000400552 <+9>:    movl   $0x0,0x1c(%rsp)
0x000000000040055a <+17>:   jmp    0x40056e <myFunction+37>
0x000000000040055c <+19>:   mov    0x1c(%rsp),%edx
0x0000000000400560 <+23>:   mov    0x8(%rsp),%rax
0x0000000000400565 <+28>:   mov    %edx,%edi
0x0000000000400567 <+30>:   callq  *%rax
0x0000000000400569 <+32>:   addl   $0x1,0x1c(%rsp)
0x000000000040056e <+37>:   cmpl   $0x4,0x1c(%rsp)
0x0000000000400573 <+42>:   jle    0x40055c <myFunction+19>
0x0000000000400575 <+44>:   nop
0x0000000000400576 <+45>:   add    $0x28,%rsp
0x000000000040057a <+49>:   retq   
End of assembler dump.

>>> disass printNumber
Dump of assembler code for function printNumber:
0x0000000000400526 <+0>:    sub    $0x18,%rsp
0x000000000040052a <+4>:    mov    %edi,0xc(%rsp)
0x000000000040052e <+8>:    mov    0xc(%rsp),%eax
0x0000000000400532 <+12>:   mov    %eax,%esi
0x0000000000400534 <+14>:   mov    $0x400624,%edi
0x0000000000400539 <+19>:   mov    $0x0,%eax
0x000000000040053e <+24>:   callq  0x400400 <printf@plt>
0x0000000000400543 <+29>:   nop
0x0000000000400544 <+30>:   add    $0x18,%rsp
0x0000000000400548 <+34>:   retq   
End of assembler dump.

None of the functions disassemblies shows function prologue. This brings to my questions,

  1. Why function prologue is not showing in function disassembly?
  2. In all the function disassemblies, my understanding is sub instruction is allocating some space in the stack for local variables and sub-routine.
    • In main function, It only has one subroutine myFunction(printNumber);. Therefore, I guess sub $0x8,%rsp is being used to allocate 8-byte stack memory for sub-routine call.
    • But, in other two functions, why allocating more stack memory? For example, function printNumber took one int type argument. Then what allocating 24-bytes in the stack?

Update: I have compiled with following,

gcc -O0 -g -Wall -Wextra -pedantic -fomit-frame-pointer -o function_as_paprameter function_as_paprameter.c
user45698746
  • 305
  • 2
  • 13
  • 1
    The prologue can be omitted as an optimization. This is the `-fomit-frame-pointer` option which is often enabled by default. Local variables can be accessed relative to `%rsp` instead of `%rbp` as you see, which makes it unnecessary to set up the stack frame with `%rbp` at all. – Nate Eldredge Jan 19 '21 at 22:57
  • @NateEldredge. Thank you. Now I can see the prologue. – user45698746 Jan 19 '21 at 22:59
  • 2
    The only prologue needed is the `sub $0x28,%rsp` to reserve space for locals. The instructions involving RBP you were expecting to see *are part* of the prologue if they're present at all, but are optional in modern calling conventions / ABIs. It's not correct to say that `push %rbp` / `mov %rsp,%rbp` *is* the function prologue. – Peter Cordes Jan 19 '21 at 23:00
  • Did you compile this with `-O1` or `-Og`? The loop in `myFunction` is strangely keeping `i` in memory, and reloading the function pointer arg as well. Include your compile options as part of a [mcve]. (And if you want, link to the code on https://godbolt.org/ with those options.) – Peter Cordes Jan 19 '21 at 23:04
  • @PeterCordes I use `-O0 -g -Wall -Wextra -pedantic` for compilation. – user45698746 Jan 19 '21 at 23:05
  • On what system, with what GCC version? (`gcc -v`) A normal config of GCC uses `-fno-omit-frame-pointer` at `-O0`, and would reference stack locals relative to RBP (after setting up RBP as a frame pointer at the top of each function like you were expecting). But yeah that does otherwise look like `-O0` code, spilling incoming register args to memory and never keeping a var in regs across C statements. – Peter Cordes Jan 19 '21 at 23:11
  • @PeterCordes, I'm running 64-bit Linux 16.04 LTS with kernel 4.4.0-200-generic. removing `-fomit-frame-pointer` from the compiling options actually let me see the desire prologue. – user45698746 Jan 19 '21 at 23:19
  • Oh, so you were using `-fomit-frame-pointer` explicitly, along with `-O0`, but you didn't include that in your comment (only in your question edit which I didn't examine closely). Ok, that totally explains everything, with that option doing exactly what it says on the tin. – Peter Cordes Jan 19 '21 at 23:27

0 Answers0