OSX + llvm / libc ++上的内存损坏/结构重新排序

时间:2014-03-19 14:50:02

标签: c++ macos llvm memory-corruption

最终得到一个跨平台项目进行编译后,我在OSX上遇到了最奇怪的错误。该程序以不同的方式崩溃(但有时可能存活以显示它的ui)。单步执行XCode调试器,我会看到多个位置,其中子对象的值根据上下文更改。这就是我所遇到的问题:

class third
{
public:
    int some_data;
    void do_something()
    {
    }

};

class second
{
public:
    third * thirdPtr;

    second()
        : thirdPtr(nullptr)
    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
    }

};


class first
{
    second * secondInstance;
    first()
        : secondInstance(nullptr)
    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        // (maybe) crash
        secondInstance->thirdPtr->do_something();

    }

};

在单步执行时,我向第三个指针添加了一个观察点,这是一个示例输出:

Watchpoint 1 hit:
old value: 0x00000001
new value: 0x00000000

Watchpoint 1 hit:
old value: 0x00000000
new value: 0x04821c34

这是该计划的标准:

second = 0x4821C20, third = 0x4821C34
second = 0x4821C20, third = 0x3404821C

我从未见过这样的事情。显然结构更复杂并且使用继承,但是在初始化下没有其他任何东西可以访问或写入这些结构。我的源代码中有很多奇怪的故障和问题,所以我认为这个故障是多个地方的问题。请注意,源代码在其他平台上已经运行了很长时间,即使在外部库代码中也是如此(我使用juce作为项目)。

起初我怀疑操作员是虚假的,但它似乎与物体的组成有关。有趣的是,两个不同的三分之一似乎有很强的相似之处。

我真的不知所措,我已经准备好得出结论llvm重新排列编译单元之间结构的布局(类/结构当然是单独的文件)..或者其他东西。我认为我错了 - 但有没有人经历过这样的事情?

---------编辑----------:

所以我添加了这段代码: (在第二个构造函数内):

    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
        printf("position = %d", offsetof(second, thirdPtr);
    }

(在第一个构造函数中):

    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        printf("position = %d", offsetof(second, thirdptr));

    }

它打印出来......

13
16

注意:这是完整结构的输出(即不是这里给出的例子)。但是:结构的布局实际上是不同的,这取决于我在哪个编译/翻译单元。这到底是怎么回事?

---------编辑2 --------:

所以我决定检查对齐方式,这可能是关键问题:

第二个构造函数中的stdout:

second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4
第一个构造函数中的

stdout:

second = 0x3649090, third = 0xA4036490
offset of second in third = 13
sizeof second = 226
align of third 1, align of second 1

所以在翻译单元中似乎改变了对齐方式。如何在整个项目中实施标准?

编辑3:我设法让它“运行”,因为它不会立即崩溃,方法是将属性((打包))应用于二等。但这真的不会让我感到安全,为什么我必须这样做呢?是否存在在翻译单元中操作此设置的全局设置?

------编辑4:解决方案----

包含的标头有一个不匹配的#pragma pack(llvm显然支持)指令,如下所示:

    #ifdef __MSVC__
        #pragma pack(push, 1)
        #pragma warning(disable:4482)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif

任何编译器都会更改打包堆栈,但只有在使用msvc ++编译器时才能恢复。这使整个项目的包装为1.

1 个答案:

答案 0 :(得分:2)

在使用用户定义的结构排列对齐时,重要的是重置包装堆栈的重要性。

Visual Studio编译器和clang的llvm前端都使用#pragma指令支持相同的语法,可以在这里研究:
http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx

在我的情况下,包含的标题有一个不匹配的#pragma pack指令,如下所示:

    #ifdef __MSVC__
        #pragma pack(push, 1)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif

任何编译器都会更改打包堆栈,但只有在使用msvc ++编译器时才能恢复。这使得包含该文件的翻译单元的打包对齐与不具有该翻译单元的翻译单元不同,即使两个翻译单元都看到完全相同的结构定义。为了完整起见,这里(在我的情况下)纠正了#pragma指令:

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack (push, 1)
    #endif

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack(pop)
    #endif