加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 在MSVC ABI中,如何只给出一个(void *)可靠地找到vtable?

发布时间:2020-12-16 06:53:43 所属栏目:百科 来源:网络整理
导读:这个问题具体是关于非便携式MSVC ABI的东西. 我试图在明显不可移植但不是魔法的C中写出相当于C的typeid.对于Itanium ABI(在Linux / Mac上使用),它非常简单: const std::type_info dynamicast_typeid(void *mdo){ std::type_info **vptr = *reinterpret_cast
这个问题具体是关于非便携式MSVC ABI的东西.

我试图在明显不可移植但不是魔法的C中写出相当于C的typeid.对于Itanium ABI(在Linux / Mac上使用),它非常简单:

const std::type_info& dynamicast_typeid(void *mdo)
{
    std::type_info **vptr = *reinterpret_cast<std::type_info ***>(mdo);
    std::type_info *typeinfo_ptr = vptr[-1];
    return *typeinfo_ptr;
}

所以现在我正在看64位的MSVC ABI,然后对它说,我很难过.对于非常简单的类,以偏移0处的vfptr开头的类,它几乎和Itanium一样简单:

const std::type_info& dynamicast_typeid(void *mdo)
{
    int *rtti_complete_object_locator = ((int ***)mdo)[0][-1];
    char *result = (char *) rtti_complete_object_locator;
    result -= rtti_complete_object_locator[5];
    result += rtti_complete_object_locator[3];
    return *(std::type_info*)result;
}

(此代码基于the Wine project’s __RTtypeid.)

问题是某些C类不是以偏移0处的vfptr开头的!有时他们从vbptr开始.

struct Class1 { virtual ~Class1() {} };
struct Class2 : virtual Class1 {};

Class1以vfptr开头; Class2以vbptr开头.

当一个类以vbptr开头时,我相信它的第一个虚拟基础子对象(它在布局顺序中的第一个,因此它的“最活泼的”)总是会在其偏移0处有一个vfptr.所以如果我知道我’在处理以vbptr开头的类时,我想这样做:

const std::type_info& dynamicast_typeid_for_vbptr_class(void *mdo)
{
    int first_vbase_offset = ((int**)mdo)[0][1];
    mdo = (char*)mdo + first_vbase_offset;
    int *rtti_complete_object_locator = ((int ***)mdo)[0][-1];
    char *result = (char *) rtti_complete_object_locator;
    result -= rtti_complete_object_locator[5];
    result += rtti_complete_object_locator[3];
    return *(std::type_info*)result;
}

我已经确定了MSVC does actually generate相当于

if constexpr(IS_VBPTR_CLASS) {
        int first_vbase_offset = ((int**)mdo)[0][1];
        mdo = (char*)mdo + first_vbase_offset;
    }
    return __RTtypeid(mdo);

当编译C表达式typeid(x)时 – 其中IS_VBPTR_CLASS是伪代码,“编译器根据x的静态类型以及编译器知道每种类型的布局这一事实神奇地知道x是否具有vbptr.”

但是,在我的情况下,我不知道x的静态类型,即使我这样做,我也不知道如何找出(从C中,使用模板元编程)x是否以vbptr开头.

最后,我继续前进并稍微捏了一下

const std::type_info& dynamicast_typeid(void *mdo)
{
    while (((int**)mdo)[0][0] == 0) {
        mdo = (char *)mdo + ((int**)mdo)[0][1];
    }
    int *rtti_complete_object_locator = ((int ***)mdo)[0][-1];
    char *result = (char *)complete_object_locator;
    result -= rtti_complete_object_locator[5];
    result += rtti_complete_object_locator[3];
    return *(std::type_info*)result;
}

只发现存储在vftable中的“Class1中的Class1”的typeinfo保存了子对象类型Class1的typeinfo,而不是最派生类型的Class2!所以还有另一块拼图丢失了.

所以我的问题简而言之:给定(void *)& object_of_type_class2,如何检索typeid(Class2)?

解决方法

我有一些现在有用的东西!

函数dynamicast_to_mdo相当于dynamic_cast< void *>(p) – 它调整p以指向其最派生的对象.

函数dynamicast_typeid相当于typeid(p) – 它从p的vtable中获取typeinfo.我只是在使用我在问题中给出的软糖/黑客,我实际上不确定为什么我几个小时前得到了错误的答案;我想当我看到错误的typeinfo时,可能是因为我不小心试图将悬挂引用的typeid取为已销毁的堆栈变量.

// 64-bit MSVC ABI
void *dynamicast_to_mdo(void *p)
{
    if (((int**)p)[0][0] == 0) {
        p = (char *)p + ((int**)p)[0][1];
    }
    int *complete_object_locator = ((int ***)p)[0][-1];
    int mdoffset = complete_object_locator[1];
    void *adjusted_this = static_cast<char *>(p) - mdoffset;
    return adjusted_this;
}

// 64-bit MSVC ABI
const std::type_info& dynamicast_typeid(void *p)
{
    if (((int**)p)[0][0] == 0) {
        p = (char *)p + ((int**)p)[0][1];
    }
    int *complete_object_locator = ((int ***)p)[0][-1];

    char *result = (char *)complete_object_locator;
    result -= complete_object_locator[5];
    result += complete_object_locator[3];
    return *(const std::type_info*)result;
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读