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.
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.