.Net Jit编译器入口分析

当你在Visual stuido 里面写完代码,摁 F5编译的时候,他的过程是,首先把C#代码编程IL代码,然后Jit接管被编译了的IL代码,调用函数,打印出结果。(QQ群676817308)

这里我们看下Jit是如何接管IL代码

接管的过程没有找到相应的代码,只能通过汇编来分析
首先,JIT会通过汇编指令Call 调用一个地址比如:
在这里插入图片描述
当我们进入到00007FFC1777CFB8
在这里插入图片描述注意箭头的指向位置,同样他会调用call 指令跳转到地址00007FFC17752208。在此之前我们看下,箭头指向的5E这个是个机器指令,当前函数被编译过了这个5E会变成5F。5E上面的E8会变成E9。81机器码是methoddesc thunk 的索引,08是precode chunk的索引。继续看地址00007FFC17752208
在这里插入图片描述
可以看到jmp 指令调用了PrecodeFixupThunk函数,继续跳转到地址07FFC1EB71940h看PrecodeFixupThunk函数实现过程
在这里插入图片描述
这个 PrecodeFixupThunk函数分别获取了precodethunkindex和methoddescthunkindex。
然后调用ThePreStub函数。
在这里插入图片描述
ThePreStub调用了PreStubWorker,后者调用了SetTargetInterlocked,来修改函数的入口地址,把编译过的机器码直接放在jmp后面,以达到native运行目的。

BOOL FixupPrecode::SetTargetInterlocked(TADDR target, TADDR expected)
{
    CONTRACTL
    {
        THROWS;         // Creating a JumpStub could throw OutOfMemory
        GC_NOTRIGGER;
    }
    CONTRACTL_END;

    INT64 oldValue = *(INT64*)this;
    BYTE* pOldValue = (BYTE*)&oldValue;

    MethodDesc * pMD = (MethodDesc*)GetMethodDesc();
    g_IBCLogger.LogMethodPrecodeWriteAccess(pMD);
    
#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
    // A different jump stub is used for this case, see Init(). This call is unexpected for resetting the entry point.
    _ASSERTE(!pMD->IsLCGMethod() || target != (TADDR)GetEEFuncEntryPoint(PrecodeFixupThunk));
#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS

    INT64 newValue = oldValue;
    BYTE* pNewValue = (BYTE*)&newValue;

    if (pOldValue[OFFSETOF_PRECODE_TYPE_CALL_OR_JMP] == FixupPrecode::TypePrestub)
    {
        pNewValue[OFFSETOF_PRECODE_TYPE_CALL_OR_JMP] = FixupPrecode::Type;

        pOldValue[offsetof(FixupPrecode, m_op)] = X86_INSTR_CALL_REL32;
        pNewValue[offsetof(FixupPrecode, m_op)] = X86_INSTR_JMP_REL32;
    }
    else if (pOldValue[OFFSETOF_PRECODE_TYPE_CALL_OR_JMP] == FixupPrecode::Type)
    {
#ifdef FEATURE_CODE_VERSIONING
        // No change needed, jmp is already in place
#else
        // Setting the target more than once is unexpected
        return FALSE;
#endif
    }
    else
    {
        // Pre-existing code doesn't conform to the expectations for a FixupPrecode
        return FALSE;
    }
	
    *(INT32*)(&pNewValue[offsetof(FixupPrecode, m_rel32)]) =
#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
        pMD->IsLCGMethod() ?
            rel32UsingPreallocatedJumpStub(&m_rel32, target, GetDynamicMethodEntryJumpStub(), true /* emitJump */) :
#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
            rel32UsingJumpStub(&m_rel32, target, pMD);

    _ASSERTE(IS_ALIGNED(this, sizeof(INT64)));
    EnsureWritableExecutablePages(this, sizeof(INT64));
    return FastInterlockCompareExchangeLong((INT64*) this, newValue, oldValue) == oldValue;
}

经过SetTargetInterlocked函数之后,被编译过的函数不需要再次进行编译,而是直接jump到被编译过的机器码。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

江湖评谈

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值