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

 

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