il2cpp中的类型信息

Japan 乐队 Tin drum 封面

1. 从虚方法的调用看起

在 il2Cpp代码转换规则测试和整理 文章中介绍了 il2cpp 如何将 C# 代码转换成对应的 C++ 代码,其中介绍了C#中的虚函数会转成什么样的代码,以及运行时如何查找到对应的方法来进行调用,这里从虚方法的调用源码来进行分析。
当代码中有定义 virtual 函数时,C++代码会生成一个VirtActionInvoker结构体:

struct VirtActionInvoker0
{
    typedef void (*Action)(void*, const RuntimeMethod*);

    static inline void Invoke (Il2CppMethodSlot slot, RuntimeObject* obj)
    {
        const VirtualInvokeData& invokeData = il2cpp_codegen_get_virtual_invoke_data(slot, obj);
        ((Action)invokeData.methodPtr)(obj, invokeData.method);
    }
};

DisplayEntity 是一个虚函数是定义在 C# 类的一个虚函数,C# 的调用代码如下:

// 虚方法
entity.DisplayEntity();

生成的 C++ 调用代码如下:

BaseEntity_t10F88AF9625E7158E20269E2A8CEA034B0C8EEFC * L_8 = ___entity0;
NullCheck(L_8);
VirtActionInvoker0::Invoke(5 /* System.Void BaseEntity::DisplayEntity() */, L_8);

跟踪代码,最后可以看到虚函数的调用逻辑在 il2cpp 源码中的如下方法中实现:

FORCE_INLINE const VirtualInvokeData& il2cpp_codegen_get_virtual_invoke_data(Il2CppMethodSlot slot, const RuntimeObject* obj)
{
    Assert(slot != kInvalidIl2CppMethodSlot && "il2cpp_codegen_get_virtual_invoke_data got called on a non-virtual method");
    return obj->klass->vtable[slot];
}

objRuntimeObject*, 它的成员 klassIl2CppClass*, Il2CppObjectIl2CppClass 定义在文件 il2cpp-class-internals.h 中,Il2CppClass 中有一个 VirtualInvokeData 类型的数组,而虚方法就是从这个数组中拿到的 VirtualInvokeData

typedef struct Il2CppClass
{
    FieldInfo* fields; // Initialized in SetupFields
    const EventInfo* events; // Initialized in SetupEvents
    const PropertyInfo* properties; // Initialized in SetupProperties
    const MethodInfo** methods; // Initialized in SetupMethods
    Il2CppClass** nestedTypes; // Initialized in SetupNestedTypes
    Il2CppClass** implementedInterfaces; // Initialized in SetupInterfaces
    Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; // Initialized in Init
    void* static_fields; // Initialized in Init
    const Il2CppRGCTXData* rgctx_data; // Initialized in Init
    // used for fast parent checks
    Il2CppClass** typeHierarchy; // Initialized in SetupTypeHierachy
    // ....
    // 省略一大堆成员
    VirtualInvokeData vtable[IL2CPP_ZERO_LEN_ARRAY];
} Il2CppClass;

我们需要看看 VirtualInvokeData 的定义:

typedef struct VirtualInvokeData
{
    Il2CppMethodPointer methodPtr;
#if RUNTIME_MONO
    const MonoMethod* method;
#else
    const MethodInfo* method;
#endif
} VirtualInvokeData;

其中 Il2CppMethodPointer就是一个函数指针,它的定义是

typedef void (*Il2CppMethodPointer)();

MethodInfo 的定义:

typedef struct MethodInfo
{
    Il2CppMethodPointer methodPointer;
    InvokerMethod invoker_method;
    const char* name;
    Il2CppClass *klass;
    const Il2CppType *return_type;
    const ParameterInfo* parameters;

    union
    {
        const Il2CppRGCTXData* rgctx_data; /* is_inflated is true and is_generic is false, i.e. a generic instance method */
        const Il2CppMethodDefinition* methodDefinition;
    };

    /* note, when is_generic == true and is_inflated == true the method represents an uninflated generic method on an inflated type. */
    union
    {
        const Il2CppGenericMethod* genericMethod; /* is_inflated is true */
        const Il2CppGenericContainer* genericContainer; /* is_inflated is false and is_generic is true */
    };

    uint32_t token;
    uint16_t flags;
    uint16_t iflags;
    uint16_t slot;
    uint8_t parameters_count;
    uint8_t is_generic : 1; /* true if method is a generic method definition */
    uint8_t is_inflated : 1; /* true if declaring_type is a generic instance or if method is a generic instance*/
    uint8_t wrapper_type : 1; /* always zero (MONO_WRAPPER_NONE) needed for the debugger */
    uint8_t is_marshaled_from_native : 1; /* a fake MethodInfo wrapping a native function pointer */
} MethodInfo;

