1.
COFF文件头中偏移0处的Machine指示目标机器类型(IMAGE_FILE_MACHINE_AMD64等),偏移18处的Characteristics位指示文件属性(IMAGE_FILE_32BIT_MACHINE0x0100,IMAGE_FILE_LARGE_ADDRESS_AWARE0x0020)。
但我们判断dll或exe支持的目标平台并不使用COFF头,而使用可选文件头(PE32,PE32+即位于此处),因为可选文件头用于为加载器提供信息。
可选文件头分为3个部分:标准域,windows特定域和数据目录。PE32/PE32+,由位于标准域处的首个标识幻数(Magic),长度为2,它的可能值和含义为:
位于可选文件头标准域的magic标志位的值,也确定了标准域和特定域的大小。
数据目录的第15个即CLR Runtime Header,记录了CLR运行时头部的地址和大小。
从CLR运行时偏移16byte处的uint32即Corflags。
2.
VS2012生成的程序集使用的CORFLAGS版本是2.5; 早前版本都是2.0。
2.0版本的Corflags的标识值包含:
COMIMAGE_FLAGS_ILONLY =0x00000001, COMIMAGE_FLAGS_32BITREQUIRED =0x00000002, COMIMAGE_FLAGS_IL_LIBRARY =0x00000004, COMIMAGE_FLAGS_STRONGNAMESIGNED =0x00000008, COMIMAGE_FLAGS_NATIVE_ENTRYPOINT =0x00000010,
COMIMAGE_FLAGS_TRACKDEBUGDATA =0x00010000,
它们和目标平台的关系是:
Any CPU: PE = PE32 and 32BIT = 0 x86: PE = PE32 and 32BIT = 1 64-bit: PE = PE32+ and 32BIT = 0
2.5版本中多了一个32BITPREF,含义如下。暂时未找到32BITPREF存放在哪位上(4.节已找到)。
CorFlags : Hexadecimal value, computed based on below 4 flags. ILONLY : 1 if MSIL otherwise 0 32BITREQ : 1 if 32-bit x86 only assembly otherwise 0 32BITPREF : 1 if 32-bit x86 only preferred in Any CPU architecture otherwise 0 Signed : 1 if signed with strong name otherwise 0
3.
.NET 4.5(即CLR Header 2.5)的CorFlags新增的标志位32BITPREF貌似有点坑爹,引自msdn:
Sets the 32BITPREFERRED flag. The app runs as a 32-bit process even on 64-bit platforms. Set this flag only on EXE files. If the flag is set on a DLL, the DLL fails to load in 64-bit processes, and a BadImageigeFormatException exception is thrown. An EXE file with this flag can be loaded into a 64-bit process.
首先,它说设置了该标志的应用程序即使是在64位平台上也运行在32位环境下。
其次如果把DLL项目设置了该属性的话,会导致DLL无法被64位进程加载,并抛出异常。
再其次,设置了该标志位的EXE也可以被加载到一个64位进程中。
各种不确定。
4.
CorHdr.h记录了CLR文件头的结构。
用vs2012新建一个c++ win32 控制台项目,在ConsoleApplication1.cpp头部添加
#include <CorHdr.h>
然后使用Shift+Ctrl+G打开该H文件。
该文件中有一个CorPEKind枚举
typedef enum CorPEKind { peNot = 0x00000000, // not a PE file peILonly = 0x00000001, // flag IL_ONLY is set in COR header pe32BitRequired=0x00000002, // flag 32BITREQUIRED is set and 32BITPREFERRED is clear in COR header pe32Plus = 0x00000004, // PE32+ file (64 bit) pe32Unmanaged=0x00000008, // PE32 without COR header pe32BitPreferred=0x00000010 // flags 32BITREQUIRED and 32BITPREFERRED are set in COR header } CorPEKind;
这应该就是2.5版本的CORFLAGS位的含义。
注意这个CLR文件头结构体仍然叫IMAGE_COR20_HEADER,直接COPY出来如下:
// #ManagedHeader // // A managed code EXE or DLL uses the same basic format that unmanaged executables use call the Portable // Executable (PE) format. See http://en.wikipedia.org/wiki/Portable_Executable or // http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx for more on this format and RVAs. // // PE files define fixed table of well known entry pointers call Directory entries. Each entry holds the // relative virtual address (RVA) and length of a blob of data within the PE file. You can see these using // the command // // link /dump /headers <EXENAME> // // // Managed code has defined one of these entries (the 14th see code:IMAGE_DIRECTORY_ENTRY_COMHEADER) and the RVA points // that the IMAGE_COR20_HEADER. This header shows up in the previous dump as the following line // // // Managed code is identified by is following line // // 2008 [ 48] RVA [size] of COM Descriptor Directory // // The IMAGE_COR20_HEADER is mostly just RVA:Length pairs (pointers) to other interesting data structures. // The most important of these is the MetaData tables. The easiest way of looking at meta-data is using // the IlDasm.exe tool. // // MetaData holds most of the information in the IL image. THe exceptions are resource blobs and the IL // instructions streams for individual methods. Intstead the Meta-data for a method holds an RVA to a // code:IMAGE_COR_ILMETHOD which holds all the IL stream (and exception handling information). // // Precompiled (NGEN) images use the same IMAGE_COR20_HEADER but also use the ManagedNativeHeader field to // point at structures that only exist in precompiled images. // typedef struct IMAGE_COR20_HEADER { // Header versioning DWORD cb; WORD MajorRuntimeVersion; WORD MinorRuntimeVersion; // Symbol table and startup information IMAGE_DATA_DIRECTORY MetaData; DWORD Flags; // The main program if it is an EXE (not used if a DLL?) // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint // (depricated for DLLs, use modules constructors intead). union { DWORD EntryPointToken; DWORD EntryPointRVA; }; // This is the blob of managed resources. Fetched using code:AssemblyNative.GetResource and // code:PEFile.GetResource and accessible from managed code from // System.Assembly.GetManifestResourceStream. The meta data has a table that maps names to offsets into // this blob, so logically the blob is a set of resources. IMAGE_DATA_DIRECTORY Resources; // IL assemblies can be signed with a public-private key to validate who created it. The signature goes // here if this feature is used. IMAGE_DATA_DIRECTORY StrongNameSignature; IMAGE_DATA_DIRECTORY CodeManagerTable; // Depricated, not used // Used for manged codee that has unmaanaged code inside it (or exports methods as unmanaged entry points) IMAGE_DATA_DIRECTORY VTableFixups; IMAGE_DATA_DIRECTORY ExportAddressTableJumps; // null for ordinary IL images. NGEN images it points at a code:CORCOMPILE_HEADER structure IMAGE_DATA_DIRECTORY ManagedNativeHeader; } IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;
红色标出的Flags,即记录CLR信息的位。将该值和CorPEKind枚举进行逻辑运算就能得到dll/exe的目标平台属性。
5.
在4.打开的CorHdr.h位于C:\Program Files (x86)\Windows Kits\8.0\Include\um,是windows sdk 8.0使用的版本。
6.0,7.0,7.1中的CorPEKind枚举定义如下
// PE file kind bits, returned by IMetaDataImport2::GetPEKind() typedef enum CorPEKind { peNot = 0x00000000, // not a PE file peILonly = 0x00000001, // flag IL_ONLY is set in COR header pe32BitRequired=0x00000002, // flag 32BIT_REQUIRED is set in COR header pe32Plus = 0x00000004, // PE32+ file (64 bit) pe32Unmanaged=0x00000008 // PE32 without COR header } CorPEKind;
另外在6.0的IMAGE_COR20_HEADER定义头部有一行注释称其为“COM+ 2.0 header structure”,后续版本都删掉了。
保存着.NET运行时信息的这个结构化数据,在Microsoft可执行文件和通用目标文件格式规范中称之为"CLR Runtime Header",在另一些地方又被成为 CLI Header,或者COR Header,这里又被称作COM+ 2.0 header,感觉也是醉了。
6. 32BitPre并不是0x10
在CorChr.h中定义了32BitPref的值是0x10
typedef enum CorPEKind { peNot = 0x00000000, // not a PE file peILonly = 0x00000001, // flag IL_ONLY is set in COR header pe32BitRequired=0x00000002, // flag 32BITREQUIRED is set and 32BITPREFERRED is clear in COR header pe32Plus = 0x00000004, // PE32+ file (64 bit) pe32Unmanaged=0x00000008, // PE32 without COR header pe32BitPreferred=0x00000010 // flags 32BITREQUIRED and 32BITPREFERRED are set in COR header } CorPEKind;
要注意的是,如果我们读取文件头获得了CorFlags的值corflags,如果对corflags和0x10逻辑求与,得到的并不是32BitPref。
也就是说虽然这里CorPEKind枚举定义的32BitPref是0x10,但corflags这个32位无符号整数的第9bit的含义并不是32BitPref。
可能它只是IMetaDataImport2::GetPEKind()方法的返回值,例如Assembly.ManifestModule.GetPEKind(..)返回的值之一。
通过把corflags的bit逐个打印出来,真正存放32BitPref的位像是第18位,即0x20000。试了试貌似是对的。
后来在CorChr.h中看到了COMIMAGE_FLAGS_32BITPREFERRED,也即是该值。
7.AnyCPU 32-bit Prefer 的条件不一定是PE==PE32 &&32BitReq==0&&32BitPref==1
使用VS提供的CorFlags工具查看AnyCPU 32bit Prefer的dll,得到的是:
PE:PE32 32BitReq:0 32BItPref:1
所以我们在代码中解析CorFlags位也是使用这个逻辑来判定目标平台是AnyCPU 32bit Prefer吗?
答案出人意料的为不是。通过解析CorFlags位需要使用如下逻辑:
PE==PE32 &&32BitReq==1&&32BitPref==1
这一点和CorFlags工具有冲突。之所以这样大约是为了COR2.5和COR2.0和的兼容,同时AnyCPU 32-bit Prefer的DLL又必须通知Loader是32优先。(笔者随时都在瞎猜)
结论来了:
通过读取文件头得到Corflag,判定AnyCPU 32-bit Prefer的条件是:
PE==PE32 &&32BitReq==1&&32BitPref==1
使用Corflags工具查看各标识,判定AnyCPU 32-bit Prefer的条件是:
PE==PE32 &&32BitReq==0&&32BitPref==1
8.
参考目录:
http://stackoverflow.com/tags/corflags/info
http://illuminatedcomputing.com/posts/2010/02/sorting-out-the-confusion-32-vs-64-bit-clr-vs-native-cs-vs-cpp/
http://msdn.microsoft.com/en-us/library/ms164699.aspx
http://blog.csdn.net/breaksoftware/article/category/1294269
http://stackoverflow.com/questions/18608785/how-to-interpret-the-corflags-flags/23614024#23614024
http://www1.huachu.com.cn/read/readbookinfo.asp?sectionid=1000001727
http://msdn.microsoft.com/en-us/magazine/cc301805.aspx
http://weblog.ikvm.net/2011/11/14/ManagedPEFileTypes.aspx
http://blogs.microsoft.co.il/sasha/2012/04/04/what-anycpu-really-means-as-of-net-45-and-visual-studio-11/
http://www.ntcore.com/files/dotnetformat.htm
http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311
http://msdn.microsoft.com/library/windows/hardware/gg463119.aspx