在OSX 10.8.2中覆盖`new`和`delete`崩溃

时间:2012-12-06 18:40:25

标签: c++ macos

我最近开始在我的代码中崩溃,我不得不覆盖默认的newdelete,我不完全确定,但可能是在最近的软件更新之后。

我正在运行Osx 10.8.2 build 12C54和以下gcc:

i686-apple-darwin11-llvm-gcc-4.2(GCC)4.2.1

这是我崩溃的堆栈痕迹。

(gdb) bt
#0  0x00007fff8f2a4212 in __pthread_kill ()
#1  0x00007fff8fdecaf4 in pthread_kill ()
#2  0x00007fff8fe30dce in abort ()
#3  0x00007fff8fe04959 in free ()
#4  0x00000001009cd947 in foundation::aligned_free ()
#5  0x00000001009e5322 in (anonymous namespace)::delete_impl ()
#6  0x00000001009e53e8 in operator delete[] ()
#7  0x00007fff87316ecc in TPropertyStream::SetLength ()
#8  0x00007fff87286099 in TPropertyStream::WriteData ()
#9  0x00007fff87286037 in TPropertyStream::Write ()
#10 0x00007fff87285ce7 in IAStoreStream::MaybeFlushBuffer ()
#11 0x00007fff87285641 in BuddyStorage::Commit ()
#12 0x00007fff87316c4c in TPropertyInfo::FlushChanges ()
#13 0x00007fff873169af in TPropertyInfo::FlushChanges ()
#14 0x00007fff8729ac47 in THFSPlusPropertyStore::FlushChanges ()
#15 0x00007fff872dc5e4 in TFSVolumeInfo::FlushVolumes ()
#16 0x00007fff872b087a in TNode::HandleFlushVolumes ()
#17 0x00007fff872684d3 in TNode::HandleNodeRequest ()
#18 0x00007fff872db4e7 in __block_global_1 ()
#19 0x00007fff87268040 in ExceptionSafeBlock ()
#20 0x00007fff87267fe1 in __PostNodeTaskRequest_block_invoke_0 ()
#21 0x00007fff8e074f01 in _dispatch_call_block_and_release ()
#22 0x00007fff8e0710b6 in _dispatch_client_callout ()
#23 0x00007fff8e07247f in _dispatch_queue_drain ()
#24 0x00007fff8e0722f1 in _dispatch_queue_invoke ()
#25 0x00007fff8e0721c3 in _dispatch_worker_thread2 ()
#26 0x00007fff8fdedcab in _pthread_wqthread ()
#27 0x00007fff8fdd8171 in start_wqthread ()

有没有人遇到过类似的问题?

2 个答案:

答案 0 :(得分:1)

Jon非常友好地将我们的问题发布在SO上。我是相关代码的作者。多年来,我们一直在Mac OS X上发布代码而没有任何问题。我们还在Windows(32位和64位)以及许多种类的Linux上发布。麻烦最近才在Mac OS X上启动。

基本上我们的应用程序由几个二进制文件组成,使用几个共享库(Mac OS X上的.dylib)。 newdelete的所有变体都在其中一个共享库中重载。它们不应该由其他共享库和二进制文件导出和使用,实际上它们不在Windows上。作为参考,重载运算符为here

我不确定到底发生了什么,但上面的callstack似乎表明某些Mac OS X代码正在使用我们自己的delete运算符释放内存,而我并不完全确定该内存是使用new的重载分配的,或者是否已完全分配给另一个内存子系统。

无论如何,它确实不应该使用我们的运营商。理想情况下,重载的newdelete将被隐藏到外部,就像Windows上的情况一样(因为共享库符号在该平台上默认隐藏)。

代码使用gcc 4.2.1(686-apple-darwin11-llvm-gcc-4.2(GCC)4.2.1(基于Apple Inc. build 5658)在Mac OS X 10.8.2上构建(LLVM build 2336.11) .00))。除此之外,我们还使用了Qt 4.8.2的预构建二进制文件(编写本文时的最新稳定版本)。我们最近在Mac OS X上切换到了更新版本的Qt,这可能与问题有关吗?可能是预先构建的Qt二进制文件与我们的代码有些不兼容吗? (我们将动态链接到Qt,因此它不应该是一个问题。)

希望有足够的细节来开始对话。

弗朗兹

答案 1 :(得分:1)

  

无论如何,它确实不应该使用我们的运营商。理想情况下,重载的new和delete将被隐藏到外部,就像Windows上的情况一样(因为默认情况下共享库符号在该平台上是隐藏的)。

Unix上共享库中的可见性通常与C ++中的外部链接相对应。也就是说,如果一个项目在C ++语言中具有外部链接,那么它在共享库中是外部可见的。 GCC的可见性属性甚至记录为修改链接。 (或者过去,我现在还没看到......)

此外,加载时间链接可以被视为C ++翻译阶段的第9阶段的一部分,因此在unix平台上共享对象和dylib的加载时间链接通常表现为静态链接而不是动态链接。也就是说,如果您在加载时链接的转换单元中替换operator new,则加载时链接器将为所有静态和加载时链接的转换单元解析此问题,就像您已替换{{1}一样在其中一个静态链接的翻译单元中。

在C ++中完全合法且定义良好,可以删除在另一个翻译单元中分配的一个翻译单元中分配的内存。因为默认情况下,加载时间链接在这方面与静态链接相同,所以即使在加载时链接的翻译单元之间也存在大量代码。同样,这是合法且定义良好的,只要正确理解加载时间链接是第9阶段的一部分,因此受C ++规范的约束。

当然,这不适用于使用operator new等例程的完全动态链接,因为第9阶段结束后程序已开始运行。这些程序的行为是实现定义的(或在C ++ 03中未定义),因此不受在一个TU中分配的资源可以在另一个TU中释放的要求的约束。


问题是Windows不会以这种方式运行。 Windows的加载时间链接似乎更像是动态链接,并且导出的符号或类型与C ++外部可见性无关。事实上,据我所知,甚至无法在Windows dll中导出dlopen()

我不相信Windows的加载时间链接确实符合要求,但很多代码必须处理它。对于只需要在一个或另一个环境中运行的代码来说没有问题,但对于应该在Windows和其他实现上运行的代码来说,它更加棘手。最常见的方法是不要重载::operator newoperator new。您可以使用其他名称编写分配和释放函数,可能作为C ++ Allocator类。如果您只需要处理自己的用户定义类型,另一个选项是定义成员operator delete来处理这些类型。