定义中包含了函数入口地址、函数名、返回值类型、参数信息等数据。
脚本后端为 il2cpp 时,VirtualInvokeData 包括一个 Il2CppMethodPointer 和一个 MethodInfo 指针,我们在源码中搜索一下 methodPtr 在哪里被赋值,可以查找到代码 Class.cpp 中的 SetupVTable 方法,进一步查找 SetupVTable 方法的引用,可以查到 Class.cpp 中有一个 InitLocked 方法和一堆的 SetupXXXX 方法,其中 InitLocked 方法在 Class::Init 函数中调用了,这个应该是类的初始化方法。在 InitLocked的源码基本上都是对 klass 的数据进行设置,也就是初始化 Class 的信息了,带着两个问题去分析:

1.1 这些 class 信息包括哪些内容

查看 Class::Init 的实现

static bool InitLocked(Il2CppClass *klass, const FastAutoLock& lock)
    {
        if (klass->initialized)
            return true;

        if (klass->generic_class && (klass->flags & TYPE_ATTRIBUTE_EXPLICIT_LAYOUT))
        {
            std::string message;
            message += "Could not load type '";
            message += klass->namespaze;
            message += ":";
            message += klass->name;
            message += "' because generic types cannot have explicit layout.";
            klass->has_initialization_error = true;
            Class::UpdateInitializedAndNoError(klass);
            klass->initializationExceptionGCHandle = gc::GCHandle::New(il2cpp::vm::Exception::GetTypeLoadException(message.c_str()), false);
            return false;
        }

        IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Class::Init, "Audit and compare to mono version");

        klass->init_pending = true;

        klass->genericRecursionDepth++;

        if (klass->generic_class)
            // 初始化泛型类信息
            InitLocked(GenericClass::GetTypeDefinition(klass->generic_class), lock);

        if (klass->byval_arg.type == IL2CPP_TYPE_ARRAY || klass->byval_arg.type == IL2CPP_TYPE_SZARRAY)
        {
            Il2CppClass *element_class = klass->element_class;
            if (!element_class->initialized)
                // 初始化元素类型信息
                InitLocked(element_class, lock);
        }

        // 设置接口数据
        SetupInterfacesLocked(klass, lock);

        if (klass->parent && !klass->parent->initialized)
            // 基类信息
            InitLocked(klass->parent, lock);

        // 设置方法数据
        SetupMethodsLocked(klass, lock);

        // 继承链数据
        SetupTypeHierarchyLocked(klass, lock);

        // 设置虚表数据
        SetupVTable(klass, lock);
        if (!klass->size_inited)
            // 设置字段数据
            SetupFieldsLocked(klass, lock);

        if (klass->has_initialization_error)
            return false;

        // 设置委托数据
        SetupEventsLocked(klass, lock);

        // 设置属性数据
        SetupPropertiesLocked(klass, lock);

        // 设置嵌套类型数据
        SetupNestedTypesLocked(klass, lock);

        if (klass == il2cpp_defaults.object_class)
        {
            for (uint16_t slot = 0; slot < klass->vtable_count; slot++)
            {
                const MethodInfo* vmethod = klass->vtable[slot].method;
                if (!strcmp(vmethod->name, "GetHashCode"))
                    s_GetHashCodeSlot = slot;
                else if (!strcmp(vmethod->name, "Finalize"))
                    s_FinalizerSlot = slot;
            }
            IL2CPP_ASSERT(s_FinalizerSlot > 0);
            IL2CPP_ASSERT(s_GetHashCodeSlot > 0);
        }

        if (!Class::IsGeneric(klass))
            SetupGCDescriptor(klass);

        if (klass->generic_class)
        {
            // 设置泛型实例上下文数据
            const Il2CppTypeDefinition* typeDefinition = GenericClass::GetTypeDefinition(klass->generic_class)->typeDefinition;
            if (klass->genericRecursionDepth < GenericMetadata::MaximumRuntimeGenericDepth)
                klass->rgctx_data = GenericMetadata::InflateRGCTX(typeDefinition->rgctxStartIndex, typeDefinition->rgctxCount, &klass->generic_class->context);
        }

        klass->initialized = true;
        Class::UpdateInitializedAndNoError(klass);
        klass->init_pending = false;

        ++il2cpp_runtime_stats.initialized_class_count;

        return true;
    }

