You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

abi::__dynamic_cast向上转型返回nullptr问题排查与解决求助

钩子C++异常抛出机制时abi::__dynamic_cast向上转型返回nullptr的问题

场景说明

我需要通过钩子__cxxabiv1命名空间下的__cxa_throw函数拦截C++异常抛出,在抛出时若为自定义异常类型或其子类,就修改其中的成员值,钩子框架如下:

namespace __cxxabiv1
{
    extern "C" void __cxa_throw(void* voidPointerToActualObject, std::type_info* stdTypeInfoOfActualObject, void (*destructor)(void *))
    {
        // 若抛出的异常是自定义类型或其子类,向其中写入值
    }
}

我编写了包含简单继承层级的测试代码验证逻辑:

#include <stdexcept>

class Upper : public std::exception
{
    public:
        int pokeMe = 111111;
};
class Lower : public Upper {};

int main()
{
    throw Lower();
}

#include <cxxabi.h>

namespace __cxxabiv1
{
    extern "C" void __cxa_throw(void* voidPointerToActualObject, std::type_info* stdTypeInfoOfActualObject, void (*destructor)(void *))
    {
        // 这段代码可以正常工作:
        Lower* staticallyTypedPointerToActualObject = reinterpret_cast<Lower*>(voidPointerToActualObject);
        auto thisWorks = dynamic_cast<Upper*>(staticallyTypedPointerToActualObject);
        thisWorks->pokeMe = 222222;

        // 但当未知实际静态类型时,仅通过void*和type_info调用abi::__dynamic_cast向上转型返回nullptr:
        auto abiTypeInfoOfActualObject = dynamic_cast<const abi::__class_type_info*>(stdTypeInfoOfActualObject);
        auto abiTypeInfoOfUpper = dynamic_cast<const abi::__class_type_info*>(&typeid(Upper));
        Upper* thisDoesNotWork = reinterpret_cast<Upper*>(abi::__dynamic_cast(voidPointerToActualObject, abiTypeInfoOfActualObject, abiTypeInfoOfUpper, -1));
        thisDoesNotWork->pokeMe = 333333;

        // 省略:调用原始__cxa_throw函数
        // 以下代码用于抑制警告:
        (void)destructor; // 未使用参数
        while (1) { } // 从不返回的函数
    }
}

核心问题

我认为abi::__dynamic_cast应该支持向上转型,但实际调用时却返回nullptr。请问这是什么原因?该如何解决?

另外测试发现,这个函数的向下转型可以正常工作:

auto abiTypeInfoOfActualObject = dynamic_cast<const abi::__class_type_info*>(&typeid(Upper)); // 测试用
auto abiTypeInfoOfUpper = dynamic_cast<const abi::__class_type_info*>(&typeid(Lower)); // 转为Lower而非Upper
Lower* thisDoesNotWork = reinterpret_cast<Lower*>(abi::__dynamic_cast(voidPointerToActualObject, abiTypeInfoOfActualObject, abiTypeInfoOfUpper, -1));

原因分析与解决方案

原因

abi::__dynamic_cast的第四个参数src2dst_offset代表源对象相对于传入指针的偏移量:

  • 当传入-1时,函数会认为要进行交叉转型或需要自动计算偏移的复杂转型;
  • 但单继承场景下的向上转型,源对象(派生类)指针与基类指针偏移量为0,传入-1会导致函数无法正确识别转型路径,最终返回nullptr。

解决方案

将abi::__dynamic_cast的第四个参数从-1改为0,适配单继承向上转型的偏移量要求:

auto abiTypeInfoOfActualObject = dynamic_cast<const abi::__class_type_info*>(stdTypeInfoOfActualObject);
auto abiTypeInfoOfUpper = dynamic_cast<const abi::__class_type_info*>(&typeid(Upper));
// 将第四个参数改为0
Upper* nowWorks = reinterpret_cast<Upper*>(abi::__dynamic_cast(voidPointerToActualObject, abiTypeInfoOfActualObject, abiTypeInfoOfUpper, 0));
if (nowWorks != nullptr) {
    nowWorks->pokeMe = 333333;
}

补充说明

  • 向下转型时传入-1可以让函数自动计算基类到派生类的指针偏移量,这也是你测试中向下转型能正常工作的原因;
  • 多继承或虚拟继承场景下,需根据实际继承结构调整偏移量,或通过-1让函数自动计算;
  • 转型后务必检查指针是否为nullptr,避免空指针访问。

内容的提问来源于stack exchange,提问作者Niko O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:15:36