JIT 编译后如何(以及在​​何处)加载本机代码?

Posted

技术标签:

【中文标题】JIT 编译后如何(以及在​​何处)加载本机代码?【英文标题】:How(and where) does the native code get loaded after JIT compilation? 【发布时间】:2019-02-21 23:20:49 【问题描述】:

我有一个应用程序,用 .net(C#,64 位)编写,我还有一个 DLL(用 C++ 编写),我想在运行时加载它并从主应用程序(从 DLL 中)调用特定函数)。为此,我必须找到这个 .net 应用程序的本机代码并声明一个指向该函数的函数指针。

现在,我已经浏览了 PE 标头和元数据,找到了所需函数的 RVA,但是它让我进入了 IL(字节码)实现,而不是原生实现(在运行时调用)。经过一些调试后,我设法找到了函数的本机(ASM)代码,映射在 64 位地址空间中,我可以从 DLL 调用它,但我不知道如何动态找到这个函数(每次 IL 代码通过 JITC 运行后)。

有什么方法可以获取有关 JITC 如何/在内存中加载本机代码的信息?我在 PE 标头中找不到与此相关的任何内容。

【问题讨论】:

JIT 编译器通常将机器代码直接写入具有 write+exec 权限的动态分配内存页面,并将其视为函数指针。例如在 Windows 上使用 VirtualAllocnew + VirtualProtect。 IDK .net 如何专门处理使链接工作的事情,但您可能只会在运行时找到它,而不是文件元数据/符号表中的任何内容。 您为什么要以错误的方式执行此操作并不明显。谷歌“如何从 C++ 调用 C# 方法”以到达某个地方。 @user2864740 :我正在为一个基本的 .net 游戏制作一个外部脚本工具(用于自动化输入),所以修改 .net 源不是选项(我不想编辑可执行文件本身)。这意味着,我不能将程序集暴露给 COM,不能托管它(游戏必须正在运行),也不能“让它”导出它的功能。 这里有一个隐藏的假设:甚至有答案吗?或者这本质上是不可预测的?例如,ASLR 故意使地址随机化。但即使不是故意的,任何 Microsoft 补丁都可能使静态假设无效。此外,另一个假设是存在单个实现。我可以想象.Net 根据分析信息重新编译一个函数。 我不认为这可以被视为对此的答案,但ICorDebugFunction::GetNativeCode 实际上返回一个指向给定代码块的 JIT 方法的指针。设法让它通过管道服务器工作,我想这仅适用于严肃的调试目的,但在我找到更好的东西之前它会起作用。此外,似乎在编译时会生成某种 MethodTable,尚未对其进行研究,但在 .net 核心文档中提到了它- 【参考方案1】:

经过一番研究,我发现ICorDebugFunction::GetNativeCode 将返回 JIT 函数的地址(假设它已被编译)。设法让它通过管道服务器工作。

对于任何试图完成与我类似的事情的人,您可能想研究对象在内存中的处理方式(我想它可能会因编译器/.net 框架而有所不同),它们似乎都有指针到包含 JIT(成员)函数地址及其 RID 的表(也可以在 PE 标头中找到)。

【讨论】:

以上是关于JIT 编译后如何(以及在​​何处)加载本机代码?的主要内容,如果未能解决你的问题,请参考以下文章

CLR加载程序集代码时,JIT编译器对性能的产生的影响

如何加快20-30%的QML加载时间?

关于作用域

如何跟踪 ASP.Net JIT 编译?它很慢

无法加载JIT编译器(CLR.DLL):文件可能丢失或损坏,请重新检查或重新安装,请问这个问题怎么解决!

C#编译过程