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!

+3


source to share


1 answer


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

      

+13


source







All Articles