2.4 简单看一下Metadata

现在我们知道了我们创建的是什么类型的PE文件了, 但是在Program.exe中真正是什么? 一个托管PE文件有如下四个主要部分: PE32 (+) header, CLR header, metadataIL. PE32(+) headerWindows需要的标准信息, CLR header是用于那些需要CLR才能运行的模块(托管模块)的一小块信息, 这个header包括CLR的主版本和次版本号: 一些标志, 一个MethodDef符号(后面会描述)用于表示模块的入口函数(如果这个模块是一个CUI或者GUI可执行程序), 和一个可选的强名字数字签名(3章会讨论). 最后, header还包括metadata的大小和偏移量. 你可以通过检查定义在CorHdr,h中的IMAGE_Cor20_HEADER来看看CLR header的准确格式.

Metadata是一块二进制的数据, 包含了几个表格. 3类表格: 定义表格, 引用表格, manifest表格. 2-1描述了一些常用的定义表格:

2-1 常用的metadata定义表格

Metadata定义表格的名称

描述

ModuleDef

总是包含一个条目用来标识模块, 包括模块的文件名和扩展名(没有路径)和模块的版本ID(编译器创建的GUID). 它允许文件被重新命名, 而保持原名字的一个记录. 然而, 强烈不推荐重命名一个文件, 重命名会导致CLR在运行时不能定位程序集, 所以不要这么做.

TypeDef

对定义在模块中的每一个类型都包含一个条目, 每个条目包括类型的名称, 基类型, 标志(public, private, )和它所包含的函数在MethodDef表格中的索引, 它所包含的字段在FieldDef表格中的索引, 它所包含的属性在PropertyDef表格中的索引, 和它所包含的事件在EventDef表格中的索引.

MethodDef

对定义在模块中的每个方法都包含一个条目, 每个条目包括方法的名字, 标志(private, public, virtual, abstract, static, final, ), 签名, 在模块中的偏移量(IL代码可在此处找到这个函数). 每个条目也指向ParamDef表格的一个条目, 后者记录着函数的参数信息.

FieldDef

对定义在模块中的每个字段都包含一个条目, 每个条目包括标志(private, public, ), 类型和名字.

ParamDef

对定义在模块中的每个参数都包含一个条目, 每个条目包含标志(in, out, retval, ), 类型, 和名字.

PropertyDef

对定义在模块中的每个属性都包含一个条目, 每个条目包含标志, 类型, 和名字.

EventDef

对定义在模块中的每个事件都包含一个条目, 每个条目包含标志和名字.

当编译器编译你的源代码时, 你的代码中定义的任何东西都会在表2-1中描述的一个表格中创建一个条目. 编译器也会检测到的你的代码中引用的类型, 字段, 函数, 属性, 事件, 所以也会对它们创建Metadata表格条目. 创建的metadata包括一组引用表格, 其对每个引用保存一个条目. 2-2给出了一些常见的引用metadata表格.

2-2 常用的引用metadata表格

Metadata引用表格的名称

描述

ModuleRef

对这个模块引用的类型, 都需要引用对应的实现模块PE, 都对应着这个表格中的一个条目, 每个条目包含模块的文件名和扩展名(没有路径). 这个表格被用于绑定在不同模块中实现的类型.

TypeRef

对这个模块引用的每个类型都包含一个条目, 每个条目包括类型的名字和一个能找到它的地方的引用. 如果类型是在另一个类型内实现的, 那么这个引用会指明一个TypeRef条目, 如果类型是在相同的模块中实现的, 那么引用会指明一个ModuleDef条目, 如果类型是在另一个模块中实现的, 那么引用会指明一个ModuleRef条目, 如果类型是在不同的程序集中实线的, 那么引用会指明一个AssemblyRef条目.

MemberRef

对这个模块引用的每个成员(字段和函数, 属性和事件方法)都包含一个条目, 每个条目包括成员的名字, 签名, 和指向TypeRef条目的指针(定义这个成员的那个type).

