CLR里的MethodTable,MethodDescChunk,MethodDesc,FixUpPreCode都是什么意思

 

 

一:看下面一些概念

1MethodTable

MethodTable可以说在CLR里面无处不在,这个东西主要是作为对象的数据类型存在,主要包含了EEClass 

模块地址,类型名称,模块路径等。

2.EEClass 

EEclass描述了实例对象和类型里面的函数描述符起始块地址,起始块以MethodDescChunk描述。EEclass

成员变量m_pChunks存放了起始块的地址。

3.MethodDesc

看这个名称就知道,它是函数描述符。主要包含了函数名称,函数所述模块,函数是否被jit编译等属性。

4.FixUpPreCode

从字面理解修理预代码,其实它也是这个功能。就是它描述了托管函数被编译前和编译后的变化。编译前

会调用非托管PrecodeFixupThunk函数,编译后直接跳到编译的结果。

 

 

二:它们是如何组织的

MethodTable会通过CLR生成实例化对象。在这个生成的过程中,会初始化EEClass。然后会给MethodDescChunks

以及MethodDesc分配相应的地址,MethodDesc跟MethodDescChunks地址是连接在一起的,类似于MethodDescChunks

->MethodDes(1)->MehtodDesc(2)->MethodDesc(3)...... 这种形式。下面就是把MethodDescChunks当MethodDesc分配

完成的时候,会遍历MethodDesc,没遍历一个,就会出初始化一个FixUpPreCode结构体。用以描述函数没被编译之前

的状态。他们的关联方式如下图所示:

 

 

 

 

三:CLR是如何被运行出来的

作为一个.Net 程式核心部分,这一块是一个重点也是一个难点

主要的步骤有以下几步:

1.CLR会加载当前项目bin/debug/文件夹下面的【目名称.ConsoleApp15.runtimeconfig.json】的这个文件,以读取runtime 的配置

2.CLR 会获取当前runtime运行时的指针

3.通过runtime运行时指针函数,传递进去需要加载的模块,类名称,以及函数名称,获取到需要调用的函数指针

4.获取到函数指针之后,就可以释放掉上面运行步骤所占用的内存

5.调用获取到的函数指针调用函数,比如Main函数,这个是程式入口。

到了这一步不多说了,因为所有的.net程序都是从Main函数开始的。这样流程就会被C#代码所接管。

 

 

四:代码是如何构造的

由于CLR代码长达几十万行甚至几百万行,所以无法一一展示。

这里取一部分看看是如何执行的

1.MethodTable的构建过程(MethodTablebuilder.cpp 12163行)

 

    MethodTableBuilder builder(
        NULL,
        pClass,
        &GetThread()->m_MarshalAlloc, 
        pamTracker);

    pMT = builder.BuildMethodTableThrowing(
        pAllocator, 
        pLoaderModule, 
        pModule, 
        cl, 
        pInterfaceBuildInfo, 
        pLayoutRawFieldInfos, 
        pParentMethodTable, 
        &genericsInfo, 
        parentInst, 
        (WORD)cInterfaces);

    END_SO_INTOLERANT_CODE;
    RETURN(TypeHandle(pMT));

 

2.EEclass构建过程(methodtablebuilder.cpp 11957行)

    EEClass * pClass = MethodTableBuilder::CreateClass(
        pModule, 
        cl, 
        fHasLayout, 
        fIsDelegate, 
        fIsEnum, 
        &genericsInfo, 
        pAllocator, 
        pamTracker);

3.MethodDescchunks和MethodDesc的构建过程是在buildmethodtablethrowing函数里面调用    AllocAndInitMethodDescs();,先看看    AllocAndInitMethodDescs();函数(methodtablebuilder.cpp 1666行)

VOID MethodTableBuilder::AllocAndInitMethodDescs()
{
    STANDARD_VM_CONTRACT;
   if (sizeOfMethodDescs != 0)
    {
        AllocAndInitMethodDescChunk(startIndex, NumDeclaredMethods() - startIndex, sizeOfMethodDescs);
    }
}

4.继续看 AllocAndInitMethodDescChunk函数(methodtablebuilder.cpp 6836行),很明显看到了里面构建了methoddescchunks和methoddesc。

