0

我最近开始在我的代码中出现崩溃,我不得不覆盖默认值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 ()

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

4

2 回答 2

1

Jon 很乐意将我们的问题发布在 SO 上。我是相关代码的作者。多年来,我们一直在 Mac OS X 上发布代码,没有出现任何问题。我们还提供 Windows(32 位和 64 位)和多种 Linux 版本。Mac OS X 最近才开始出现问题。

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

我不确定到底发生了什么,但是上面的调用堆栈似乎表明某些 Mac OS X 代码正在使用我们自己的delete运算符释放内存,而且我不完全确定内存是通过我们的过载分配的new,或者如果它完全被分配了另一个内存子系统。

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

该代码是使用 gcc 4.2.1 (686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (基于 Apple Inc. build 5658) (LLVM build 2336.11.00) 在 Mac OS X 10.8.2 上构建的)。除其他外,我们正在使用 Qt 4.8.2(撰写本文时的最新稳定版本)的预构建二进制文件。我们最近在 Mac OS X 上切换到更新版本的 Qt,这可能与问题有关吗?会不会是预建的 Qt 二进制文件与我们的代码有些不兼容?(我们正在动态链接 Qt,所以这应该不是问题。)

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

弗朗茨

于 2012-12-06T21:25:16.493 回答
1

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

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

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

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

当然,这不适用于使用例程的完全动态链接,例如dlopen()在阶段 9 结束并且程序开始运行之后发生的情况。此类程序的行为是实现定义的(或在 C++03 中未定义),因此不受在一个 TU 中分配的资源可以在另一个 TU 中释放的要求的约束。


问题是 Windows 的行为方式并非如此。Windows 的加载时间链接似乎更像动态链接,并且是否导出符号或类型与 C++ 外部可见性无关。事实上,据我所知,甚至不可能::operator new在 Windows dll 中导出。

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

于 2012-12-06T22:49:02.487 回答