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,
- Why
function prologueis not showing in function disassembly? - In all the function disassemblies, my understanding is
subinstruction 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 guesssub $0x8,%rspis 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
printNumbertook oneinttype argument. Then what allocating24-bytesin the stack?
- In main function, It only has one subroutine
Update: I have compiled with following,
gcc -O0 -g -Wall -Wextra -pedantic -fomit-frame-pointer -o function_as_paprameter function_as_paprameter.c
-fomit-frame-pointeroption which is often enabled by default. Local variables can be accessed relative to%rspinstead of%rbpas you see, which makes it unnecessary to set up the stack frame with%rbpat all.sub $0x28,%rspto 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 thatpush %rbp/mov %rsp,%rbpis the function prologue.-O1or-Og? The loop inmyFunctionis strangely keepingiin memory, and reloading the function pointer arg as well. Include your compile options as part of a minimal reproducible example. (And if you want, link to the code on godbolt.org with those options.)-O0 -g -Wall -Wextra -pedanticfor compilation.