前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET9 FCall/QCall(二)

.NET9 FCall/QCall(二)

作者头像
江湖评谈
发布2024-03-14 15:48:36
880
发布2024-03-14 15:48:36
举报
文章被收录于专栏:天下风云天下风云

前言

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

QCall

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

代码语言:javascript
复制
#define DllImportEntry(impl) \
    {#impl, (void*)impl},

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

Entry的定义:

代码语言:javascript
复制
typedef struct
{
    const char* name; //函数名
    const void* method;//函数地址
} Entry;

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

当C#有如下代码:

代码语言:javascript
复制
[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汇编函数。

代码语言:javascript
复制
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#代码:

代码语言:javascript
复制
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int _CollectionCount(int generation, int getSpecialGCCount);

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

代码语言:javascript
复制
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调用约定。下面是它跳转的代码:

代码语言:javascript
复制
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大致调用过程了。

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-12,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • QCall
  • FCall
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com