除了我在表2-12-2中列出的, 还有很多表格, 但是我只是想让你知道编译器都产生了哪些metadata信息, 早些时候, 我提到还有一组manifest metadata表格, 我将在后面讲解这件事情.

各种不同的工具允许你检查一个托管PE文件的metadata, 就我个人而言, 我比较喜欢使用ILDasm.exe, 它是IL反汇编器. 为了看到metadata表格, 执行如下的命令:

ILDasm Program.exe

这会运行ILDasm.exe, 从而载入Program.exe程序集, 为了能以可读的方式看到metadata, 选择view|MetaInfo|Show!菜单项(或者按Ctrl+M). 这回得到如下的信息:

================================================

ScopeName : Program.exe

MVID : {CA73FFE8-0D42-4610-A8D3-9276195C35AA}

================================================

Global functions

------------------------------------------------

Global fields

------------------------------------------------

Global MemberRefs

------------------------------------------------

TypeDef #1 (02000002)

------------------------------------------------

TypDefName: Program (02000002)

Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass]

[BeforeFieldInit] (00100101)

Extends : 01000001 [TypeRef] System.Object

Method #1 (06000001) [ENTRYPOINT]

------------------------------------------------

MethodName: Main (06000001)

Flags : [Public] [Static] [HideBySig] [ReuseSlot] (00000096)

RVA : 0x00002050

ImplFlags : [IL] [Managed] (00000000)

CallCnvntn: [DEFAULT]

ReturnType: Void

No arguments.

 

Method #2 (06000002)

------------------------------------------------

MethodName: .ctor (06000002)

Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName]

[RTSpecialName] [.ctor] (00001886)

RVA : 0x0000205c

ImplFlags : [IL] [Managed] (00000000)

CallCnvntn: [DEFAULT]

hasThis

ReturnType: Void

No arguments.

 

TypeRef #1 (01000001)

------------------------------------------------

Token: 0x01000001

ResolutionScope: 0x23000001

TypeRefName: System.Object

MemberRef #1 (0a000004)

------------------------------------------------

Member: (0a000004) .ctor:

CallCnvntn: [DEFAULT]

hasThis

ReturnType: Void

No arguments.

 

TypeRef #2 (01000002)

------------------------------------------------

Token: 0x01000002

ResolutionScope: 0x23000001

TypeRefName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute

MemberRef #1 (0a000001)

------------------------------------------------

Member: (0a000001) .ctor:

CallCnvntn: [DEFAULT]

hasThis

ReturnType: Void

1 Arguments

   Argument #1: I4

TypeRef #3 (01000003)

------------------------------------------------

Token: 0x01000003

ResolutionScope: 0x23000001

TypeRefName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute

MemberRef #1 (0a000002)

------------------------------------------------

Member: (0a000002) .ctor:

CallCnvntn: [DEFAULT]

hasThis

ReturnType: Void

No arguments.

TypeRef #4 (01000004)

------------------------------------------------

Token: 0x01000004

ResolutionScope: 0x23000001

TypeRefName: System.Console

MemberRef #1 (0a000003)

------------------------------------------------

Member: (0a000003) WriteLine:

CallCnvntn: [DEFAULT]

ReturnType: Void

1 Arguments

   Argument #1: String

 

Assembly

------------------------------------------------

Token: 0x20000001

Name : Program

Public Key :

Hash Algorithm : 0x00008004

Version: 0.0.0.0

Major version: 0x00000000

Minor version: 0x00000000

Build Number: 0x00000000

Revision Number: 0x00000000

Locale: <null>

Flags : [none] (00000000)

CustomAttribute #1 (0c000001)

------------------------------------------------

CustomAttribute Type: 0a000001

CustomAttributeName:

System.Runtime.CompilerServices.CompilationRelaxationsAttribute ::

  instance void .ctor(int32)

Length: 8

value : 01 00 08 00 00 00 00 00 > <

ctor args: (8)

 

CustomAttribute #2 (0c000002)

