6

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.

1 Answers1

10

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.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Everything you've said makes perfect sense to me so thanks a ton for your explanation. The non optimized code was confusing me a bit since I'm still fairly new but I have a decent grasp on the concepts. As for the 16 bytes allocation, I could be wrong but I remember testing the sizeof(struc) in C and it returned 16. Then I tried the same struct with 3 ints and without the pointer and it was 12. Regardless, I was most confused about the 40 being subtracted from esp but I think it my be a stack alignment issue I just don't understand yet. –  Sep 06 '14 at 21:51