Need help understanding the basic assembly code that was generated from C code
I am looking into Assembly and I am trying to understand how an assembly is generated from C code.
I created the following dummy C code:
#include <stdio.h>
int add(int x, int y){
int result = x + y;
return result;
}
int main(int argc, char *argv[]){
int x = 1 * 10;
int y = 2 * 5;
int firstArg = x + y;
int secondArg = firstArg / 2;
int value;
value = add(firstArg, secondArg);
return value;
}
And got the following assembly code
.file "first.c"
.text
.globl add
.type add, @function
add:
.LFB39:
.cfi_startproc
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
.cfi_endproc
.LFE39:
.size add, .-add
.globl main
.type main, @function
main:
.LFB40:
.cfi_startproc
movl $30, %eax
ret
.cfi_endproc
.LFE40:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
And I was really surprised, where did all these arithmetic operations basically disappear? I don't understand how we got $ 30 out of nowhere (well, I suppose this is the return value from the add function, but why I can't see any command that gets this value, in fact I see that the return value has already been pushed to eax in add function, so why would we put $ 30 in eax again?). Where are all local master vars declared? I created 5 of them on purpose to see how they are pushed onto the stack.
Can you also help me understand what it is. LFE39.LFB40: .LFB39: mean?
I'm ready to book, but that doesn't clarify this case for me. In fact, the book says that the whole function should start with initializing the stack:
pushl %ebp movl %esp, %ebp
Also, when the function ends, it should populate it with a pop command.
This is not the case in the above code. I don't see stack initialization.
Thank!
source to share
You are compiling with optimizations enabled. GCC was smart enough to do all this compile-time computation and replace all this useless code with a simple constant.
First of all, x
and y
will be replaced by their constant expressions:
int x = 10;
int y = 10;
Then the places where these variables are used will instead get their constant values:
int firstArg = 20;
int secondArg = 10;
Then your function add
is small and trivial, so it will certainly be injected:
value = firstArg + secondArg;
Now these are constants too, so this will all be replaced with:
int main(int argc, char *argv[]) {
return 30;
}
While most functions will have a prologue, as you have shown, your program does nothing but return 30. More specifically, it no longer uses local variables or calls other functions. Because of this, main
no frame or reserved space in the call stack is required. So there is no need for the compiler to emit the prologue / epilogue.
main:
movl $30, %eax
ret
These are just two instructions that the program runs (apart from the C-runtime startup code).
Note that since your function add
was unlabeled static
, the compiler must have assumed that someone outside might call it. For this reason, we can still see it add
in the generated assembly, although no one calls it:
add:
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
source to share