------------------------------------------------

CustomAttribute Type: 0a000002

CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute ::

instance void .ctor()

Length : 30

Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx<

: 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ceptionThrows <

ctor args: ()

AssemblyRef #1 (23000001)

------------------------------------------------

Token: 0x23000001

Public Key or Token: b7 7a 5c 56 19 34 e0 89

Name: mscorlib

Version: 2.0.0.0

Major Version: 0x00000002

Minor Version: 0x00000000

Build Number: 0x00000000

Revision Number: 0x00000000

Locale: <null>

HashValue Blob:

Flags: [none] (00000000)

 

User Strings

------------------------------------------------

70000001 : C 2) L"Hi"

 

Coff symbol name overhead: 0

================================================

================================================

================================================

幸运的是, ILDasm处理了metadata表并适当地合并了信息, 你不用解析原始的表格信息. 例如, 在上面的导出信息中, 你会看到ILDasm显示了TypeDef条目, 对应的成员定义信息显示在第一个TypeDef条目之前.

你不必完全理解你看到的所有内容, 要记住的最重要的事情是Program.exe包含一个名字为ProgramTypeDef, 这个类型标识着一个公开的密封类(public sealed class), 派生自System.Object(从另一个程序集引用的类型). Program类型还定义两个函数: Main.ctor(一个构造函数).

Main是公开的, 静态的函数, 它是IL代码(native CPU代码相对). Main有一个void返回类型, 没有参数. 构造函数(总是显示成.ctor的名称)是公开的, 它也是IL代码. 构造函数的返回类型是void, 没有参数, 有一个this指针, 其指向对象的内存, 当这个函数被调用时, 对象会在这个位置创建.

我强烈鼓励你使用ILDasm进行试验, 它能为你展示丰富的信息, 你对你看到的内容理解得越多, 你就会更好地理解CLR和它的能力. 正如你看到的, 我在本书中会常常使用ILDasm.

只是为了好玩, 让我们看看有关Program.exe程序集的统计信息. 当你选择ILDasmView|Statistics菜单项时, 下面的信息会显示出来:

File size : 3072

PE header size : 512 (496 used) (16.67%)

PE additional info : 839 (27.31%)

Num.of PE sections : 3

CLR header size : 72 ( 2.34%)

CLR meta-data size : 604 ( 19.66%)

CLR additional info : 0 ( 0.00%)

CLR method headers : 2 ( 0.07%)

Managed code : 18 ( 0.59%)

Data : 1536 ( 50.00%)

Unaccounted : -511 (-16.63%)

 

Num.of PE sections : 3

.text - 1024

.rsrc - 1024

.reloc – 512

 

CLR meta-data size : 604

Module - 1 (10 bytes)

TypeDef - 2 (28 bytes) 0 interfaces, 0 explicit layout

TypeRef - 4 (24 bytes)

MethodDef - 2 (28 bytes) 0 abstract, 0 native, 2 bodies

MemberRef - 4 (24 bytes)

CustomAttribute - 2 (12 bytes)

Assembly - 1 (22 bytes)

AssemblyRef - 1 (20 bytes)

Strings - 176 bytes

Blobs - 68 bytes

UserStrings - 8 bytes

Guids - 16 bytes

Uncategorized - 168 bytes

 

CLR method headers : 2

Num.of method bodies - 2

Num.of fat headers - 0

Num.of tiny headers – 2

 

Managed code : 18

Ave method size – 9

从这,你会看到文件的大小(字节数)和组成文件的不同部分的大小(字节数和所占的百分比). 对于这个非常小的Program.cs应用程序来说, PE headermetadata占了文件中很大一块, 实际上, IL代码只占了18个字节. 当然, 随着应用程序的增长, 它会重用很多类型, 应用工其他类型和程序集, 这会使得metadataheader的信息显著地减小(和整个文件的大小想比).

注意: ILDasm.exe有一个bug, 它会影响显示的文件大小信息. 特别地, 你不要相信未作说明的信息.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值