斯坦福大学的教程和海湾合作委员会之间的冲突 [英] Conflict between a Stanford tutorial and GCC

查看:138
本文介绍了斯坦福大学的教程和海湾合作委员会之间的冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据这个电影(约38分钟),如果我有两个函数具有相同的本地变量,他们将使用相同​​的空间。所以,下面的程序,应该打印 5 。与 GCC 结果编译它 -1218960859 。为什么?

程序:

 的#include<&stdio.h中GT;无效A()
{
    int类型的;
    的printf(%i的一个);
}无效B()
{
    int类型的;
    1 = 5;
}诠释的main()
{
    B();
    一个();
    返回0;
}

的要求,这里是从反汇编器的输出:

  0804840c< A&GT ;:
 804840c:55推EBP
 804840d:89 E5 MOV EBP,ESP
 804840f:83 EC 28分ESP,0x28
 8048412:8B F4 45 EAX MOV,DWORD PTR [EBP-位于0xC]
 8048415:89 44 24 04 MOV DWORD PTR [ESP +为0x4],EAX
 8048419:C7 04 24 84 E8 08 04 MOV DWORD PTR [ESP],0x80484e8
 8048420:E8 CB FE FF FF通话80482f0< printf的PLT @>
 8048425:C9离开
 8048426:C3 RET08048427< B&GT ;:
 8048427:55推EBP
 8048428:89 E5 MOV EBP,ESP
 804842a:83 EC 10分ESP,为0x10
 804842d:C7 45 FC 05 00 00 00 MOV DWORD PTR [EBP-位于0x4],0x5的
 8048434:C9离开
 8048435:C3 RET08048436<主计算值:
 8048436:55推EBP
 8048437:89 E5 MOV EBP,ESP
 8048439:83 E4 f0和ESP,0xfffffff0
 804843c:E8 E6 FF FF FF拨打8048427< B>
 8048441:E8 C6 FF FF FF通话804840c< A>
 8048446:B8 00 00 00 00 MOV eax中,为0x0
 804844b:C9离开
 804844c:C3 RET
 804844d:66 90 XCHG斧,斧
 804844f:90 NOP


解决方案

是的,没错,这就是的未定义行为的,因为你正在使用的变量初始化 1

然而,在x86架构 2 这个实验应该工作。从堆栈中的值是不是删除了,因为它不是在初始化B(),同样的价值应该仍然在那里,只要堆栈帧是相同的。

我冒昧地猜测,由于 int类型的不是的使用的内部无效的B(),优化的code出编译器和一个5从未写入栈上的位置。尝试在 B添加的printf ()以及 - 它可能只是工作

此外,编译器标志 - 即优化级别 - 可能会影响这个实验也是如此。试图通过传递 -O0 来禁止GCC优化。

编辑:的我刚刚编译你的code与的gcc -O0 (64位),而事实上,该程序打印5正如一位熟悉调用堆栈期望的那样。事实上,它的工作,即使没有 -O0 。一个32位版本的行为可能不同。

免责声明:永远不要,曾经使用真实code这样的事情

<分> 1 - 有事情<一辩论href=\"http://stackoverflow.com/questions/19559133/conflict-between-a-stanford-tutorial-and-gcc/19559236?noredirect=1#comment29028763_19559192\">below这个是否是正式UB,或者只是未predictable。

<子> 2 - 同样的x64,大概每隔架构,使用调用堆栈(在与至少MMU的)


让我们来看看在一个原因,它的没有的工作。这是最好的出现在32位,所以我会用 -m32 编译。

$ GCC --version
海湾合作委员会(GCC)4.7.2 20120921(红帽4.7.2-2)

我用 $ GCC -m32 -O0 test.c以(禁用优化)编译。当我运行这一点,它打印乱码。

看着 $ objdump的-Mintel -d ./a.out

080483ec&LT; A&GT ;:
 80483ec:55推EBP
 80483ed:89 E5 MOV EBP,ESP
 80483ef:83 EC 28分ESP,0x28
 80483f2:8B F4 45 EAX MOV,DWORD PTR [EBP-位于0xC]
 80483f5:89 44 24 04 MOV DWORD PTR [ESP +为0x4],EAX
 80483f9:C7 04 24 84 4 08 04 MOV DWORD PTR [ESP],0x80484c4
 8048400:E8 CB FE FF FF调用80482d0&LT; printf的PLT @&GT;
 8048405:C9离开
 8048406:C3 RET08048407&LT; B&GT ;:
 8048407:55推EBP
 8048408:89 E5 MOV EBP,ESP
 804840a:83 EC 10分ESP,为0x10
 804840d:C7 45 FC 05 00 00 00 MOV DWORD PTR [EBP-位于0x4],0x5的
 8048414:C9离开
 8048415:C3 RET

我们看到,在 B ,编译器保留为0x10个字节的堆栈空间,并初始化我们的 int类型的变量在 [EBP-位于0x4] 5

A 但是,编译器放在 int类型的 [EBP-位于0xC] 。因此,在这种情况下,我们的局部变量的没有的在同一个地方结束了!通过在 A 添加的printf()通话以及将导致 A的堆栈帧 B 是相同的,并打印 55

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

解决方案

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.

这篇关于斯坦福大学的教程和海湾合作委员会之间的冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