两个GCC编译相同的输入,生成两个不同的代码(第二个错误) [英] Two GCC compiles for same input, two different codes generated (second one wrong)

查看:101
本文介绍了两个GCC编译相同的输入,生成两个不同的代码(第二个错误)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有时会遇到GCC(4.6.4,Ubuntu 12.04)的一个奇怪问题,我正在使用它来编译一个巨大的项目(数百个文件和数十万行代码),但我最近发现了一些问题。在某些编译之后(似乎是随机发生的),我得到了一段特定的代码,这些代码的编译方式不同且错误,导致我的代码中出现未定义的行为:

 <$ c 

{
public
struct anotherDerived:public anoterBaseClass
{
void SomeMethod()
{
someMember-> someSetter(2);





其中定义了someSetter as:

  void someSetter(varType varName){someOtherMember = varName; } 

通常,SomeMethod()会被编译为:

  00000000019fd910 mov 0x20(%rdi),%r​​ax 
00000000019fd914 movl $ 0x2,0x278c(%rax)
00000000019fd91e retq

但是有时它会被(错误地)编译为:

  000000000196e4ee mov 0x20(%rdi),%r​​ax 
000000000196e4f2 movl $ 0x2,0x27d4(%rax)
000000000196e4fc retq

setter似乎被内联,可能是因为编译标志-O2:

  -std = c ++ 11 -m64 -O2 -ggdb3 -pipe -Wliteral-suffix -fpermissive -fno-fast-math -fno-strength-reduce -fno-delete-null-pointer-checks -fno-严格别名

但这不是问题。真正的问题是成员 someOtherMember 的偏移量, 0x278c 是正确的(第一种情况),而 0x27d4 不正确(第二种情况)最终修改完全不同的类的成员。这是为什么发生?我错过了什么? (另外,我不知道我可以发布什么其他相关信息,所以请问)。请记住,在再次编译项目(完全重新编译或仅编译修改的文件),没有修改受影响的文件(或使用了类的文件)时会发生这种情况。例如,在一个完全不相关的文件中添加一个简单的printf()可能会触发这种行为,或者当它发生时让它消失。
我是否应该将此归咎于-O2?如果没有优化标记,我无法重现它,因为这完全是随机的。
我正在使用 make -j 8 ,即使在清理构建文件夹后,也会发生这种情况,但不一定只会在执行此操作后才会发生。 正如评论中所述,你可能有不同的 .cpp 中定义你的类的定义的东西,对于例如一个 #pragma pack 或类似的东西在包含 .h 之前。当链接器必须选择时,它可能会选择非确定性的(因为它期望所有的定义是相同的)。

缩小搜索范围问题,我会这样做:
$ b


  1. 使用调试符号编译整个项目( -g
  2. 使用 gdb 来确定根据每个模块的有问题字段的偏移量
  3. li>
  4. 一旦你发现你有不同的值,你可以使用 gcc -E 展开所有预处理器的东西并寻找你的问题。 / li>

作为步骤2的辅助工具,您可以使用此bash单行程序(要在目标文件所在的目录中运行) :

 对于我在./*.o中;做echo -n$ i:; gdb -batch -q$ i-exprint&((YourClass *)0) - > yourField;完成


I am having a strange issue with GCC (4.6.4, Ubuntu 12.04) sometimes, I am using it to compile a huge project (hundreds of files and hundreds of thousands of lines of code), but I recently spotted something. After certain compiles (seems to happen randomly), I get a specific piece of code compiled differently and erroneously, causing undefined behavior in my code:

class someDerivedClass : public someBaseClass
{
    public:
        struct anotherDerived : public anoterBaseClass
        {
            void SomeMethod()
            {
                someMember->someSetter(2);
            }
        }
}

Where "someSetter" is defined as:

void someSetter(varType varName) { someOtherMember = varName; }

Normally, SomeMethod() gets compiled to:

00000000019fd910  mov 0x20(%rdi),%rax 
00000000019fd914  movl $0x2,0x278c(%rax) 
00000000019fd91e  retq  

But sometimes it gets (wrongfully) compiled to:

000000000196e4ee  mov 0x20(%rdi),%rax 
000000000196e4f2  movl $0x2,0x27d4(%rax) 
000000000196e4fc  retq  

The setter seems to get inlined, probably because of compile flags -O2:

-std=c++11 -m64 -O2 -ggdb3 -pipe -Wliteral-suffix -fpermissive -fno-fast-math -fno-strength-reduce -fno-delete-null-pointer-checks -fno-strict-aliasing

but that's not the issue. The real issue is the offset of the member someOtherMember, 0x278c is correct (first case) but 0x27d4 is incorrect (second case) and this obviously ends up modifying a totally different member of the class. Why is this happening? What am I missing? (also, I don't know what other relevant info I can post, so ask). Please keep in mind that this happens when compiling the project again (either full recompile or just compiling modified files only), without modifying the affected file (or files with the used classes). For example, just adding a simple printf() somewhere in a totally unrelated file might trigger this behavior or make it go away when it happens. Should I simply blame this on the -O2? I can't reproduce it without optimization flag because this happens totally random. I am using make -j 8, this happens even after cleaning build folder, but doesn't necessarily happen only after doing that

解决方案

As stated in the comments, you probably have something that conditions the definition of your class differently in the various .cpp, for example a #pragma pack or something like that before the inclusion of your .h; when the linker has to choose, it may choose non-deterministically (since it expects all the definitions to be the same).

To narrow your search for the root of the problem, I would do something like this:

  1. compile your whole project with debug symbols (-g);
  2. use gdb to determine what is the offset of the "problematic" field according to each module
  3. once you find where you have different values, you may use gcc -E to expand all the preprocessor stuff and look for your problem.

As an aid for step 2, you can use this bash one-liner (to be run in the directory where are the object files):

for i in ./*.o; do echo -n "$i: "; gdb -batch -q "$i" -ex "print &((YourClass*)0)->yourField"; done

这篇关于两个GCC编译相同的输入,生成两个不同的代码(第二个错误)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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