Consider this simple piece of C code:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int main()
{
int var = 6;
int var2 = 5;
int var3 = add(var, var2);
return 0;
}
Compiling this code with gcc core.c -O0 -g yields this assembly code:
.file "core.c"
.text
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size add, .-add
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $6, -12(%rbp)
movl $5, -8(%rbp)
movl -8(%rbp), %edx
movl -12(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call add
movl %eax, -4(%rbp)
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
In gdb, I am setting a breakpoint when entering the add function. Taking a look at the stack in memory at this point ($rbp = 0x7fffffffdb10 and $rsp = 0x7fffffffdb10):
0x7fffffffdb50: 0xffffdc30 0x00000001 0xffffdc48 0x00007fff
0x7fffffffdb40: 0x00000000 0x00000000 0x55555141 0x00005555
0x7fffffffdb30: 0x00000001 0x00000000 0xf7c29d90 0x00007fff frame ptr, main RA
0x7fffffffdb20: 0x00001000 0x00000006 0x00000005 0x00005555 4096, var, var2, var3
rbp --> 0x7fffffffdb10: 0xffffdb30 0x00007fff 0x5555516a 0x00005555 main frame, add RA
0x7fffffffdb00: 0x00000002 0x00000000 0x00000005 0x00000006 (8 rand byte), var2, var
We can see several things:
- the current base pointer (rbp) points towards the main frame address.
- Above it is the return address (0x55555555516a) to the main function.
- Following this logic, the base pointer of the main frame (0x...db30) points towards 0x1 and the return address is 0x7ffff7c29d90.
- As main has 3 local variables, and it reserves 16 bytes (0x...db20 to 0x...db2c) and puts them there in reverse order.
- the add function pushes the values needed for calculation on stack without moving the stack pointer (0x...db08 and 0x...db0c).
To the questions:
Q1: Are the values of the return address and the base frame in main special values? At least the RA cannot be constant as there is no guarantee its free.
Q2: When memory for the local variables is allocated, why are 16 bytes allocated instead of the expected 12? (suspecting aligning?) And what is that does the 0x1000 in the extra byte mean?
Q3: When add calculates the result, why is the stack pointer not adjusted when it clearly puts data on the stack? Couldn't that lead to loss of information?
I would be grateful for any help, also pointers to resources would be appreciated.