gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-28 02:53:36 25 4
gpt4 key购买 nike

在最终编译一个跨平台项目后,我在 OSX 上遇到了最奇怪的错误。该程序以不同的方式崩溃(但有时可能会幸存下来以显示它的用户界面)。通过 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)。

起初我怀疑 operator new 是假的,但它似乎与对象的组合有关。有趣的是,这两个不同的三分之一似乎有很强的相似性。

我在这里真的不知所措,我已经准备好得出结论,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

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

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

来自第二个构造函数内部的标准输出:

second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4

来自第一个构造函数内部的标准输出:

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

所以翻译单元的对齐方式似乎发生了变化。我可以做些什么来在整个项目中实现标准?

我设法让它“运行”,因为它不会立即崩溃,方法是将 attribute((packed)) 应用于第二个类。但这真的不会让我感到安全,为什么我必须这样做?是否有在翻译单元中操纵此设置的全局设置?

最佳答案

很明显,在使用用户定义的结构打包对齐方式时,重置打包堆栈是多么重要。

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

在我的例子中,包含的 header 有一个不匹配的#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

关于c++ - OSX + llvm/libc++ 上的内存损坏/结构重新排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22509596/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com