Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conflict between a Stanford tutorial and GCC

According to this movie (around minute 38), if I have two functions with the same local vars, they will use the same space. So the following program, should print 5. Compiling it with gcc results -1218960859. why?

The program:

#include <stdio.h>  void A() {     int a;     printf("%i",a); }  void B() {     int a;     a = 5; }  int main() {     B();     A();     return 0; } 

as requested, here is the output from the disassembler:

0804840c <A>:  804840c:   55                      push   ebp  804840d:   89 e5                   mov    ebp,esp  804840f:   83 ec 28                sub    esp,0x28  8048412:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]  8048415:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax  8048419:   c7 04 24 e8 84 04 08    mov    DWORD PTR [esp],0x80484e8  8048420:   e8 cb fe ff ff          call   80482f0 <printf@plt>  8048425:   c9                      leave    8048426:   c3                      ret      08048427 <B>:  8048427:   55                      push   ebp  8048428:   89 e5                   mov    ebp,esp  804842a:   83 ec 10                sub    esp,0x10  804842d:   c7 45 fc 05 00 00 00    mov    DWORD PTR [ebp-0x4],0x5  8048434:   c9                      leave    8048435:   c3                      ret      08048436 <main>:  8048436:   55                      push   ebp  8048437:   89 e5                   mov    ebp,esp  8048439:   83 e4 f0                and    esp,0xfffffff0  804843c:   e8 e6 ff ff ff          call   8048427 <B>  8048441:   e8 c6 ff ff ff          call   804840c <A>  8048446:   b8 00 00 00 00          mov    eax,0x0  804844b:   c9                      leave    804844c:   c3                      ret      804844d:   66 90                   xchg   ax,ax  804844f:   90                      nop 
like image 532
elyashiv Avatar asked Oct 24 '13 07:10

elyashiv


2 Answers

Yes, yes, this is undefined behavior, because you're using the variable uninitialized1.

However, on the x86 architecture2, this experiment should work. The value isn't "erased" from the stack, and since it's not initialized in B(), that same value should still be there, provided the stack frames are identical.

I'd venture to guess that, since int a is not used inside of void B(), the compiler optimized that code out, and a 5 was never written to that location on the stack. Try adding a printf in B() as well - it just may work.

Also, compiler flags - namely optimization level - will likely affect this experiment as well. Try disabling optimizations by passing -O0 to gcc.

Edit: I just compiled your code with gcc -O0 (64-bit), and indeed, the program prints 5, as one familiar with the call stack would expect. In fact, it worked even without -O0. A 32-bit build may behave differently.

Disclaimer: Don't ever, ever use something like this in "real" code!

1 - There's a debate going on below about whether or not this is officially "UB", or just unpredictable.

2 - Also x64, and probably every other architecture that uses a call stack (at least ones with an MMU)


Let's take a look at a reason why it didn't work. This is best seen in 32 bit, so I will compile with -m32.

$ gcc --version gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2) 

I compiled with $ gcc -m32 -O0 test.c (Optimizations disabled). When I run this, it prints garbage.

Looking at $ objdump -Mintel -d ./a.out:

080483ec <A>:  80483ec:   55                      push   ebp  80483ed:   89 e5                   mov    ebp,esp  80483ef:   83 ec 28                sub    esp,0x28  80483f2:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]  80483f5:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax  80483f9:   c7 04 24 c4 84 04 08    mov    DWORD PTR [esp],0x80484c4  8048400:   e8 cb fe ff ff          call   80482d0 <printf@plt>  8048405:   c9                      leave    8048406:   c3                      ret      08048407 <B>:  8048407:   55                      push   ebp  8048408:   89 e5                   mov    ebp,esp  804840a:   83 ec 10                sub    esp,0x10  804840d:   c7 45 fc 05 00 00 00    mov    DWORD PTR [ebp-0x4],0x5  8048414:   c9                      leave    8048415:   c3                      ret     

We see that in B, the compiler reserved 0x10 bytes of stack space, and initialized our int a variable at [ebp-0x4] to 5.

In A however, the compiler placed int a at [ebp-0xc]. So in this case our local variables did not end up at the same place! By adding a printf() call in A as well will cause the stack frames for A and B to be identical, and print 55.

like image 86
Jonathon Reinhart Avatar answered Sep 21 '22 12:09

Jonathon Reinhart


It's undefined behavior. An uninitialized local variable has an indeterminate value, and using it will lead to undefined behavior.

like image 28
Some programmer dude Avatar answered Sep 19 '22 12:09

Some programmer dude