Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why gcc sometimes allocate extra space for local but sometimes not?

Tags:

c

gcc

Here is my code

#include <stdio.h>                                                              

char func_with_ret()
{
    return 1;
}
void func_1()
{
    char buf[16];
    func_with_ret();
}
void func_2()
{
    char buf[16];
    getchar();
}
int main()
{
    func_1();
    func_2();
}
  1. I declare 16-byte local buffers to keep the stack pointer aligned(for x86).
  2. I write two function "func_1", "func_2", they look almost the same - allocate 16-byte local buffer and call a function with char return value and no parameter, but one is self-defined and the other is getchar().
  3. Compile with gcc parameter "-fno-stack-protector"(so there's no canary on stack) and "-O0" to avoid unexpected optimization behavior.

Here is the disassembly code by gdb for func_1 and func_2.

Dump of assembler code for function func_1:
   0x08048427 <+0>:     push   ebp
   0x08048428 <+1>:     mov    ebp,esp
   0x0804842a <+3>:     sub    esp,0x10
   0x0804842d <+6>:     call   0x804841d <func_with_ret>
   0x08048432 <+11>:    leave  
   0x08048433 <+12>:    ret 

Dump of assembler code for function func_2:
   0x08048434 <+0>:     push   ebp
   0x08048435 <+1>:     mov    ebp,esp
   0x08048437 <+3>:     sub    esp,0x18
   0x0804843a <+6>:     call   0x80482f0 <getchar@plt>
   0x0804843f <+11>:    leave  
   0x08048440 <+12>:    ret

In func_1, buffer is allocated for 0x10(16) bytes, but in func_2 , it is allocated for 0x18(24) bytes, why?


Edit: @Attie figure out that buffer size is actually the same for both, but there's strange 8-byte stack spaces in func_2 don't know where it comes from.

like image 863
poming Avatar asked Mar 31 '17 16:03

poming


Video Answer


1 Answers

I have just tried to reproduce this, see below:

Compiling for x86-64 (no joy):

$ gcc p.c -g -o p -O0 -fno-stack-protector
$ objdump -d p

p:     file format elf64-x86-64

[...]

0000000000400538 <func_1>:
  400538:       55                      push   %rbp
  400539:       48 89 e5                mov    %rsp,%rbp
  40053c:       48 83 ec 10             sub    $0x10,%rsp
  400540:       b8 00 00 00 00          mov    $0x0,%eax
  400545:       e8 e3 ff ff ff          callq  40052d <func_with_ret>
  40054a:       c9                      leaveq
  40054b:       c3                      retq

000000000040054c <func_2>:
  40054c:       55                      push   %rbp
  40054d:       48 89 e5                mov    %rsp,%rbp
  400550:       48 83 ec 10             sub    $0x10,%rsp
  400554:       e8 c7 fe ff ff          callq  400420 <getchar@plt>
  400559:       c9                      leaveq
  40055a:       c3                      retq

Compiling for i386 (success):

$ gcc p.c -g -o p -O0 -fno-stack-protector -m32
$ objdump -d p

p:     file format elf32-i386

[...]

08048427 <func_1>:
 8048427:       55                      push   %ebp
 8048428:       89 e5                   mov    %esp,%ebp
 804842a:       83 ec 10                sub    $0x10,%esp
 804842d:       e8 eb ff ff ff          call   804841d <func_with_ret>
 8048432:       c9                      leave
 8048433:       c3                      ret

08048434 <func_2>:
 8048434:       55                      push   %ebp
 8048435:       89 e5                   mov    %esp,%ebp
 8048437:       83 ec 18                sub    $0x18,%esp
 804843a:       e8 b1 fe ff ff          call   80482f0 <getchar@plt>
 804843f:       c9                      leave
 8048440:       c3                      ret

This doesn't appear to be related to any of the following:

  • The fact that getchar() is a library function, and thus we are calling into the PLT (Procedure Linkage Table).
  • Related to the return type of the function
  • The order of the calls in main()
  • The order of the functions in the binary
  • Dynamic / static compliation

If you however increase the size of your buffer by one, to 17, then the stack usage increases to 32 and 40 bytes (from 16 and 24 bytes) respectively. The difference is 16 bytes, which is used for alignment, as answered here.

I can't answer why the stack alignment appears to be off by 8-bytes upon entering func_2() though.


If you update func_1() and func_2() to have a 15-byte buffer, and a single byte variable, and write data to them, then you can see where these items are in the stack frame:

void func_1(void) {
    char buf[15];
    char x;
    buf[0] = 0xaa;
    x = 0x55;
    func_with_ret();
}

void func_2(void) {
    char buf[15];
    char x;
    buf[0] = 0xaa;
    x = 0x55;
    getchar();
}
08048434 <func_1>:
 8048434:       55                      push   %ebp
 8048435:       89 e5                   mov    %esp,%ebp
 8048437:       83 ec 10                sub    $0x10,%esp
 804843a:       c6 45 f0 aa             movb   $0xaa,-0x10(%ebp)
 804843e:       c6 45 ff 55             movb   $0x55,-0x1(%ebp)
 8048442:       e8 d6 ff ff ff          call   804841d <func_with_ret>
 8048447:       c9                      leave
 8048448:       c3                      ret

08048449 <func_2>:
 8048449:       55                      push   %ebp
 804844a:       89 e5                   mov    %esp,%ebp
 804844c:       83 ec 18                sub    $0x18,%esp
 804844f:       c6 45 e8 aa             movb   $0xaa,-0x18(%ebp)
 8048453:       c6 45 f7 55             movb   $0x55,-0x9(%ebp)
 8048457:       e8 94 fe ff ff          call   80482f0 <getchar@plt>
 804845c:       c9                      leave
 804845d:       c3                      ret
like image 69
Attie Avatar answered Nov 15 '22 01:11

Attie