I am currently learning the basics of assembly and came across something odd when looking at the instructions generated by GCC(6.1.1).
Here is the source:
#include <stdio.h>
int foo(int x, int y){
return x*y;
}
int main(){
int a = 5;
int b = foo(a, 0xF00D);
printf("0x%X\n", b);
return 0;
}
Command used to compile: gcc -m32 -g test.c -o test
When examining the functions in GDB I get this:
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080483f7 <+0>: lea ecx,[esp+0x4]
0x080483fb <+4>: and esp,0xfffffff0
0x080483fe <+7>: push DWORD PTR [ecx-0x4]
0x08048401 <+10>: push ebp
0x08048402 <+11>: mov ebp,esp
0x08048404 <+13>: push ecx
0x08048405 <+14>: sub esp,0x14
0x08048408 <+17>: mov DWORD PTR [ebp-0xc],0x5
0x0804840f <+24>: push 0xf00d
0x08048414 <+29>: push DWORD PTR [ebp-0xc]
0x08048417 <+32>: call 0x80483eb <foo>
0x0804841c <+37>: add esp,0x8
0x0804841f <+40>: mov DWORD PTR [ebp-0x10],eax
0x08048422 <+43>: sub esp,0x8
0x08048425 <+46>: push DWORD PTR [ebp-0x10]
0x08048428 <+49>: push 0x80484d0
0x0804842d <+54>: call 0x80482c0 <printf@plt>
0x08048432 <+59>: add esp,0x10
0x08048435 <+62>: mov eax,0x0
0x0804843a <+67>: mov ecx,DWORD PTR [ebp-0x4]
0x0804843d <+70>: leave
0x0804843e <+71>: lea esp,[ecx-0x4]
0x08048441 <+74>: ret
End of assembler dump.
(gdb) disas foo
Dump of assembler code for function foo:
0x080483eb <+0>: push ebp
0x080483ec <+1>: mov ebp,esp
0x080483ee <+3>: mov eax,DWORD PTR [ebp+0x8]
0x080483f1 <+6>: imul eax,DWORD PTR [ebp+0xc]
0x080483f5 <+10>: pop ebp
0x080483f6 <+11>: ret
End of assembler dump.
The part that confuses me is what it is trying to do with the stack. From my understanding this is what it does:
- It takes a reference to some memory address 4 bytes higher in the stack which from my knowledge should be the variables passed to main since
esp
currently pointed to the return address in memory. - It aligns the stack to a 0 boundary for performance reasons.
- It pushes onto the new stack area
ecx+4
which should translate to pushing the address we are suppose to be returning to on the stack. - It pushes the old frame pointer onto the stack and sets up the new one.
- It pushes
ecx
(which is still pointing to would should be an argument tomain
) onto the stack.
Then the program does what it should and begins the process of returning:
- It restores
ecx
by using a-0x4
offset onebp
which should access the first local variable. - It executes the leave instruction which really just sets
esp
toebp
and then popsebp
from the stack.
So now the next thing on the stack is the return address and the esp and ebp registers should be back to what they need to be to return right?
Well evidently not because the next thing it does is load esp
with ecx-0x4
which since ecx
is still pointing to that variable passed to main
should put it at the address of return address on the stack.
This works just fine but raises the question: why did it bother to put the return address onto the stack in step 3 since it returned the stack to the original position at the end just before actually returning from the function?
gcc -m32 -O -Wall -S -fverbose-asm test.c
then look inside the generatedtest.s
– Jehannamain
. – Illicitebp
like a normal stack frame make sense? – Jett