大体可以看出来,Class 信息中包含如下的数据

  • Methods 数据
typedef struct MethodInfo
{
    Il2CppMethodPointer methodPointer;
    InvokerMethod invoker_method;
    const char* name;
    Il2CppClass *klass;
    const Il2CppType *return_type;
    const ParameterInfo* parameters;

    union
    {
        const Il2CppRGCTXData* rgctx_data; /* is_inflated is true and is_generic is false, i.e. a generic instance method */
        const Il2CppMethodDefinition* methodDefinition;
    };

    /* note, when is_generic == true and is_inflated == true the method represents an uninflated generic method on an inflated type. */
    union
    {
        const Il2CppGenericMethod* genericMethod; /* is_inflated is true */
        const Il2CppGenericContainer* genericContainer; /* is_inflated is false and is_generic is true */
    };

    uint32_t token;
    uint16_t flags;
    uint16_t iflags;
    uint16_t slot;
    uint8_t parameters_count;
    uint8_t is_generic : 1; /* true if method is a generic method definition */
    uint8_t is_inflated : 1; /* true if declaring_type is a generic instance or if method is a generic instance*/
    uint8_t wrapper_type : 1; /* always zero (MONO_WRAPPER_NONE) needed for the debugger */
    uint8_t is_marshaled_from_native : 1; /* a fake MethodInfo wrapping a native function pointer */
} MethodInfo;

包括 函数指针、函数名、函数所属类指针、返回值类型、参数信息等,如果是泛型方法,还包括泛型上下文数据

  • Fields 数据
typedef struct FieldInfo
{
    const char* name;       // 字段名
    const Il2CppType* type; // 类型
    Il2CppClass *parent;    // 所属类
    // 相对偏移量
    int32_t offset; // If offset is -1, then it's thread static
    uint32_t token;
} FieldInfo;

包括字段名、字段类型、字段偏移值等数据

  • Properties 数据
typedef struct PropertyInfo
{
    Il2CppClass *parent;
    const char *name;
    const MethodInfo *get;
    const MethodInfo *set;
    uint32_t attrs;
    uint32_t token;
} PropertyInfo;
  • Events 数据
typedef struct EventInfo
{
    const char* name;
    const Il2CppType* eventType;
    Il2CppClass* parent;
    const MethodInfo* add;
    const MethodInfo* remove;
    const MethodInfo* raise;
    uint32_t token;
} EventInfo;
  • Interface 数据
    修改的是 Il2CppClass 中的 implementedInterfaces 字段的数据
Il2CppClass** implementedInterfaces; // Initialized in SetupInterfaces
  • VTable 虚表数据
    修改 Il2CppClass中的 vtable 字段
VirtualInvokeData vtable[IL2CPP_ZERO_LEN_ARRAY];
  • RGCTX Data 泛型上下文数据
    修改的是 Il2CppClass 中的 rgctx_data 字段数据
const Il2CppRGCTXData* rgctx_data; // Initialized in Init
1.2 数据怎么来的,有什么用

从代码的实现来看,这些数据是 Il2Cpp 在进行代码转换时放到了 global-metadata.dat 文件中,然后在 il2cpp 运行时初始化的时候,加载到 MetadataCache 中,然后在类调用 Class::Init 方法时调用各种 Setup 方法设置到 Class 中

初始化MetadataCache

你可能感兴趣的:(il2cpp中的类型信息)