CorFlags.exe /32BIT+ 是如何工作的?

13
我想我的问题与CLR加载程序有关。我想要了解CorFlags.exe /32BIT+功能背后的机制。
我们知道,在64位Windows上启动使用 Any CPU 标志编译的程序集时,它将以64位进程启动。如果在该程序集上运行 CorFlags /32BIT + ,则它将启动为32位进程。我认为这是一个迷人的特性。
我有很多关于它的问题:
  1. 它是如何实现的?
  2. 操作系统加载程序是否会介入?
  3. 是否可能构建自定义应用程序(我猜测是非托管的),根据需要加载32位或64位CLR?
是否有一篇文章、书籍、博客等解释此功能内部工作原理的资料?
2个回答

7

我不知道有哪个地方有很好的文档记录,但是我可以指向一个相关的MSDN文章。是的,你的推断是正确的,Windows XP及以上版本的加载器有意识地加载托管可执行文件。它会自动加载.NET加载器shim(c:\windows\system32\mscoree.dll),相关的入口点是_CorValidateImage()。链接的MSDN文章中的备注部分描述了将32位.exe文件转换为64位进程的机制:

在Windows XP及更高版本中,操作系统加载器通过检查通用对象文件格式(COFF)头中的COM描述符目录位来检查托管模块。设置位表示托管模块。如果加载器检测到托管模块,则加载MsCorEE.dll并调用_CorValidateImage,该函数执行以下操作:

  • 确认图像是有效的托管模块。
  • 将图像中的入口点更改为公共语言运行时(CLR)中的入口点。
  • 对于64位版本的Windows,通过将其从PE32转换为PE32+格式来修改内存中的图像。
  • 加载托管模块映像后返回加载器。

对于可执行映像,操作系统加载器然后调用_CorExeMain函数,而不管可执行文件中指定的入口点。对于DLL程序集映像,加载器调用_CorDllMain函数。

_CorExeMain或_CorDllMain执行以下操作:

  • 初始化CLR。
  • 从程序集的CLR标头中定位托管入口点。
  • 开始执行。

当卸载托管模块映像时,加载器调用_CorImageUnloading函数。但是,此函数不执行任何操作;它只返回。


感谢您的快速回答。这是一个很好的起点。我想了解clr如何处理.reloc节。我在sscli中进行了挖掘,主要是在pedecoder.h/pewriter.cpp中找到了答案。仍然有许多问题(例如Windows 2000 x64怎么办),但我想我会在sscli中找到答案。 - Nullptr Dev
这很简单,Windows 2000 x64 最后被发现是由伟大的白色雪人使用的。 - Hans Passant
1
哇,我想知道是否有任何方法可以利用这种“特殊意识”来为Windows创建适当的fat(本机代码)二进制文件。 - Fowl
尝试过了,没有找到答案。如果操作系统中没有安装.NET,这个魔法可能根本不起作用。这使得它对本地可执行文件毫无用处。 - hypersw

2
补充一下Hans的回答,还有一些Windows内核模式代码会响应该标志。每个加载的可执行文件都有一个与之关联的内核结构SECTION_IMAGE_INFORMATION。以下是其符号信息:
 0: kd> dt nt!_SECTION_IMAGE_INFORMATION
           +0x000 TransferAddress           : Ptr64 Void
           +0x008 ZeroBits                  : Uint4B
           +0x010 MaximumStackSize          : Uint8B
           +0x018 CommittedStackSize        : Uint8B
           +0x020 SubSystemType             : Uint4B
           +0x024 SubSystemMinorVersion     : Uint2B
           +0x026 SubSystemMajorVersion     : Uint2B
           +0x024 SubSystemVersion          : Uint4B
           +0x028 GpValue                   : Uint4B
           +0x02c ImageCharacteristics      : Uint2B
           +0x02e DllCharacteristics        : Uint2B
           +0x030 Machine                   : Uint2B
           +0x032 ImageContainsCode         : UChar
           +0x033 ImageFlags                : UChar
           +0x033 ComPlusNativeReady        : Pos 0, 1 Bit
           +0x033 ComPlusILOnly             : Pos 1, 1 Bit
           +0x033 ImageDynamicallyRelocated : Pos 2, 1 Bit
           +0x033 ImageMappedFlat           : Pos 3, 1 Bit
           +0x033 BaseBelow4gb              : Pos 4, 1 Bit
           +0x033 Reserved                  : Pos 5, 3 Bits

标志ComPlusILOnlyComPlusNativeReady与.NET有关,ComPlusILOnly仅表示程序集是否仅为CIL(不是混合或本地代码 - 在这种情况下,程序集已经是特定于架构的),而ComPlusNativeReady仅在未设置/32BIT+(较新版本的CorFlags中的32BITREQ或32BITPREF)时为1。这些标志在nt!PspAllocateProcess期间进行检查,并基于它们创建一个32位64位进程。

我写了一篇文章详细介绍了这个问题。


非常感谢!!!我在使用Windows NT/2000本地API参考手册中过时的信息计算此结构体字段的某些偏移量时感到困惑。 - Ta Thanh Dinh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接