.Net7 CLR的呼叫函式和編譯函式

江湖評談發表於2023-03-17

前言

.Net執行模型,無非就兩個過程。一個是呼叫入口函式,另外一個就是編譯入口函式。前者主呼叫,後者主編譯。


概括

一:入口函式:RunMainInternal
所有的.Net程式,包括控制檯,Web,窗體等等入口都是Main。而呼叫這個Main的就是RunMainInternal,從這個函式的名字就可以看出,它的Run就表示是執行Main函式的,而後面的Internal表示是內部的,而非外部所能呼叫,當然你用一些非常規手段,這個函式還是可以外部的呼叫的,那是後話。


二:透過LLDB看下它的堆疊

* thread #1, name = 'corerun', stop reason = step over
  * frame #0: 0x00007ffff6d53cdf libcoreclr.so`RunMainInternal
    frame #1: 0x00007ffff6d53c99 libcoreclr.so`RunMain
    frame #2: 0x00007ffff6d4ec76 libcoreclr.so`RunMain
    frame #3: 0x00007ffff6d4ea22 libcoreclr.so`RunMain
    frame #4: 0x00007ffff6d4f030 libcoreclr.so`Assembly::ExecuteMainMethod
    frame #5: 0x00007ffff6dc2b9c libcoreclr.so`CorHost2::ExecuteAssembly
    frame #6: 0x00007ffff6d1a6c1 libcoreclr.so`::coreclr_execute_assembly
    frame #7: 0x000055555556f9f9 corerun`run(config=0x00007fffffffdbc0)
    frame #9: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main
    frame #10: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl
    frame #11: 0x000055555556c0e5 corerun`_start + 37

可以看到它在Linux平臺下面,是透過Glibc OR Newlibc呼叫的。而它的前面一個函式就是RunMain,這個透過它名字也可以看到它就是執行Main函式的。


三:編譯函式PreStubWorker
這個函式顧名思義,預樁工作。就是插樁的意思。每次編譯一個函式,都會經過它。它會呼叫RyuJIT把函式編譯成機器碼,然後再返回到這個被編譯函式的函式頭地址執行。
部分程式碼:

extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, MethodDesc* pMD)
{
    PCODE pbRetVal = NULL;

    BEGIN_PRESERVE_LAST_ERROR;

    STATIC_CONTRACT_THROWS;
    STATIC_CONTRACT_GC_TRIGGERS;
    STATIC_CONTRACT_MODE_ANY;
    STATIC_CONTRACT_ENTRY_POINT;
    // 此處省略一萬字
}

可以看到它只是帶了一個MethodDesc,也就是函式的描述結構體。透過它可以找到Module,MSIL,然後進行一個編譯。


四:看下它的堆疊

 thread #1, name = 'corerun', stop reason = breakpoint 2.1
  * frame #0: 0x00007ffff6ef55d0 libcoreclr.so`::PreStubWorker
    frame #2: 0x00007ffff731e14f libcoreclr.so`CallDescrWorkerInternal
    frame #3: 0x00007ffff6faae98 libcoreclr.so`CallDescrWorkerWithHandler
    frame #4: 0x00007ffff6fabb8d libcoreclr.so`MethodDescCallSite::CallTargetWorker
    frame #5: 0x00007ffff6d2e4b3 libcoreclr.so`MethodDescCallSite::Call
    frame #6: 0x00007ffff6d53f6f libcoreclr.so`RunMainInternal
    frame #7: 0x00007ffff6d53c99 libcoreclr.so`RunMain
    frame #8: 0x00007ffff6d4ec76 libcoreclr.so`RunMain
    frame #9: 0x00007ffff6d4ea22 libcoreclr.so`RunMain
    frame #10: 0x00007ffff6d4f030 libcoreclr.so`Assembly::ExecuteMainMethod
    frame #11: 0x00007ffff6dc2b9c libcoreclr.so`CorHost2::ExecuteAssembly
    frame #12: 0x00007ffff6d1a6c1 libcoreclr.so`::coreclr_execute_assembly
    frame #13: 0x000055555556f9f9 corerun`run(config=0x00007fffffffdbc0) argv=0x00007fffffffddb8) at corerun.cpp:624:21
    frame #15: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main
    frame #16: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl
    frame #17: 0x000055555556c0e5 corerun`_start + 37

它剛好在RunMainInternal的後面,說明它是在被呼叫之後,才會進行編譯。



結尾

作者:江湖評談
image

相關文章