共享程序集和强命名程序集(4):“运行时”如何解析类型引用

GAC(Global Assembly Cache) 全局程序集缓存

安装公共语言运行库的每台计算机都有一个称为全局程序集缓存的机器级代码缓存。全局程序集缓存存储专门指定由计算机上的多个应用程序共享的程序集。

“运行时”如何解析类型引用

首先是一组毫无营养但是有用的代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Reflection;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Project_1
 9 {
10     public class Program
11     {
12         public static void Main(string[] args)
13         {
14             System.Console.WriteLine("HaHa");
15        }
16     }
17 }

编译这些代码并生成程序集Program.exe。运行应用程序,CLR会加载并初始化自身,读取程序集的CLR头,查找标识了应用程序的入口方法main的MethodDefToken,检索MethodDef元数据表找到方法的IL代码在文件中的偏移量,将IL代码Jit编译成本机代码,最后执行本机代码。

.method public hidebysig static void  Main(string[] args) cil managed
// SIG: 00 01 01 1D 0E
{
  .entrypoint
  // 方法在 RVA 0x2050 处开始
  // 代码大小       13 (0xd)
  .maxstack  8
  IL_0000:  /* 00   |                  */ nop
  IL_0001:  /* 72   | (70)000001       */ ldstr      "HaHa"
  IL_0006:  /* 28   | (0A)000003       */ call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  /* 00   |                  */ nop
  IL_000c:  /* 2A   |                  */ ret
} // end of method Program::Main

对这些代码进行JIT编译,CLR会检测所有类型和成员引用,加载他们的定义程序集。上述IL代码包含读System.Console.WriteLine的引用。具体地说,IL call指令引用了元数据token0A000003。该token标识MemberRef元数据表中的记录项3。CLR检查该MemberRef记录项,发现他的字段引用了TypeRef表中的记录项。按照TypeRef记录项,CLR被引到至一个AssemblyRef记录项:

AssemblyRef #1 (23000001)
-------------------------------------------------------
    Token: 0x23000001
    Public Key or Token: b7 7a 5c 56 19 34 e0 89 
    Name: mscorlib
    Version: 4.0.0.0
    Major Version: 0x00000004
    Minor Version: 0x00000000
    Build Number: 0x00000000
    Revision Number: 0x00000000
    Locale: <null>
    HashValue Blob:
    Flags: [none] (00000000)

这时CLR就知道了他需要的是哪个程序集。接下来,CLR必须定位该程序集。

解析引用的类型时,CLR可能在以下三个地方找到类型:

 ●相同文件:编译时便能发现对相同文件中的类型的访问,这称为早起绑定。类型直接从文件中加载,执行继续;

 ●不同文件,相同程序集:“运行时”确保被引用的文件在当前程序集元数据的FileDef表中,检查假造程序集清单文件的目录,加载被引用的文件。检查哈希值以确保文件完整性。发现类型的成员,执行继续;

 ●不同文件,不同程序集:如果引用的类型在其他程序集文件中,“运行时”会加载被引用程序集的清单文件。如果需要的类型不在该文件中,就继续加载包含了类型的文件。发现该类型的成员,执行继续。

 解析类型引用时有任何错误都会抛出异常。

在上例中,CLR发现System.Console在和调用者不同的程序集中实现。所以,CLR必须查找到那个程序集,加载包含应用程序集的PE文件。然后扫描清单,判断是哪个PE文件实现了类型。如果被引用的类型就在清单文件中,一切都很简单。如果类型在程序集的另一个数据结构来表示类型,JIT编译器完成Main方法的编译。最后,Main方法开始执行。绑定过程如下图所示:

还要注意的是,对于CLR,所有程序集都根据名称、版本、语言文化和公钥来识别。但GAC根据名称、版本、语言文化、公钥和CPU架构来识别。在GAC中搜索程序集时,CLR判断应用程序当前在什么类型的进程中运行,是32位x64,64位x64,还是32位ARM。然后,在GAC中搜索程序集时,CLR首先搜索程序集的CPU架构专用版本。如果没有找到符合要求的,就搜索不区分CPU的版本。

原文地址:https://www.cnblogs.com/renzhoushan/p/10397572.html