VOID MethodTableBuilder::AllocAndInitMethodDescChunk(COUNT_T startIndex, COUNT_T count, SIZE_T sizeOfMethodDescs)
{
    CONTRACTL {
        STANDARD_VM_CHECK;
        PRECONDITION(sizeOfMethodDescs <= MethodDescChunk::MaxSizeOfMethodDescs);
    } CONTRACTL_END;

    void * pMem = GetMemTracker()->Track(
        GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(TADDR) + sizeof(MethodDescChunk) + sizeOfMethodDescs)));

    // Skip pointer to temporary entrypoints
    MethodDescChunk * pChunk = (MethodDescChunk *)((BYTE*)pMem + sizeof(TADDR));

    COUNT_T methodDescCount = 0;

    SIZE_T offset = sizeof(MethodDescChunk);

#ifdef _PREFAST_ 
#pragma warning(push)
#pragma warning(disable:22019) // Suppress PREFast warning about integer underflow
#endif // _PREFAST_
    for (COUNT_T i = 0; i < count; i++)
#ifdef _PREFAST_ 
#pragma warning(pop)
#endif // _PREFAST_

    {
        bmtMDMethod * pMDMethod = (*bmtMethod)[static_cast<SLOT_INDEX>(startIndex + i)];

        MethodDesc * pMD = (MethodDesc *)((BYTE *)pChunk + offset); 

        pMD->SetChunkIndex(pChunk);

        InitNewMethodDesc(pMDMethod, pMD);

#ifdef _PREFAST_ 
#pragma warning(push)
#pragma warning(disable:22018) // Suppress PREFast warning about integer underflow
#endif // _PREFAST_
        offset += pMD->SizeOf();
#ifdef _PREFAST_ 
#pragma warning(pop)
#endif // _PREFAST_

        methodDescCount++;

        // If we're a value class, we want to create duplicate slots
        // and MethodDescs for all methods in the vtable
        // section (i.e. not non-virtual instance methods or statics).
        // In the name of uniformity it would be much nicer
        // if we created _all_ value class BoxedEntryPointStubs at this point.
        // However, non-virtual instance methods only require unboxing
        // stubs in the rare case that we create a delegate to such a
        // method, and thus it would be inefficient to create them on
        // loading: after all typical structs will have many non-virtual
        // instance methods.
        //
        // Unboxing stubs for non-virtual instance methods are created
        // in code:MethodDesc::FindOrCreateAssociatedMethodDesc.

        if (NeedsTightlyBoundUnboxingStub(pMDMethod))
        {
            MethodDesc * pUnboxedMD = (MethodDesc *)((BYTE *)pChunk + offset); 

            //////////////////////////////////
            // Initialize the new MethodDesc

            // <NICE> memcpy operations on data structures like MethodDescs are extremely fragile
            // and should not be used.  We should go to the effort of having proper constructors
            // in the MethodDesc class. </NICE>

            memcpy(pUnboxedMD, pMD, pMD->SizeOf());

            // Reset the chunk index
            pUnboxedMD->SetChunkIndex(pChunk);

            if (bmtGenerics->GetNumGenericArgs() == 0) {
                pUnboxedMD->SetHasNonVtableSlot();
            }

            //////////////////////////////////////////////////////////
            // Modify the original MethodDesc to be an unboxing stub

            pMD->SetIsUnboxingStub();

            ////////////////////////////////////////////////////////////////////
            // Add the new MethodDesc to the non-virtual portion of the vtable

            if (!bmtVT->AddUnboxedMethod(pMDMethod))
                BuildMethodTableThrowException(IDS_CLASSLOAD_TOO_MANY_METHODS);

            pUnboxedMD->SetSlot(pMDMethod->GetUnboxedSlotIndex());
            pMDMethod->SetUnboxedMethodDesc(pUnboxedMD);

            offset += pUnboxedMD->SizeOf();
            methodDescCount++;
        }
    }
    _ASSERTE(offset == sizeof(MethodDescChunk) + sizeOfMethodDescs);

    pChunk->SetSizeAndCount((ULONG)sizeOfMethodDescs, methodDescCount);

    GetHalfBakedClass()->AddChunk(pChunk);
}

5.关于构建fixupprecode (methodtablebuilder.cpp 10497行)

   {
        for (MethodDescChunk *pChunk = GetHalfBakedClass()->GetChunks(); pChunk != NULL; pChunk = pChunk->GetNextChunk())
        {
            // Make sure that temporary entrypoints are create for methods. NGEN uses temporary
            // entrypoints as surrogate keys for precodes.
            pChunk->EnsureTemporaryEntryPointsCreated(GetLoaderAllocator(), GetMemTracker());
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章