当 C# JIT 编译器在执行过程中将 .dll 编译为机器代码时,如何根据 .exe 的机器代码解析内存地址?

问题描述 投票:0回答:2

我正在尝试更好地了解 C# 中动态链接库的优缺点,以及它们的优缺点与 C++ 中使用动态链接库的优缺点相比。

这就是我目前困惑的地方:当一个已经运行了一段时间的C#程序去加载一个包含托管代码的.dll时,JIT编译器将.dll的MSIL编译为机器代码并放置在机器中RAM 中的代码,.dll 新编译的机器代码的内存地址如何链接到原始 .exe 的机器代码?

  1. 绝对地址 - 就像 .exe 和 .dll 中的代码在运行之前已编译为单个机器代码可执行文件一样?
  2. 通过 .dll 机器代码的加载时重定位进行链接?
  3. .dll 的机器代码被编译为位置无关代码 (PIC)?

到目前为止,我发现了这些有关机器代码 .dll 加载不同方式的有用文章: https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/ https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

...还有这个 StackOverflow 问题,关于如果 .exe 和 .dll 都是机器代码,静态和动态链接的优缺点: 静态链接与动态链接

但我一直无法找到有关这些概念如何在 .NET、MSIL 和 JIT 编译器领域应用的有用信息。

c# .net-core dll clr jit
2个回答
0
投票

以下针对我的问题的两条评论的组合提供了我需要的答案。非常感谢 Hans Passant、Charlieface 和 Jonas H. 的帮助!

来自乔纳斯 H: 抖动不编译整个程序集。方法是及时编译的,带有尚未编译的方法的存根,并且抖动正在做它的事情,修复地址等。方法源自哪个 dll 并不重要,只是运行时必须在第一次调用其中的方法时查找程序集文件。

来自查理脸: JIT 插入占位符以用于对非 JIT 函数的嵌套调用。如果仍有占位符用于调用丢失的机器代码,则可以通过进一步 JITting 嵌套调用来修复该函数的实际首次运行。所以你们的选项都不是真正正确的:使用绝对地址,除非不可用并且 JIT 需要修复它们。选项 2 和 3 在 JIT 上下文中没有任何意义:代码毕竟只在运行时编译。仅当使用提前编译时,移动机器代码才有意义(类似于预编译为机器代码的 C++)。


0
投票

我认为你从根本上误解了抖动的工作原理。

抖动以每种方法为基础,基本上是这样工作的。细节将是特定于实现的,我不是代码生成方面的专家,所以一些细节肯定是不正确的。

当遇到“调用”CLI 指令时,它将被替换为对抖动中“存根”的调用。如果/当这被称为抖动时,抖动会做一些事情:

  1. 被调用的方法是否已经编译?如果是这样,请更改原始调用以改为针对已编译的方法。
  2. 该方法的程序集是否已加载?如果没有,请找到该程序集并加载它。有一系列规则来具体管理如何完成此操作。
  3. 假设程序集已加载,将方法编译为机器代码,再次用对存根的调用替换任何“Call”指令,修复调用者地址以将其作为目标,并在方法开始时恢复执行。

最终你只会得到一堆机器代码。抖动在任何时候都不会编译整个 dll,因此无需在每个程序集级别处理调用地址。

当然,您可以创建一个执行其他操作的 CLR 实现,例如一次编译所有内容。但是,如果没有至少一些 CLR 存根来按需生成代码,这将是非常困难的,因为 .Net 包含很难以其他方式实现的泛型和反射。

© www.soinside.com 2019 - 2024. All rights reserved.