Jan Koester
Jan Koester

Reputation: 1218

C allocated space size on stack for an array

I have a simple program called demo.c which allocates space for a char array with the length of 8 on the stack

#include<stdio.h>


main()
{
        char buffer[8];

        return 0;
}

I thought that 8 bytes will be allocated from stack for the eight chars but if I check this in gdb there are 10 bytes subtracted from the stack.

I compile the the program with this command on my Ubuntu 32 bit machine:

$ gcc -ggdb -o demo demo.c

Then I analyze the program with:

$ gdb demo

$ disassemble main

(gdb) disassemble main
Dump of assembler code for function main:
   0x08048404 <+0>: push   %ebp
   0x08048405 <+1>: mov    %esp,%ebp
   0x08048407 <+3>: and    $0xfffffff0,%esp
   0x0804840a <+6>: sub    $0x10,%esp
   0x0804840d <+9>: mov    %gs:0x14,%eax
   0x08048413 <+15>:    mov    %eax,0xc(%esp)
   0x08048417 <+19>:    xor    %eax,%eax
   0x08048419 <+21>:    mov    $0x0,%eax
   0x0804841e <+26>:    mov    0xc(%esp),%edx
   0x08048422 <+30>:    xor    %gs:0x14,%edx
   0x08048429 <+37>:    je     0x8048430 <main+44>
   0x0804842b <+39>:    call   0x8048340 <__stack_chk_fail@plt>
   0x08048430 <+44>:    leave  
   0x08048431 <+45>:    ret    
End of assembler dump.

0x0804840a <+6>: sub $0x10,%esp says, that there are 10 bytes allocated from the stack right?

Why are there 10 bytes allocated and not 8?

Upvotes: 2

Views: 2586

Answers (4)

Peter Cordes
Peter Cordes

Reputation: 365332

(I skipped over some things the other answers explain in more detail).

You compiled with -O0, so gcc is operating in a super-simple way that tells you something about compiler internals, but little about how to make good code from C.

gcc is keeping the stack 16B-aligned at all times. The 32bit SysV ABI only guarantees 4B stack alignment, but GNU/Linux systems actually assume and maintain gcc's default -mpreferred-stack-boundary=4 (16B-aligned).


Your version of gcc also defaults to using -fstack-protector, so it checks for stack-smashing in functions with local char arrays with 4 or more elements:

-fstack-protector
Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call "alloca", and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.

For some reason, this is actually kicking in with char arrays >= 4B, but not with integer arrays. (At least, not when they're unused!). char pointers can alias anything, which may have something to do with it.

See the code on godbolt, with asm output. Note how main is special: it uses andl $-16, %esp to align the stack on entry to main, but other functions assume the stack was 16B-aligned before the call instruction that called them. So they'll typically sub $24, %esp, after pushing %ebp. (%ebp and the return address are 8B total, so the stack is 8B away from being 16B-aligned). This leaves room for the stack-protector canary.


The 32bit SysV ABI only requires arrays to be aligned to the natural alignment of their elements, so this 16B alignment for the char array is just what the compiler decided to do in this case, not something you can count on.

The 64bit ABI is different:

An array uses the same alignment as its elements, except that a local or global array variable of length at least 16 bytes or a C99 variable-length array variable always has alignment of at least 16 bytes

(links from the tag wiki)

So you can count on char buf[1024] being 16B-aligned on SysV, allowing you to use SSE aligned loads/stores on it.

Upvotes: 2

vane
vane

Reputation: 2215

sub $0x10, %esp is saying that there are 16 bytes on the stack, not 10 since 0x is hexadecimal notation.

The amount of space for the stack is completely dependent on the compiler. In this case it's most like an alignment issue where the alignment is 16 bytes and you've requested 8, so it gets increased to 16.

If you requested 17 bytes, it would most likely have been sub $0x20, %esp or 32 bytes instead of 17.

Upvotes: 3

David J
David J

Reputation: 1564

Please note that the constant $0x10 is in hexadecimal this is equal to 16 byte. Take a look at the machine code:

0x08048404 <+0>: push   %ebp
0x08048405 <+1>: mov    %esp,%ebp
0x08048407 <+3>: and    $0xfffffff0,%esp
0x0804840a <+6>: sub    $0x10,%esp
...
0x08048430 <+44>:    leave  
0x08048431 <+45>:    ret 

As you can see before we subtract 16 from the esp we ensure to make esp pointing to a 16 byte aligned address first (take a look at the and $0xfffffff0,%esp instruction). I guess the compiler try to respect the alignment so he simply reserves 16 byte as well. It does not matter anyway because 8 byte fit into 16 byte very well.

Upvotes: 3

unwind
unwind

Reputation: 400029

No, 0x10 means it's hexadecimal, i.e. 1016, which is 1610 bytes in decimal.

Probably due to alignment requirements for the stack.

Upvotes: 5

Related Questions