调用 new(堆)后由意外的汇编代码引发的异常

Posted

技术标签:

【中文标题】调用 new(堆)后由意外的汇编代码引发的异常【英文标题】:Exception thrown by unexpected assembler code after a call to new (heap) 【发布时间】:2010-12-22 08:21:55 【问题描述】:

我正在追踪一个异常,该异常是在每次调用 new 后由编译器添加的部分代码引发的。它是标准的 C++ new,应该从堆中获取一些内存并调用类的构造函数。

我们在 SH4 处理器上运行带有 GCC 2.95(或 2.96 不确定)的 VxWorks 5.5.1。在 SNiFF+ 4.1 补丁 1 中编译。

C++ 代码如下所示。

CBlocksFile* pBlockFile = new CBlocksFile(szHeaderFile, szDataFile);

并且生成的汇编代码在调用new之后有一个terminate/delete/throw处理。这种模式似乎适用于对 new 的所有调用。

// call to "new"
c4d6000  d14d        mov.l      @(0x134,pc),r1 (= 0x0c06c1e0 = ___builtin_new)
c4d6002  410b        jsr        @r1
c4d6004  e414       (mov        #20,r4)
...
// compiler generates throw path address
c4d6022  d246        mov.l      @(0x118,pc),r2 (= 0x0c4d6034)
...
// and pushes it to the stack
c4d602c  1121        mov.l      r2,@(4,r1)
...
c4d6030  a002        bra        +4       (==> 0x0c4d6038 : GOOD_PATH)
...
// throw path (there is no visible jump to this address)
c4d6034  a088        bra        +272       (==> 0x0c4d6148 : THROW_PATH)
...
GOOD_PATH:
...
// call to constructor
c4d6058  d139        mov.l      @(0xe4,pc),r1 (= 0x0c4d1730 T ___Q211CBlocksFilePCcT1bUcl)
...
c4d6060  410b        jsr        @r1
...
// normal path
return

THROW_PATH:
...
// same pattern again, compiler generates terminate path address
c4d6164  d22f        mov.l      @(0xbc,pc),r2 (= 0x0c4d6172)
...
// and pushes it to the stack
c4d616a  1121        mov.l      r2,@(4,r1)
...
c4d616e  a002        bra        +4       (==> 0x0c4d6176 : NO_TERMINATE)
...
c4d6172  a039        bra        +114       (==> 0x0c4d61e8 : TERMINATE_A)
...
NO_TERMINATE:
...
// delete handling
if ( ?? )
c4d617c  2118        tst        r1,r1
c4d617e  8d04        bt/s       +8       (==> 0x0c4d618a)
...

    delete ??
...
c4d6184  d128        mov.l      @(0xa0,pc),r1 (= 0x0c06be20 = ___builtin_delete)
c4d6186  410b        jsr        @r1
...

c4d618a  9044        mov.w      @(0x88,pc),r0 (= 0x0000028c)
c4d618c  02ee        mov.l      @(r0,r14),r2
c4d618e  5121        mov.l      @(4,r2),r1
c4d6190  6112        mov.l      @r1,r1
c4d6192  1211        mov.l      r1,@(4,r2)
c4d6194  d125        mov.l      @(0x94,pc),r1 (= 0x0c06a880 = ___sjthrow)
c4d6196  410b        jsr        @r1

这段代码有什么用?看起来不像是没有内存的情况,因为 throw 不在新函数内。

为什么叫 throw?来自谁?这种将代码地址放入堆栈的模式有两次,以后可能会用于执行。

谢谢

【问题讨论】:

是多线程代码吗?尝试在新抛出之前检查代码内存使用情况。 这是单线程代码。对我来说,看起来 new 没有进行抛出,因为在这种情况下,我希望在调用堆栈上看到 __builtin_new (并且对 ___sjthrow 的调用与所示代码处于同一堆栈级别)。或者它可能是在处理构造函数抛出的异常时重新抛出。明年我会检查内存使用情况。 【参考方案1】:

猜测会应用此模式,因为 c++ 的要求是,如果构造函数抛出异常,则应释放对象的内存。因此,如果在构造函数中抛出异常,新分配的对象将被删除。

您总是可以尝试从对象的构造函数中显式抛出一些东西,以查看执行路径以验证此答案是否有用。

【讨论】:

这是一个很好的观点。重新抛出异常可能是接下来要寻找的东西。在这种情况下,我不会在调用堆栈中看到对构造函数的调用或对 new 的调用。明年我会检查的。谢谢【参考方案2】:

我想在问题解决后快速回答我自己的问题。

我添加了一个新的失败处理程序来查看它是否会被调用。并且在引发上述异常之前就调用了它。所以它变成了“内存不足的情况”,在几个小时内我就发现了内存泄漏。

在 VxWorks 中,如果在内存不足情况后引发异常,您似乎看不到堆栈跟踪中的“新”函数调用。

感谢 DumpCoder 和 villintehaspam,他们让我思考了正确的方向。

【讨论】:

以上是关于调用 new(堆)后由意外的汇编代码引发的异常的主要内容,如果未能解决你的问题,请参考以下文章

从可变参数函数的调用引发异常崩溃一例引发的一些思考

在 IClientChannel 代理上调用 Abort() 会引发异常吗?

调试与异常

Pyside 插槽中的异常处理令人惊讶

Active Directory - 调用的目标已引发异常

如何使用 Mockery 在模拟方法的第 N 次调用中引发异常