I'm exploring some disassembled C programs with godbolt and I am having trouble wrapping my head around the x86_64 "call" instruction.
With the C code:
int func(int i)
{
return i + 1;
}
int main(void)
{
int i = 5;
i = func(i);
return 0;
}
I get the equivalent assembly code:
func:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, DWORD PTR [rbp-4]
add eax, 1
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 5
mov eax, DWORD PTR [rbp-4]
mov edi, eax
call func
mov DWORD PTR [rbp-4], eax
mov eax, 0
leave
ret
I have read that when I do call func
the return address is pushed onto the stack. Because this is 64 bit assembly I would assume that this address would be 64 bits wide (i.e 8 bytes). However when investigating the assembly for func
the line mov DWORD PTR [rbp-4], edi
seems to imply that there are only 4 bytes for the return address as [rbp-4]
would be 4 bytes past the base pointer of the stack frame. I have a feeling I am fundamentally misunderstanding what is going on here and I was hoping to get some guidance.
I expected mov DWORD PTR [rbp-8], edi
but was given mov DWORD PTR [rbp-4], edi
I'm exploring some disassembled C programs with godbolt and I am having trouble wrapping my head around the x86_64 "call" instruction.
With the C code:
int func(int i)
{
return i + 1;
}
int main(void)
{
int i = 5;
i = func(i);
return 0;
}
I get the equivalent assembly code:
func:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, DWORD PTR [rbp-4]
add eax, 1
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 5
mov eax, DWORD PTR [rbp-4]
mov edi, eax
call func
mov DWORD PTR [rbp-4], eax
mov eax, 0
leave
ret
I have read that when I do call func
the return address is pushed onto the stack. Because this is 64 bit assembly I would assume that this address would be 64 bits wide (i.e 8 bytes). However when investigating the assembly for func
the line mov DWORD PTR [rbp-4], edi
seems to imply that there are only 4 bytes for the return address as [rbp-4]
would be 4 bytes past the base pointer of the stack frame. I have a feeling I am fundamentally misunderstanding what is going on here and I was hoping to get some guidance.
I expected mov DWORD PTR [rbp-8], edi
but was given mov DWORD PTR [rbp-4], edi
- 1 In the used system sizeof( int ) is equal to 4. So in this statement mov DWORD PTR [rbp-4], edi there is reserved 4 bytes for the parameter of the type int passed to the function through the register edi. – Vlad from Moscow Commented yesterday
- @VladfromMoscow, That makes perfect sense I'm not too sure what I was thinking. – BatterySoda Commented yesterday
- 1 The return address is above the base pointer, not below. The stack grows downwards. – BoP Commented yesterday
1 Answer
Reset to default 2However when investigating the assembly for func the line
mov DWORD PTR [rbp-4], edi
seems to imply that there are only 4 bytes for the return address as[rbp-4]
would be 4 bytes past the base pointer of the stack frame.
No. That mov
instruction has nothing to do with the return address. It is about argument passing. In particular, it copies the value passed to parameter i
into the func
's stack frame.
I have a feeling I am fundamentally misunderstanding what is going on here and I was hoping to get some guidance.
Yes. The call
instruction pushes the return address onto the stack, including updating the stack pointer. When func
executes mov rbp, rsp
, that sets its frame pointer past the return address, regardless of the size of that address (and also past the stored value of the caller's frame pointer, which func
pushed on top). After that, [rbp-4]
is the address of the first 4-byte word in func
's stack frame.