Search code examples
cassemblyx86mallocatt

struct allocation in x86 assembly


So I'm trying to write some x86 to allocate memory for a struct. My c code looks like this...

   struc *uno = malloc(sizeof(struc));
   uno->first = 0;
   uno->second = 0;
   uno->third = 0;

   //And the struct
   struct struc {
       int first;
       int second;
       int *third;
   }

And the disassemble looks like...

    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $12, (%esp)
    call    malloc
    movl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    movl    $0, (%eax)
    movl    -12(%ebp), %eax
    movl    $0, 4(%eax)
    movl    -12(%ebp), %eax
    movl    $0, 8(%eax)
    movl    $0, %eax

So I have a few questions...

1) The size of the struct is 16 but why does the assembly only shows it allocating 12?

2) What is the meaning for the

    movl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax

Isn't is just putting the contents of eax into the address of ebp - 12. Then the second statement would be redundant?

3) Why is the esp being decremented by 40 when there are no other local variables or parameters to be pushed on the stack? I would've thought it only needs to be decremented 16.

Any help is appreciated, as well as anything I may have missed that you deem relevant. I'm pretty new to assembly. Thanks.


Solution

  • The struct has size 12. The two integers, and the pointer, are all 4 bytes on x86, so there's no padding and the struct has size 12. You can always ask the compiler by using sizeof if you are unsure of the size of a type.


    You are compiling without optimization and so the code seems a little inefficient.

    movl    $12, (%esp)
    call    malloc
    movl    %eax, -12(%ebp)
    

    This calls malloc, and then saves the value that was return in %eax to the local variable uno, stored at -12(%ebp). So, these three instructions make up the call to malloc: prepare the stack, call the function, save the return value.

    We move on to the next line.

    uno->first = 0;
    

    Because there is no optimisation, the compiler does not realise that %eax already contains the value of uno and so it loads it up again, and then writes zero into the first member

    movl    -12(%ebp), %eax
    movl    $0, (%eax)
    

    Next up is

    uno->second = 0;
    

    And again we load the value of uno into %eax, and write a zero, this time into the second member at offset 4

    movl    -12(%ebp), %eax
    movl    $0, 4(%eax)
    

    You get the idea, and I'm sure I don't need to explain the final assignment.

    Try compiling with optimisation and the output will look very different. The compiler should be able to optimise uno into %eax and not put it on the stack at all. It could produce this code:

    movl    $12, (%esp)
    call    malloc
    movl    $0, (%eax)
    movl    $0, 4(%eax)
    movl    $0, 8(%eax)
    

    I'm not really sure why you think that the stack reserve should be 16 bytes. I only see a need for 4 bytes, the argument passed to malloc, in the optimised variant above. And without optimisation it would be 8 I guess, 4 for the argument, and 4 for the local variable. But I've no idea why the function reserves 40 bytes for the stack. Perhaps the answer can be found in one of these questions:

    Finally, it might be more productive for you to look at optimised code. Without optimisation the compiler may take many decisions that seem odd. The code will be more concise when optimised and in the long run, the code that you execute for real is likely optimised.