So I am currently learning about stack frames, and I wanted to experiment printing the stack frame (manually) of a function.
I have the following picture in mind of a stack frame (I may be wrong):
| | 0xffff0fdc
+--------------------------------+
| ... | 0xffff0fd8
+--------------------------------+
| parameter 2 | 0xffff0fd4
+--------------------------------+
| parameter 1 | 0xffff0fd0
+--------------------------------+
| return address | 0xffff0fcc
+--------------------------------+
| local variable 2 | 0xffff0fc8
+--------------------------------+
| local variable 1 | 0xffff0fc4
+--------------------------------+
thus I first wrote this function to achieve the above result and print it :
void func(int a,int b)
{
uint64_t loc = 0;
uint64_t *sp = &loc;
printf("%" PRIu64 "\n",*(sp));
printf("%" PRIu64 "\n",*(sp+4));
printf("%" PRIu64 "\n",*(sp+8));
printf("%" PRIu64 "\n",*(sp+12));
}
int main()
{
func(2,3);
return 0;
}
and I get :
0
12884901890
51266344552759297
18034967110614932
definitely not what was expected
I also tried "scanning" through the stack to find one of the argument :
while (*sp != a) sp++
without much success. What is wrong with my method ?
I also had another question : Given a recursive function, take simply factorial(int n), how could we spot the address where the base pointer is located in the stack ?
in case you need the assembly code : Note that this only contains the function "func" generated assembly code. I added comments to where the assembly code relates to the source code.
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
***// uint64_t loc = 0;***
movq $0, -16(%rbp)
***// uint64_t *sp = &loc;***
leaq -16(%rbp), %rax
movq %rax, -8(%rbp)
***// printf("%" PRIu64 "\n",*sp);***
movq -8(%rbp), %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***printf("%" PRIu64 "\n",*(sp+8));***
movq -8(%rbp), %rax
addq $64, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***// printf("%" PRIu64 "\n",*(sp+16));***
movq -8(%rbp), %rax
subq $-128, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***// printf("%" PRIu64 "\n",*(sp+32));***
movq -8(%rbp), %rax
addq $256, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
Any advices to help me undertand the stack better would be greatly appreciated!
PS : I am not allowed to use any external functions
x86-64 does not pass the first few arguments on the stack (type permitting) so you have no chance to print those. Also, the actual stack layout for the locals depends on compiler and settings.
Since you have provided the assembly code, we can examine the layout which looks like this:
Also note that
a
andb
are 4 bytes, the rest are 8. Furthermore, C pointer arithmetic scales by item size, so*(sp+4)
goes4 * 8 = 32
bytes not4
as you probably intended.If the stack layout is unchanged, you can use this code as illustration:
Sample output: