斯坦福大学的教程和海湾合作委员会之间的冲突 [英] Conflict between a Stanford tutorial and GCC
问题描述
根据这个电影(约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屋!