大內高手專欄:

Metadata 的格式和意義(上)

作者:蔡學鏞

2003 年 12 月

如果你閱讀 .NET 的書,你可能會常常看到 Metadata 一詞。根據書上的說法,Metadata 可以為 .NET 帶來莫大的威力。事實上,Java 的 class 檔案內也具有 Metadata。只是 Java 與 .NET 的 metadata 結構不同,且 Java 的 metadata 不若 .NET 的完整。不過,如果你是 Java 的愛好者,你可以放心,因為預計在未來版本中,Java 也會向 .NET 看齊,提供更豐富的 Metadata。

本文章的主旨在簡單地介紹 Metadata。我會先介紹 .NET 的 Metadata 格式和意義,本文章的下篇會接著介紹 .Java 的 Metadata 格式和意義。

.NET 程式碼編譯後的檔案是 exe 檔 (或 dll 檔),完全符合原來 PE (portable executable) 檔的規範,如圖一所示:

對 1
圖 1

其中,.NET 原始程式碼所編譯出來的結果,也就是 Metadata 與 IL,都是放在 Section 中,如圖二所示:

對 2
圖 2

IL 和 Metadata 是 .NET PE 檔案內的兩大重點。Metadata 敘述靜態的結構,IL 敘述動態的程式。如果你對 IL 感興趣,請看我在台灣 MSDN 網站上發表的一篇文章,網址在:https://www.microsoft.com/taiwan/msdn/columns/DoNet/IL.htm

本文章的重點在於 Metadata。在 PE 檔案格式中,Metadata 的結構十分複雜,所以我無法在此詳細討論。一言以蔽之,Metadata 就像是一個資料庫,包含許多 table,且 table 之間可能會互相參照 (reference),也就是類似關連式資料庫的 foreign key。目前,.NET PE 檔的 Metadata 中可以使用的 table 有 44 種,如圖三所示。

對 3
圖 3

以圖四為例,class 的定義放在名為 TypeDef 的 table 中,每一筆記錄,就是一個 class 的定義。此 table 紀錄了 class 的旗標 (Flags)、class 的名稱 (Name)、名稱空間 (namespace)、繼承對象 (Extends)、欄位清單 (FieldList)、方法清單 (MethodList)。

對 4
圖 4

  • class 的旗標 (Flags) 記錄的是 public、internal、private 等資料。
  • class 的名稱 (Name) 指到外部的一個字串區塊。
  • 名稱空間 (namespace) 指到外部的一個字串區塊。
  • 繼承對象 (Extends) 指到 TypeDef 或者 TypeRef 內的另一個 class。
  • 欄位清單 (FieldList) 指到 Field table 內的某筆資料。由於一個 class 可能有多個 Field,所以從被指到的 Field 開始,一直到下一個被指到的 Field 之前,都是屬於此 class 的欄位。請看圖五的解釋。
  • 方法清單 (MethodList) 指到 Method table 內的某筆資料。作法類似欄位清單。

對 5
圖 5

這些 Metadata table 彼此之間的關係,如圖六所示。

對 6
圖 6

下面簡單地介紹這 44 個 table 中,最常見的 table:

**Module:**描述目前的模組
**ModuleRef:**描述參考的模組
**TypeDef:**描述類別 (或介面) 的定義
**TypeRef:**描述所參考到的類別 (或介面)
**Field:**描述欄位定義
**Method:**描述方法定義
**Event:**描述事件
**Property:**描述屬性
**MemberRef:**描述參考的欄位或方法
**Param:**描述方法參數的定義
**InterfaceImpl:**描述什麼類別實現了什麼介面
**Constant:**描述欄位、參數、或屬性的常數值
**CustomAttribute:**描述自行定義的屬性
**FieldMarshal:**描述 .NET 和非 .NET 的程式溝通時,欄位或參數的傳遞方式
**DeclSecurity:**紀錄對於安全性的宣告
**MethodSemantics:**描述方法和事件或屬性的關連
**MethodImpl:**描述方法的實作
**Assembly:**定義組件 (assembly)
**AssemblyRef:**描述所參考的組件
**File:**描述外部的檔案
**ExportedType:**描述在同一個組件,但在不同模組內,有哪些類別
**ManifestResource:**描述資源
**NestedClass:**描述哪些類別是其他類別的巢狀類別

由於在 PE 檔案格式中 Metadata 的組織方式複雜,所以透過 Dump 的方式來研究其 Binary Code,並非很好的作法。事實上,利用 ILDASM 更適合。ILDASM 是 .NET Framework SDK 中內附的一套程式,可以反組譯 .NET PE。命令列用法如下例所示:

ildasm /adv Hello.exe

接著,你會看到 ILDASM 的視窗,如圖七所示:

對 7
圖 7

請從 Menu 中選擇 View/MetaInfo,確定 Header 已經被勾選 (Checked),然後選擇「Show!」,即可看到下面的訊息:

Coff symbol name overhead:  0
Strings: 122(0x7a), Blobs: 40(0x28), Guids: 16(0x10), User strings: 16(0x10)
 0: Module               cRecs:    1(0x1), cbRec: 10(0xa), cbTable:    10(0xa)
 1: TypeRef              cRecs:    3(0x3), cbRec:  6(0x6), cbTable:    18(0x12)
 2: TypeDef              cRecs:    2(0x2), cbRec: 14(0xe), cbTable:    28(0x1c)
 6: Method               cRecs:    2(0x2), cbRec: 14(0xe), cbTable:    28(0x1c)
 8: Param                cRecs:    1(0x1), cbRec:  6(0x6), cbTable:     6(0x6)
10: MemberRef            cRecs:    3(0x3), cbRec:  6(0x6), cbTable:    18(0x12)
12: CustomAttribute      cRecs:    1(0x1), cbRec:  6(0x6), cbTable:     6(0x6)
32: Assembly             cRecs:    1(0x1), cbRec: 22(0x16), cbTable:    22(0x16)
35: AssemblyRef          cRecs:    1(0x1), cbRec: 20(0x14), cbTable:    20(0x14)

你可以看出來,我們的 Hello.exe 內有「#Strings」、「#Blob」、「#GUID」、「#US」,以及 9 個 Table。由此可以看出,雖然可以使用的 table 有 44 個,但是大多數的 assembly (也就是 .NET 的 exe 和 dll) 只使用一小部分的 table。

在本文章下篇中,我會接著介紹 .Java 的 Metadata 格式和意義,並與 .NET 進行整體性的比較。

意見與支援

 您有任何問題、意見或建議嗎?您可以透過下列電子郵件與作者連絡:
 xy.cai@msa.hinet.net

更多資訊

想知道大內高手專欄的其他文章嗎?請至此專欄所有列表