两个GCC编译用于相同的输入,生成两个不同的代码(第二个错误)

时间:2015-05-03 23:55:38

标签: c++ gcc

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

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

where" someSetter"定义为:

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

通常,SomeMethod()被编译为:

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

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

000000000196e4ee  mov 0x20(%rdi),%rax 
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-strict-aliasing

但这不是问题。真正的问题是成员的偏移 someOtherMember 0x278c 是正确的(第一种情况)但 0x27d4 是不正确的(第二种情况)这显然最终修改了一个完全不同的班级成员。为什么会这样?我错过了什么? (另外,我不知道我可以发布的其他相关信息,所以请问)。请记住,再次编译项目(完全重新编译或仅编译修改过的文件),没有修改受影响的文件(或使用已使用类的文件)时会发生这种情况。例如,在一个完全不相关的文件中的某处添加一个简单的printf()可能会触发此行为,或者在它发生时将其消失。 我应该把这个归咎于-O2吗?我无法在没有优化标志的情况下重现它,因为这种情况完全是随机发生的。 我正在使用 make -j 8 ,即使在清理构建文件夹之后也会发生这种情况,但是只有在这样做之后才会发生

1 个答案:

答案 0 :(得分:4)

正如评论中所述,在包含.cpp之前,您可能会在各种#pragma pack中以不同的方式对您的类的定义进行不同的调整,例如.h或类似的内容。 }};当链接器必须选择时,它可以选择非确定性(因为它期望所有定义都相同)。

要缩小搜索问题的根源,我会做这样的事情:

  1. 使用调试符号(-g);
  2. 编译整个项目
  3. 使用gdb根据每个模块确定“有问题”字段的偏移量
  4. 一旦找到具有不同值的位置,就可以使用gcc -E展开所有预处理器内容并查找问题。
  5. 作为第2步的辅助,你可以使用这个bash one-liner(在目标文件所在的目录中运行):

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