.NET9 FCall/QCall(二)

本文详细解析了.NET中QCall和FCall的内部工作机制,包括如何通过s_QCall数组查找函数地址、托管C#与CLR之间的转换过程,以及FCIMPL函数的调用流程。作者还推荐了.NET9顶级技术的学习资源。
摘要由CSDN通过智能技术生成

点击上方蓝字 江湖评谈设为关注

a2c9de591fb1375029267d85e0c2e8b5.png

前言

本篇承接上一篇:.NET9 FCall/QCall调用约定,继续源码级分析下F,C级的调用过程。

QCall

QCall在CLR里面会把函数名和函数地址放入到数组,比如Buffer_MemMove

#define DllImportEntry(impl) \
    {#impl, (void*)impl},


static const Entry s_QCall[] =
{   
   //省略部分代码,便于观察
    DllImportEntry(Buffer_MemMove)
}

Entry的定义:

typedef struct
{
    const char* name; //函数名
    const void* method;//函数地址
} Entry;

实际上s_QCall数组宏定义展开的就是包含了函数名,函数地址的数组。

当C#有如下代码:

[DllImport("QCall", CharSet = CharSet.Unicode)]
private unsafe static extern void Buffer_MemMove(byte* dest, byte* src, [NativeInteger] UIntPtr len);

CLR首先会查找s_QCall数组里名称为Buffer_MemMove的函数,然后返回这个函数的地址。接着跳转到这个函数地址,运行Buffer_MemMove。

这里的跳转函数地址,以及查找函数名是通过CLR来运行的。但是在此之前是托管的C#,切换到托管C#到CLR的正是NDirectImportThunk汇编函数。

NESTED_ENTRY NDirectImportThunk, _TEXT


        ;
        ; Allocate space for XMM parameter registers and callee scratch area.
        ;
        alloc_stack     68h


        ;
        ; Save integer parameter registers.
        ; Make sure to preserve r11 as well as it is used to pass the stack argument size from JIT
        ;
        save_reg_postrsp    rcx, 70h
        save_reg_postrsp    rdx, 78h
        save_reg_postrsp    r8,  80h
        save_reg_postrsp    r9,  88h
        save_reg_postrsp    r11,  60h


        save_xmm128_postrsp xmm0, 20h
        save_xmm128_postrsp xmm1, 30h
        save_xmm128_postrsp xmm2, 40h
        save_xmm128_postrsp xmm3, 50h
    END_PROLOGUE


        ;
        ; Call NDirectImportWorker w/ the NDirectMethodDesc*
        ;
        mov             rcx, METHODDESC_REGISTER
        call            NDirectImportWorker


        ;
        ; Restore parameter registers
        ;
        mov             rcx, [rsp + 70h]
        mov             rdx, [rsp + 78h]
        mov             r8,  [rsp + 80h]
        mov             r9,  [rsp + 88h]
        mov             r11, [rsp + 60h]
        movdqa          xmm0, [rsp + 20h]
        movdqa          xmm1, [rsp + 30h]
        movdqa          xmm2, [rsp + 40h]
        movdqa          xmm3, [rsp + 50h]


        ;
        ; epilogue, rax contains the native target address
        ;
        add             rsp, 68h


    TAILJMP_RAX
NESTED_END NDirectImportThunk, _TEXT

FCall

相对于QCall,FCall则简单许多了,它在JIT编译的时候就已经确定好了函数头位置(函数地址),在调用的时候直接跳转即可。

如下C#代码:

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int _CollectionCount(int generation, int getSpecialGCCount);

当调用GC.CollectionCount(0)的时候,托管首先跳转到

src\coreclr\System.Private.CoreLib\src\System\GC.CorrCLR.cs


public static int CollectionCount(int generation)
{
   ArgumentOutOfRangeException.ThrowIfNegative(generation);
   return _CollectionCount(generation, 0);
}

这里的_CollectionCount会直接跳转到FCIMPL函数,关于FCIMPL可以参考上一篇文章.NET9 FCall/QCall调用约定。下面是它跳转的代码:

FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount)
{
    FCALL_CONTRACT;


    //We've already checked this in GC.cs, so we'll just assert it here.
    _ASSERTE(generation >= 0);


    //We don't need to check the top end because the GC will take care of that.
    int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
    FC_GC_POLL_RET();
    return result;
}
FCIMPLEND

以上就是F,C大致调用过程了。了解更多,或者想要学习.NET9顶级技术,可以加入CLR/JIT学习圈

往期精彩回顾

面试官问.Net对象赋值为null,就会被GC回收吗?

a4b672ec09c4bda42d66015b57a0ac3b.jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值