In MSVC ABI, how can I reliably find the vtable given only (void *)?

This question is for MSVC ABI non-portable materials.

I am trying to write the C ++ equivalent typeidin explicitly non-portable, but not magical C ++. For Itanium ABI (as used on Linux / Mac) it is very simple:

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;
}

So, now I look at the 64-bit MSVC ABI, and I am stunned. For very simple classes that start with vfptr with an offset of 0, it's almost as easy as 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;
}

(This code is based on the Wine project__RTtypeid .)

The problem is that some C ++ classes do not start with vfptr at offset 0! Sometimes they start with vbptr.

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

Class1starts with vfptr; Class2starts with vbptr.

vbptr, , ( , , " " ) vfptr 0. , , , 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

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

++ typeid(x) - IS_VBPTR_CLASS ", , x vbptr x , ."

x, , , ( ++, ), 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;
}

, info, vftable Class1 2, info Class1, Class2! .

, : (void*)&object_of_type_class2, typeid(Class2)?

+4
1

-, !

dynamicast_to_mdo dynamic_cast<void*>(p) - p, .

dynamicast_typeid typeid(p) - info p vtable. fudge/hack, , , ; , info, , - , 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;
}
0

Source: https://habr.com/ru/post/1679571/


All Articles