第 43 章 目的檔與執行檔

43.1 前言

連結器產生的檔案是執行檔,而執行作業系統需要二進位檔,如何將執行檔轉換成二進位檔是這一章的目的。 這章可以幫助讀者了目的檔,執行檔和二進位檔之間的差異,也會更了解程式碼編譯成作業系統的過程。 這一部份的內容,讀者若沒有興趣,可以略過,因為書附的軟體已經可以應付作業系統開發的需要。


43.2 目的檔

目的檔的內容是用 COFF 格式填寫,COFF 是 COMMOM OBJECT FILE FORMAT 的簡寫,中文的意思是通用目的檔格式。 當程式碼還是 C 語言或組合語言的原始碼的時候,需要用編譯器將這原始程式碼編譯成目的檔(OBJ FILE)。 目的檔的內容有編譯後的機械碼、已初始化的資料記憶體空間、未初始化的資料記憶體空間以及要引入引出的符號表。 引出的符號表是指當程式原始碼檔案中的函式或變數要給別的檔案的程式碼使用時,必須做符號表引出的動作。 相對地,要使用別的程式碼檔案的變數或函式時,就要做引入該程式的符號表的動作。

在 C 語言中,通常會在標頭檔中做引出和引入的動作,例如 EXTERN FUNCTION()。 這樣的動作,當程式碼看到彼此的標頭檔時就可以引用彼此的變數和函式。

在組合語言中,以 NASM 為例,使用 GLOBAL 把 變數引出給別的程式檔案使用,使用 EXTERN 將變數引入給組合語言程式檔案自己使用。符號的引出與 引入的動作會紀錄在目的檔中,當許多的目的檔要連結成執行檔時,連結器 (LINKER) 就會把它們的符 號賦予實際的值,並將各個目的檔接駁在一起,成為單一個檔案,並命命為執行檔。

COFF 檔內容:

欄位名稱說明
COFF HEADER檔頭
SECTION TABLE區段表
RAW DATA區段資料區
SYMBOL TABLE符號表
STRING TABLE字串表

43.2.1 目的檔範例

這裡以 MAIN.C 的目的檔 MAIN.O 來說明 COFF 的應用。

MAIN.C 的程式碼只有短短幾行,除了 Main() 是本檔的函式之外,其餘的都是從外 面引入的函式和變數。外來的函式和變數有OsInit、OsTask、CreatRootTask、RootTaskStack、 RootTaskName、OsStart等 6 個符號。在後面的章節中,會講解這幾個符號在目的檔中安置的方法。 MAIN.O 的檔案大小為 0x278 位元組,這個目的檔雖然不大,在解釋 COFF 的原理已經足夠。

MAIN.C 的檔案內容:

01 #include "includes.h"
02 
03 void Main(void){
04     OsInit();
05     OsTaskCreat(RootTask,ROOT_TASK_STACK_SIZE,&RootTaskName,0);
06     OsStart();
07 }

MAIN.O 的檔案內容:

MAIN.O

43.2.1.1 目的檔的檔頭

COFF HEADER 內容定義,共 20 位元組:

欄位名稱位元組數說明
MACHINE TYPE2機械型號代碼,指CPU 的類型。
NUMBER OF SECTIONS2區段數目
TIME DATE STAMP4檔案建立時間(秒),從 1970.01.01 00:00 開始算起。
POINTER TO SYMBOL TABLE4指標指向符號表位址
NUMBER OF SYMBOLS4符號表中的符號數目
SIZE OF OPTIONAL HRADER2OPTIONAL HEADER 位元組數
CHARACTERISTICS2目的檔特徵值

MAIN.O 的 COFF 檔頭:

0x00~0x0f 4C 01 03 00 00 00 00 00 F6 00 00 00 0F 00 00 00 
0x10~0x13 00 00 04 01

MAIN.O 的 COFF HEADER 的說明:

範圍欄位值(LSB~MSB)說明
0x00~0x014C 010x014C 是 i386 的 機械型號代碼
0x02~0x0303 000x0003 表示有 3 個區段。
0x04~0x0700 00 00 00 檔案建立時間為 0x00000000 ,表示沒有檔案建立時間記錄
0x08~0x0bF6 00 00 00 表示符號表開始位址為 0x000000F6
0x0c~0x0f0F 00 00 00 表示符號表中有 15 個符號
0x10~0x1100 00表示 COFF HEADER 中的 OPTIONAL HEADER 大小為 0 位元組。
0x12~0x1304 010x0104 表示是此目的檔為32位元的架構,也沒有 COFF 的 LINE NUMBER。

43.2.1.2 目的檔的區段表

MAIN.O 的 SECTION TBALE,又名 SECTION HEADERS。從前面的 COFF HEADER 中,得知 區段數目為 3 個區段,每個區段是 40 位元組,所以這個區域的位元組數為 3*40 共120位元組。

區段檔頭 (SECTION HEADER) 的內容定義:

欄位名稱位元組數說明
NAME8區段名稱
VIRTUAL SIZE4區段虛體積,區段載入記憶體時的空間大小。
VIRTUAL ADDRESS4區段虛位址
SIZE OF RAW DATA4區段資料大小,指此區段在此目的檔中的實際大小。
POINTER OF RAW DATA4區段資料位址,指此區段在此目的檔中的位址。
POINTER TO RELOCATIONS4區段重新定址指標
POINTER TO LINE NUMBERS4LINE NUMBER 指標
NUMBER OF RELOCATIONS2區段重新定址指標數目
NUMBER OF LINE NUMBERS2LINE NUMBER 數目
CHARACTERISTICS4區段特徵值

43.2.1.2.1 第一個區段檔頭

區段檔頭的內容:

0x14~0x23 2E 74 65 78 74 00 00 00 00 00 00 00 00 00 00 00
0x24~0x33 38 00 00 00 8C 00 00 00 C4 00 00 00 00 00 00 00
0x34~0x3B 06 00 00 00 20 00 30 60

區段檔頭的說明:

範圍內容說明
0x14~0x1B2E 74 65 78 74 00 00 00區段名稱為 ".text",這是存放可執行的程式碼的區段。
0x1C~0x1F00 00 00 00區段虛體積為 0。
0x20~0x2300 00 00 00區段虛位址為 0。
0x24~0x2738 00 00 00區段資料長度為 0x38 位元組。
0x28~0x2B8C 00 00 00區段資料位址從 0x8C 開始。
0x2C~0x2FC4 00 00 00區段重新定址指標指向 0xC4,此指標用來指定此目的檔的資料要坐落於映像檔的何處使用。
0x30~0x3300 00 00 00LINE NUMBER 指標為空指標 (NULL)。
0x34~0x3505 00區段重新定址指標數目為 5。
0x36~0x3700 00LINE NUMBER 數目為 0。
0x38~0x3B20 00 30 60區段特徵值為 0x60300020,
其中 0x00000020 的意思是區段含有可執行碼,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料能像程式碼般被執行,也可以被讀取。

從第一個區段檔頭,得到程式碼的位址範圍是 0x8C~0xC3 共 0x38 位元組。

目的檔中的執行碼範圍 0x8C~0xC3:

MAIN.O

43.2.1.2.2 第二個區段檔頭

0x3C~0x4B 2E 64 61 74 61 00 00 00 00 00 00 00 00 00 00 00
0x4C~0x5B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x5C~0x63 00 00 00 00 40 00 30 C0

區段檔頭的說明:

範圍內容(HEX)說明
0x3C~0x432E 64 61 74 61 00 00 00區段名稱為 ".data",這是存放已初始化的變數的區段。
0x44~0x4700 00 00 00區段虛體積為 0
0x48~0x4B00 00 00 00區段虛位址為 0
0x4C~0x4F00 00 00 00區段資料長度為 0x00 位元組
0x50~0x5300 00 00 00區段資料位址從 0x00 開始
0x54~0x5700 00 00 00區段重新定址指標值 0x00
0x58~0x5B00 00 00 00LINE NUMBER 指標為空指標 (NULL)
0x5C~0x5D00 00區段重新定址指標數目為 0x0000
0x5E~0x5F00 00LINE NUMBER 數目為 0
0x60~0x6340 00 30 C0區段特徵值為 0xC0300040,
其中 0x00000040 的意思是區段含有已初始化的資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料可以被讀取。

第二個區段沒有任何資料。為什麼區段的內容是空的,卻還是存在於區段表的現象, 有可能是編譯器的基本思考邏輯就是假定一支程式基本上都要有可執行程式碼區段、已 初始化資料區段和未初始化資料區段。所以即使區段內容是空的,區段檔頭的資料結構卻早已生成, 然後就直接的寫進區段表中了。


43.2.1.2.3 第三個區段檔頭

第三個區段並沒有任何實質的內容。為什麼區段的內容是空的,卻還是存在於區段表的理由和第二個區段一樣。

0x64~0x73 2E 62 73 73 00 00 00 00 00 00 00 00 00 00 00 00
0x74~0x83 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x84~0x8B 00 00 00 00 80 00 30 C0

區段檔頭的說明:

範圍內容(HEX)說明
0x64~0x6B2E 62 73 73 00 00 00 00區段名稱為 ".bss",這是存放未初始化的變數的區段。
0x6C~0x6F00 00 00 00區段虛體積為 0
0x70~0x7300 00 00 00區段虛位址為 0
0x74~0x7700 00 00 00區段資料長度為 0x00 位元組
0x78~0x7C00 00 00 00區段資料位址從 0x00 開始
0x7C~0x7F00 00 00 00區段重新定址指標值 0x00
0x80~0x8300 00 00 00LINE NUMBER 指標為空指標 (NULL)
0x84~0x8500 00區段重新定址指標數目為 0x0000
0x86~0x8700 00LINE NUMBER 數目為 0
0x88~0x8B80 00 30 C0區段特徵值為 0xC0300080,
其中 0x00000080 的意思是區段含有未初始化的資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料可以被讀取。

43.2.1.3 目的檔的符號表

目的檔檔頭內有符號表的位址值 0xF6。 符號數目共 15 個,符號表的長度為 15*18 位元組,即 270 位元組。

符號 (SYMBOL TABLE) 的格式:

欄位名稱位元組數說明
NAME8區段名稱,如果前4位元組都為 0 時,區段名稱為字串表加後4位元的位址處。
VALUE4重定址位址值或其他意義。
SECTION NUMBER2區段號碼。
TYPE2符號型態,0x20表示是函式,0x00表示不是函式。
STORAGE CLASS1儲存類別。
NUMBER OF AUX SYMBOLS1輔助符號表數目。

43.2.1.3.1 第 1 個符號

符號內容:

0x0F6~0x105 2E 66 69 6C 65 00 00 00 00 00 00 00 FE FF 00 00
0x106~0x107 67 01

符號說明:
字串 .file 是編譯器的特殊符號名稱。

範圍內容(HEX)說明
0x0F6~0x0FD2E 66 69 6C 65 00 00 00符號表名稱 ".file"。
0x0FE~0x10100 00 00 00重定址位址值為 0。
0x102~0x103FE FF區段號碼為 -2 (0xFFFE),意思是提供一般用途的偵錯資訊。
微軟將它與 ".file" 符號共用。
0x104~0x10500 00非函式符號。
0x10667儲存類別為 0x67。
0x10701輔助符號數目為 1。

43.2.1.3.2 第 2 個符號

符號內容:

0x108~0x117 6D 61 69 6E 2E 63 00 00 00 00 00 00 00 00 00 00
0x118~0x119 00 00

符號說明:
此符號為前一個符號的輔助符號,字串 main.c 是檔案名稱。

範圍內容(HEX)說明
0x108~0x10F6D 61 69 6E 66 2E 63 00符號表名稱 "main.c"。
0x110~0x11300 00 00 00重定址位址值為 0。
0x114~0x11500 00區段號碼 0x0000。
0x116~0x11700 00非函式符號。
0x11800儲存類別為 0。
0x11900輔助符號數目為 0。

43.2.1.3.3 第 3 個符號

符號內容:

0x11A~0x129 5F 4D 61 69 6E 00 00 00 00 00 00 00 01 00 20 00
0x12A~0x12B 02 01

符號說明:
字串 _Main 要引出的符號,函式 Main。

範圍內容(HEX)說明
0x11A~0x1215F 4D 61 69 6E 00 00 00 00符號表名稱 "_Main"。
0x122~0x12500 00 00 00重定址位址值為 0。
0x126~0x12701 00區段號碼 1。
0x128~0x12920 00為函式符號。
0x12A02儲存類別為 2。
0x12B01輔助符號數目為 1。

43.2.1.3.4 第 4 個符號

符號內容:

0x12C~0x13B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x13C~0x13D 00 00

符號說明:
這是一個空符號。筆者不懂這個符號存在的原因。

範圍內容(HEX)說明
0x12C~0x13300 00 00 00 00 00 00 00符號表名稱 NULL。
0x134~0x13700 00 00 00重定址位址值為 0。
0x138~0x13901 00區段號碼 1。
0x13A~0x13B00 00非函式符號。
0x13C00儲存類別為 0。
0x13D00輔助符號數目為 0。

43.2.1.3.5 第 5 個符號

符號內容:

0x13E~0x14D 2E 74 65 78 74 00 00 00 00 00 00 00 01 00 00 00
0x14E~0x14F 03 01

符號說明:
字串 .text 是可執行程式碼的區段名稱。

範圍內容(HEX)說明
0x13E~0x1452E 74 65 78 74 00 00 00符號表名稱 ".text"。
0x146~0x00900 00 00 00重定址位址值為 0。
0x14A~0x00B01 00區段號碼 1。
0x14C~0x00D00 00非函式符號。
0x14E03儲存類別為 3。
0x14F01輔助符號數目為 1。

43.2.1.3.6 第 6 個符號

符號內容:

0x150~0x15F 36 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00
0x160~0x161 00 00

符號說明:
筆者不知道這個符號的用途,看起來應該是符號 .text 的輔助符號,但筆者看不懂符號表的名稱。

範圍內容(HEX)說明
0x150~0x15736 00 00 00 05 00 00 00符號表名稱 6。
0x158~0x15B00 00 00 00重定址位址值為 0。
0x15C~0x15D00 00區段號碼 0x0000。
0x15E~0x15F00 00非函式符號。
0x16000儲存類別為 0x00。
0x16100輔助符號數目為 0。

43.2.1.3.7 第 7 個符號

符號內容:

0x162~0x171 2E 64 61 74 61 00 00 00 00 00 00 00 02 00 00 00
0x172~0x173 03 01

符號說明:
字串 .data 是已初始化資料的區段名稱。

範圍內容(HEX)說明
0x162~0x1692E 64 61 74 61 00 00 00符號表名稱 ".data"
0x16A~0x16D00 00 00 00重定址位址值為 0。
0x16E~0x16F02 00區段號碼 2。
0x170~0x17100 00非函式符號。
0x17203儲存類別為 3。
0x17301輔助符號數目為 1。

43.2.1.3.8 第 8 個符號

符號內容:

0x174~0x183 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x184~0x185 00 00

符號說明:
這個符號是個空符號 (NULL)。為什麼會有這個符號的原因,筆者猜,可能是區段的符號 .data 內假定至少有一 個符號,但事實上並沒有符號在 .data 的區段內,所以編譯器就寫一個空符號在這裡。無論如何,這個目的檔的相容性看起來是沒有問題的。

範圍內容(HEX)說明
0x174~0x17B00 00 00 00 00 00 00 00符號表名稱 NULL。
0x17C~0x17F00 00 00 00重定址位址值為 0。
0x180~0x18100 00區段號碼 0。
0x182~0x18300 00非函式符號。
0x18400儲存類別為 0。
0x18500輔助符號數目為 0。

43.2.1.3.9 第 9 個符號

符號內容:

0x186~0x195 2E 62 73 73 00 00 00 00 00 00 00 00 03 00 00 00
0x196~0x197 03 01

符號說明:
字串 .bss 是已初始化資料的區段名稱。

範圍內容(HEX)說明
0x186~0x18D2E 62 73 73 00 00 00 00符號表名稱 ".bss"。
0x18E~0x19100 00 00 00重定址位址值為 0。
0x192~0x00303 00區段號碼 3。
0x194~0x00500 00非函式符號。
0x19603儲存類別為 0。
0x19701輔助符號數目為 1。

43.2.1.3.10 第 10 個符號

符號內容:

0x198~0x1A7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1A8~0x1A9 00 00

符號說明:
這個符號是個空符號 (NULL)。

範圍內容(HEX)說明
0x198~0x19F00 00 00 00 00 00 00 00符號表名稱 NULL
0x1A0~0x1A300 00 00 00重定址位址值為 0
0x1A4~0x1A500 00區段號碼 0x0000
0x1A6~0x1A700 00非函式符號
0x1A800儲存類別為 0x00
0x1A900輔助符號數目為 0

43.2.1.3.11 第 11 個符號

符號內容:

0x1AA~0x1B9 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00
0x1BA~0x1BB 02 00

符號說明:
這個符號的字串是在字串表的第 4 位元組,即字串 _RootTaskName ,是根任務的名稱字串。

範圍內容(HEX)說明
0x1AA~0x1B100 00 00 00 04 00 00 00符號表名稱是在字串表的第 4 位元組,即字串 _RootTaskName。
0x1B2~0x1B580 00 00 00重定址位址值為 0x00000080
0x1B6~0x1B700 00區段號碼 0。
0x1B8~0x1B900 00非函式符號。
0x1BA02儲存類別為 2。
0x1BB00輔助符號數目為 0。

43.2.1.3.12 第 12 個符號

符號內容:

0x1BC~0x1CB 5F 4F 73 53 74 61 72 74 00 00 00 00 00 00 20 00
0x1CC~0x1CD 02 00

符號說明:
字串 _OsStart 是啟動根任務函式的名稱,這是是一個函式符號。

範圍內容(HEX)說明
0x1BC~0x1C35F 4F 73 53 74 61 72 74符號表名稱 "_OsStart"。
0x1C4~0x1C700 00 00 00重定址位址值為 0。
0x1C8~0x1C900 00區段號碼 0。
0x1CA~0x1CB20 00為函式符號。
0x1CC02儲存類別為 0。
0x1CD00輔助符號數目為 0。

43.2.1.3.13 第 13 個符號

符號內容:

0x1CE~0x1DD 00 00 00 00 12 00 00 00 00 00 00 00 00 00 20 00
0x1DE~0x1DF 02 00

符號說明:
這個符號的字串是在字串表的第 18 位元組,即字串 _RootTask ,是根任務函式的名稱字串。

範圍內容(HEX)說明
0x1CE~0x1D500 00 00 00 12 00 00 00符號表名稱是在字串表的第 18 位元組,即字串 _RootTask。
0x1D6~0x1D900 00 00 00重定址位址值為 0。
0x1DA~0x1DB00 00區段號碼 0。
0x1DC~0x1DD20 00函式符號。
0x1DE02儲存類別為 2。
0x1DF00輔助符號數目為 0。

43.2.1.3.14 第 14 個符號

符號內容:

0x1E0~0x1EF 00 00 00 00 1C 00 00 00 00 00 00 00 00 00 20 00
0x1F0~0x1F1 02 00

符號說明:
這個符號的字串是在字串表的第 28 位元組,即字串 _RootTaskCreat ,是根任務堆疊的名稱字串。

範圍內容(HEX)說明
0x1E0~0x1E700 00 00 00 1C 00 00 00符號表名稱是在字串表的第 32 位元組,即字串 _RootTaskCreat。
0x1E8~0x1EB00 00 00 00重定址位址值為 0。
0x1EC~0x1ED00 00區段號碼 0。
0x1EE~0x1EF20 00函式符號。
0x1F002儲存類別為 2。
0x1F100輔助符號數目為 0。

43.2.1.3.15 第 15 個符號

符號內容:

0x1F2~0x201 5F 4F 73 49 6E 69 74 00 00 00 00 00 00 00 20 00
0x202~0x203 02 00

符號說明:
字串 _OsInit 是多工核心初始化函式的名稱,這是是一個函式符號。

範圍內容(HEX)說明
0x1F2~0x1F95F 4F 73 49 6E 69 74 00符號表名稱 "_OsInit"。
0x1FA~0x1FD00 00 00 00重定址位址值為 0
0x1FE~0x1FF00 00區段號碼 0x0000
0x200~0x20120 00函式符號
0x20202儲存類別為 0x00
0x20300輔助符號數目為 0

至此,所有的符號都已經找到。 可以觀察到符號表中,有些符號是給區段名稱用的符號、有些符號是空的符號。 在符號的次序上,分檔案符號、區段符號、變數符號和函式符號。 基本上,所有的要引出或引入的符號都可以在符號表中找到。 在目的檔連結成執行檔的過程中,符號表的符號都會替換成真實的位址值,整併到一個完整的映像中,形成最後的執行檔。


43.2.1.3 目的檔的字串表

字串表緊接在符號表的後面,字串表的長度紀錄在字串表的第 1 個位元組的位址上,字串表的位址算法是 0xF6+15*18, 即第 0x204 位元組的 0x29 。字串表是符號表的輔助,當符號表的字串長度太長時,就要把該字串拿到字串表來放,而符 號表本身就會有指標找到字串表的位置取用字串。這個目的檔的字串表裡面共有 3 個字串,分別是給符號 11、13、14 使用。


43.2.1.3.1 第一個字串

0x208~0x215 5f 52 6f 6f 74 54 61 73 6b 4e 61 6d 65 00

說明內容為字串 _RootTaskName。

43.2.1.3.2 第二個字串

0x216~0x21F 5f 52 6f 6f 74 54 61 73 6b 00

說明內容為字串 _RootTask。

43.2.1.3.3 第三個字串

0x220~0x22C 5f 4f 73 54 61 73 6b 43 72 65 61 74 00

說明內容為字串 _OsTaskCreat。

43.3 執行檔

OS KERNEL 的 C 語言程式碼部份是用 MINGW GCC 編譯,組合語言部份都是使用 NASM 編譯。這兩種編 譯器編出來的檔案是目的檔(OBJ FILE),所以當編譯器編譯完成的時候,會產生很多個目的檔。基本上 ,編譯器根據 MAKEFILE 的描述,對所有的原始碼檔案都會產生一個同樣名稱目的檔。把所有的檔 案編譯完成後,還需要透過連結器(LD.EXE in MINGW GCC) 把各個目的檔重組成執行檔,執行檔的檔尾 是 EXE。這裡的執行檔的格式和 MS-DOS 的執行檔格式一樣,起頭都是 'MZ'。只是用 GCC 的連結器編 出來的執行檔不能在 MS-DOS 或 MS-WINDOWS 下執行。這個執行檔必須轉成二進位檔,然後放到 RAM 中執行。

執行檔結構:

區域名稱說明
MS-DOS MZ HEADER執行檔的檔頭是 MS-DOS 相容的檔頭
MS-DOS 2.0 STUB PROGRAM STUB PROGRAM 是用來判斷作業系統是否可執行本執行檔的程式
PE HEADER執行碼的檔頭
SECTION TABLE執行碼的區段表
OTHERS執行碼的相關資訊

雖然執行檔有很多的資料內容,在轉換成二進位檔的過程中,並非每個資料都會用到。 只要掌握幾個重要資訊,就可以從執行檔中把二進位檔的內容整理出來。

整理二進位檔的步驟是:

步驟說明
1找出 MZ HEADER。
2從 MZ HEADER 找出 PE OFFSET。
3從 PE HEADER 找出 SECTION NUMBER 和 SECTION TABLE。
4依據 SECTION TABLE 中 SECTION HEADER 的描述,將 SECTION 的內容重組成二進位檔。

為了解析執行檔的內容,需要了解 MZ HEADER、PE HEADER、OPTIONAL HEADER、SECTION HEADER。


43.3.1 MZ 檔頭

MZ 檔頭的定義(MZ HEADER):

欄位名稱位元組數
MAGIC NUMBER2
BYTES ON LAST PAGE2
PAGES IN FILE2
RELOCATIONS2
SIZE OF HEADER IN PARAGRAPH2
MINIMUN EXTRA PARAGRAPHS NEEDED2
MAXIMUN EXTRA PARAGRAPHS NEEDED2
INITIAL SS VALUE2
INITIAL SP VALUE2
CHECKSUM2
INITIAL IP VALUE2
INITIAL CS VALUE2
FILE ADDRESS OF RELOCATION TABLE2
OVERLAY NUMBER2
RESERVED 4 WORDS8
OEM IDENTIFIER2
OEM INFORMATION2
RESERVED 10 WORDS 20
FILE ADDRESS OF NEW EXE HEADER4

43.3.2 PE 檔頭

可攜式執行碼的檔頭定義(PE HEADER):

欄位名稱位元組數說明
MACHINE TYPE2機械型號代碼,指CPU 的類型。
NUMBER OF SECTIONS2區段數目
TIME DATE STAMP4檔案建立時間(秒),從 1970.01.01 00:00 開始算起。
POINTER TO SYMBOL TABLE4指標指向符號表位址
NUMBER OF SYMBOLS4符號表中的符號數目
SIZE OF OPTIONAL HRADER2OPTIONAL HEADER 位元組數
CHARACTERISTICS2目的檔特徵值

43.3.3 選擇性檔頭

選擇性檔頭的定義(OPTIONAL HEADER):
這個定義是以 PE32 為準,若是 PE32+ 會有些不一樣。

欄位名稱位元組數
MAGIC NUMBER2
MAJOR LINKER VERSION1
MINOR LINKER VERSION1
SIZE OF CODE4
SIZE OF INITIALIZED DATA4
SIZE OF UNINITIALIZED DATA4
ADDRESS OF ENTRY POINT4
BASE OF CODE4
BASE OF DATA4
IMAGE BASE4
SECTION ALIGNMENT4
FILE ALIGNMENT4
MAJOR OPERATION SYSTEM VERSION2
MINOR OPERATION SYSTEM VERSION2
MAJOR IMAGE VERSION2
MINOR IMAGE VERSION2
MAJOR SUBSYSTEM VERSION2
MINOR SUBSYSTEM VERSION2
RESERVED DOUBLE WORD4
SIZE OF IMAGE4
SIZE OF HEADERS4
CHECKSUM4
SUBSYSTEM2
DLL CHARACTERISTICS2
SIZE OF STACK RESERVE4
SIZE OF STACK COMMIT4
SIZE OF HEAP RESERVE4
SIZE OF HEAP COMMIT4
LOADER FLAGS4
NUMBER OF RVA AND SIZES4
DATA DIRECTORY[DIRECTORY_ENTRIES]SIZE OF IMAGE_DATA_DIRECTORY

43.3.4 區段檔頭

區段檔頭的定義(SECTION HEADER):

欄位名稱位元組數說明
NAME8區段名稱
VIRTUAL SIZE4區段虛體積,區段載入記憶體時的空間大小。
VIRTUAL ADDRESS4區段虛位址
SIZE OF RAW DATA4區段資料大小,指此區段在此目的檔中的實際大小。
POINTER OF RAW DATA4區段資料位址,指此區段在此目的檔中的位址。
POINTER TO RELOCATIONS4區段重新定址指標
POINTER TO LINE NUMBERS4LINE NUMBER 指標
NUMBER OF RELOCATIONS2區段重新定址指標數目
NUMBER OF LINE NUMBERS2LINE NUMBER 數目
CHARACTERISTICS4區段特徵值

43.4 執行檔範例

在這裡就以 OSKERNEL.EXE 檔轉成OSKERNEL.BIN 的過程說明轉檔過程。 關於 OSKERNEL.EXE的完整內容,請參考 FORMOSA_V2\SAMPLE_FILES\OSKERNEL.EXE。


43.4.1 執行檔的 MZ 檔頭

OSKERNEL.EXE 執行檔的檔頭內容:

0x00~0x0f 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 
0x10~0x1f B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 
0x20~0x2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x30~0x3f 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 

OSKERNEL.EXE 執行檔的 MZ 檔頭內容解說:

範圍內容說明
0x00~0x014D 5A這是DOS的執行檔的特徵數值,它的意義是'MZ'二字,是這個 執行檔格式的創始人的名字的簡寫。
0x02~0x0390 000x0090 BYTES IN LAST BLOCK
0x04~0x0503 000x0003 BLOCKS IN FILE
0x06~0x0700 00RELOCATE ENTRY NUMBER
0x08~0x0904 00PARAGRAPH NUMBER
0x0A~0x0B00 00MINIMUN EXTRA PARAGRAPH
0x0C~0x0DFF FFMAXIMUN EXTRA PARAGRAPH
0x0E~0x0F00 00STACK SEGMENT
0x10~0x11B8 00STACK POINTER
0x12~0x1300 00CHECKSUM
0x14~0x1500 00INSTRUCTION POINTER
0x16~0x1700 00CODE SEGMENT
0x18~0x1940 00FIRST EXECUTABLE CODE ADDRESS,STUB PROGRAM。
0x1A~0x1B00 00OVERLAY NUMBER
0x1C~0x238's 004 RESERVED WORDS
0x24~0x2500 00OEM ID
0x26~0x2700 00OEM INFO
0x28~0x3B20's 00 10 RESERVED WORDS
0x3C~0x3F08 00 00 00PE 的開始位址是0x00000080

執行檔的MZ檔頭,得到 PE檔頭的起始位址為0x80。而0x40到0x80之間則是的 DOS 的 STUB CODE,這是一段可以在DOS環境下執行的程式碼,這段程式碼執行的目的就是要告訴作業系統 是否可以執行後面的程式。用 GCC 編譯出來的程式碼是不能在一般作業系統中執行的,如果不小心執 行了的話,STUB CODE 就會讓執行檔停掉,這是一種在作業系統中執行程式時的保護機制。


43.4.2 執行檔的 PE 檔頭

緊接在 MZ 檔頭和 STUB CODE 後面的就是 PE 檔頭。PE 是 PORTABLE EXECUTABLE 的 簡寫,意思是可攜帶的可執行程式碼。可攜帶的意思是作業系統可以把它放到任意位置執行,因為它的 性質就是可以被重新定址並執行的可執行程式碼。只是本作業系統的執行位址已經在 MAKEFILE 中指定 了,所以不可以任意更改執行位址。目前本作業系統的執行位址是 0x00010400。

執行檔的 PE 檔頭中包含 COFF 檔頭和 OPTIONAL 檔頭,COFF 檔頭的體積是固定值 20 位元組,OPTIONAL 標頭的體積由 COFF 檔頭決定。


43.4.2.1 COFF 檔頭

PE 檔頭內的 COFF 檔頭內容:

0x80~0x8f 50 45 00 00 4C 01 05 00 F6 6A C8 4B 00 00 00 00
0x90~0x97 00 00 00 00 E0 00 0F 03

PE 檔頭內的 COFF 檔頭內容說明:

範圍內容說明
0x80~0x8350 45 00 00PE開頭的特徵數值 0x00004550,意思是 PE 二字。
0x84~0x854C 01機械代碼 0x014C 的意思是 i386。
0x86~0x8705 00區段數目為 5 個區段
0x88~0x8BAA E7 66 4B時間紀錄為 0x4B66E7AA 秒,從1970.01.01 00:00 開始算起的時間值,日期為 2010.02.03。
0x8C~0x8F00 00 00 00符號表指標值為 0,空指標,意思是沒有符號表。
0x90~0x9300 00 00 00符號數目為 0,即沒有符號。
0x94~0x95E0 00可選擇性檔頭為0xE0。整個PE檔頭的體積是 0x18+0xE0,共 0xF8 位元組。 區段表的位址緊接在 PE 檔頭後面,所以區段表的位址等於 PE 檔頭的起始位址(0x80)加上 PE 檔頭體積 (0xF8),0x0178。
0x96~0x970F 03PE檔的特徵值為0x030F。
0x0200 是沒有DEBUG資訊,
0x0100 是指程式碼 32 位元,
0x0008 是指沒有符號表,
0x0004 是指沒有LINE NUMBER,
0x0002 是指此映像檔有連結成功,
0x0001 是指此映像檔必須被載入到被設定的地址執行,因為沒有提供可執行地址。
本作業系統執行的位址是 0x00010400,所以起動磁區必須把作業系統的映像複製到這個位址上。

從 PE 檔頭的資訊,得知區段的數目為 5 個,區段表的位址是 0x178。 雖然選擇性檔頭有 0xE0 位元組,在這裡並不需要對它裡面的資訊做任何處理,直接跳過即可。 這是因為我們只是想要把執行檔轉成二進位檔,這樣的動作所需要的資訊通通藏在區段表。 區段表的內容的解析才是真正的重點。


43.4.2.2 OPTIONAL 檔頭

OPTIONAL 檔頭在本作業系統的轉檔過程中,並未用到,不過筆者還是解析檔頭內各欄位的內容。

PE 檔頭內的 OPTIONAL 檔頭內容:

0x098~0x0A7 0B 01 02 38 00 82 00 00 00 1C 00 00 00 2E 00 00
0x0A8~0x0B7 00 04 C1 FF 00 04 C1 FF 00 90 C1 FF 00 00 40 00
0x0B8~0x0C7 00 10 00 00 00 02 00 00 04 00 00 00 01 00 00 00
0x0C8~0x0D7 04 00 00 00 00 00 00 00 00 F0 C1 FF 00 04 00 00
0x0D8~0x0E7 BB 29 01 00 03 00 00 00 00 00 20 00 00 10 00 00
0x0E8~0x0F7 00 00 10 00 00 10 00 00 00 00 00 00 10 00 00 00
0x0F8~0x107 00 00 00 00 00 00 00 00 00 E0 C1 FF 14 00 00 00
0x108~0x117 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x118~0x127 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x128~0x137 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x138~0x147 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x148~0x157 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x158~0x167 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x168~0x177 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

PE 檔頭內的 OPTIONAL 檔頭內容說明:

範圍內容說明
0x098~0x0990B 01MAGIC NUMBER,0x010B 表示格式為 PE32。
0x09A02MAJOR LINK VERSION 0x02。
0x09B38MINOR LINK VERSION 0x38。
0x09C~0x09F00 82 00 00SIZE OF CODE,0x0008200。
0x0A0~0x0A300 1C 00 00SIZE OF INITIALIZED SZIE,0x00001C00。
0x0A4~0x0A700 2E 00 00SIZE OF UNINITIALIZED SIZE,0x00002E00。
0x0A8~0x0AB00 04 C1 FFADDRESS OF ENTRY POINT,0xFFC10400。
0x0AC~0x0AF00 04 C1 FFBASE OF CODE,0xFFC10400。
0x0B0~0x0B300 90 C1 FFBASE OD DATA,0xFFC19000。
0x0B4~0x0B700 00 40 00IMAGE BASE,0x00400000。
0x0B8~0x0BB00 10 00 00SECTION ALIGNMENT,0x00001000。
0x0BC~0x0BF00 02 00 00FILE ALIGNMENT,0x00000200。
0x0C0~0x0C100 04MAJOR OPERATION SYSTEM VERSION。
0x0C2~0x0C300 00MINOR OPERATION SYSTEM VERSION。
0x0C4~0x0C500 01MAJOR IMAGE VERSION,0x0001。
0x0C6~0x0C700 00MINOR IMAGE VERSION,0x0000。
0x0C8~0x0C904 00MAJOR SUBSYSTEM VERSION,0x0400。
0x0CA~0x0CB00 00MINOR SUBSYSTEM VERSION,0x0000。
0x0CC~0x0CF00 00 00 00RESERVED DOUBLE WORDS,0x00000000。
0x0D0~0x0D300 F0 C1 FFSIZE OF IMAGE,0xFFC1F000。
0x0D4~0x0D700 04 00 00SIZE OF HEADERS,0x00000400。
0x0D8~0x0DBBB 28 01 00CHECKSUM,0x000128BB。
0x0DC~0x0DD03 00SUBSYSTEM,0x0003。
0x0DE~0x0DF00 00DLL CHARACTERISTICS,0x0000。
0x0E0~0x0E300 00 20 00SIZE OF STACK RESERVED,0x00200000。
0x0E4~0x0E700 10 00 00SIZE OF STACK COMMIT,0x000001000。
0x0E8~0x0EB00 00 10 00SIZE OF HEAP RESERVED,0x00100000。
0x0EC~0x0EF00 10 00 00SIZE OF HEAP COMMIT,00001000。
0x0F0~0x0F300 00 00 00LOADER FLAGS,0x00000000。
0x0F4~0x0F710 00 00 00NUMBER OF RVA AND SIZES,0x00000010。表示有 16 個目錄,但筆者觀察只有第二個目錄不是空目錄。
0x100~0x10700 E0 C1 FF 14 00 00 002ND DIRECTORY,RVA 0xFFC1E000,SIZE 0x00000014。

43.4.3 執行檔的區段表

區段表 (SECTION TABLE)紀錄著執行檔中的可執行的程式碼的全部內容。 只要把每個區段的內容併接起來就可以得到二進位檔的映像。


43.4.3.1 第一個區段檔頭

區段檔頭的資料內容:

0x178~0x187 2E 74 65 78 74 00 00 00 5C 81 00 00 00 04 C1 FF
0x188~0x197 00 82 00 00 00 04 00 00 00 00 00 00 00 00 00 00
0x198~0x19F 00 00 00 00 20 00 50 60

區段檔頭的說明:

範圍內容說明
0x178~0x17F2E 74 65 78 74 00 00 00區段名稱為 ".text"
0x180~0x1835C 81 00 00區段虛體積為 0x0000815C 位元組。本區段真正有用的資料長度。
0x184~0x18700 04 C1 FF區段虛位址為 0xFFC10400。本區段在執行時的起始位址為 0x00010400,在二進位檔中的位址是 0。
0x188~0x18B00 82 00 00區段資料長度為 0x00008200 位元組。本區段在執行檔中所佔據的體積。
0x18C~0x18F00 04 00 00區段資料位址從 0x00000400 開始。本區段在執行檔中開始的位址。
0x190~0x19300 00 00 00區段重新定址指標為空指標 (NULL)。
0x194~0x19700 00 00 00LINE NUMBER 指標為空指標 (NULL)。
0x198~0x19900 00區段重新定址指標數目為 0。
0x19A~0x19B00 00LINE NUMBER 數目為 0。
0x19C~0x19F20 00 50 60區段特徵值為 0x60500020。
0x00000020 表示區段有可執行碼,
0x00500000 表示區段資料對齊 16 位元組的邊界,
0x60000000 表示區段資料能像程式碼般被執行,也可以被讀取。

43.4.3.2 第二個區段檔頭

區段檔頭的資料內容:

0x1A0~0x1AF 2E 64 61 74 61 00 00 00 18 0A 00 00 00 90 C1 ff
0x1B0~0x1BF 00 0c 00 00 00 86 00 00 00 00 00 00 00 00 00 00
0x1C0~0x1C7 00 00 00 00 40 00 60 C0

區段檔頭的說明:

範圍內容說明
0x1A0~0x1A72E 64 61 74 61 00 00 00區段名稱為 ".data"
0x1A8~0x1AB40 0A 00 00區段虛體積為 0x00000A40 位元組。本區段真正有用的資料長度。
0x1AC~0x1AF00 90 C1 FF區段虛位址為 0xFFC19000。本區段在執行時的起始位址為 0x00019000,在二進位檔中的位址是 0x00008C00。
0x1B0~0x1B300 0C 00 00區段資料長度為 0x00000C00 位元組。本區段在執行檔中所佔據的體積。
0x1B4~0x1B700 86 00 00區段資料位址從 0x00008600 開始。本區段在執行檔中開始的位址。
0x1B8~0x1BB00 00 00 00區段重新定址指標為空指標 (NULL)。
0x1BC~0x1BF00 00 00 00LINE NUMBER 指標為空指標 (NULL)。
0x1C0~0x1C100 00區段重新定址指標數目為 0。
0x1C2~0x1C300 00LINE NUMBER 數目為 0。
0x1C4~0x1C740 00 60 C0區段特徵值為 0xC0600040,
其中 0x00000040 的意思是區段含有已初始化資料,
0x00600000 的意思是此區段資料對齊 32 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。

43.4.3.3 第三個區段檔頭

區段檔頭的資料內容:

0x1C8~0x1D7 2E 72 64 61 74 61 00 00 B4 0C 00 00 00 A0 C1 FF
0x1D8~0x1E7 00 0E 00 00 00 92 00 00 00 00 00 00 00 00 00 00 
0x1E7~0x1EF 00 00 00 00 40 00 60 40

區段檔頭的說明:

範圍內容說明
0x1C8~0x1CF2E 72 64 61 74 61 00 00區段名稱為 ".rdata"
0x1D0~0x1D3B4 0C 00 00區段虛體積為 0x00000CB4 位元組。本區段真正有用的資料長度。
0x1D4~0x1D700 A0 C1 FF區段虛位址為 0xFFC1A000。本區段在執行時的起始位址為 0x0001A000,在二進位檔中的位址是 0x00009C00。
0x1D8~0x1DB00 0E 00 00區段資料長度為 0x00000E00 位元組。本區段在執行檔中所佔據的體積。
0x1DC~0x1DF00 92 00 00區段資料位址從 0x00009200 開始。本區段在執行檔中開始的位址。
0x1E0~0x1E300 00 00 00區段重新定址指標為空指標 (NULL)。
0x1E4~0x1E700 00 00 00LINE NUMBER 指標為空指標 (NULL)。
0x1E8~0x1E900 00區段重新定址指標數目為 0。
0x1EA~0x1EB00 00LINE NUMBER 數目為 0。
0x1EB~0x1EF40 00 60 40區段特徵值為 0x40600040。
0x00000040 的意思是區段含有已初始化資料,
0x00600000 的意思是此區段資料對齊 32 位元組的邊界,
0x40000000 的意思此區段資料只可以被讀取。

43.4.3.4 第四個區段檔頭

區段檔頭的資料內容:

0x1F0~0x1FF 2E 62 73 73 00 00 00 00 D4 2C 00 00 00 B0 C1 FF
0x200~0x20F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x210~0x217 00 00 00 00 80 00 30 C0

區段檔頭的說明:

範圍內容說明
0x1F0~0x1F72E 62 73 73 00 00 00 00區段名稱為 ".bss"
0x1F8~0x1FBD4 2C 00 00區段虛體積為 0x00002CD4 位元組。本區段真正有用的資料長度。
0x1FC~0x1FF00 B0 C1 FF區段虛位址為 0xFFC1B000。本區段在執行時的起始位址為 0x0001B000,在二進位檔中的位址是 0x0000AC00。
0x200~0x20300 00 00 00區段資料長度為 0x00000000 位元組。本區段在執行檔中所佔據的體積。
0x204~0x20700 00 00 00區段資料位址從 0x00000000 開始。本區段在執行檔中開始的位址。
0x208~0x20B00 00 00 00區段重新定址指標為空指標 (NULL)。
0x20C~0x20F00 00 00 00LINE NUMBER 指標為空指標 (NULL)
0x210~0x21100 00區段重新定址指標數目為 0
0x212~0x21300 00LINE NUMBER 數目為 0
0x214~0x21780 00 30 C0區段特徵值為 0xC0300080。
其中 0x00000080 的意思是區段含有未初始化資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。

43.4.3.5 第五個區段檔頭

0x218~0x227 2E 69 64 61 74 61 00 00 14 00 00 00 00 E0 C1 FF
0x228~0x237 00 02 00 00 00 A0 00 00 00 00 00 00 00 00 00 00
0x238~0x23F 00 00 00 00 40 00 30 C0

OSKERNEL.EXE 的第 5 個區段檔頭的說明:

範圍內容說明
0x218~0x21F2E 69 64 61 74 61 00 00區段名稱為 ".idata"
0x220~0x22314 00 00 00區段虛體積為 0x00000014 位元組。本區段真正有用的資料長度。
0x224~0x22700 E0 C1 FF區段虛位址為 0xFFC1E000。本區段在執行時的起始位址為 0x0001E000,在二進位檔中的位址是 0x0000DC00。
0x228~0x22B00 20 00 00區段資料長度為 0x00002000 位元組。本區段在執行檔中所佔據的體積。
0x22C~0x22F00 A0 00 00區段資料位址從 0x0000A000 開始。本區段在執行檔中開始的位址。
0x230~0x23300 00 00 00區段重新定址指標為空指標 (NULL)。
0x234~0x23700 00 00 00LINE NUMBER 指標為空指標 (NULL)。
0x238~0x02900 00區段重新定址指標數目為 0。
0x23A~0x02B00 00LINE NUMBER 數目為 0。
0x23C~0x02F40 00 30 C0區段特徵值為 0xC0300040。
其中 0x00000040 的意思是區段含有已初始化資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。

43.4.3.6 區段表的結論

從區段表的內容,可以找到各區段的資料的位址以及要寫到二進位檔的位址。 各區段的資料是由區段資料長度和區段資料位址取得,再依照區段虛位址和區段虛體積,將區 段資料填寫到二進位檔,即可得到作業系統的二進位檔。
二進位檔的位址 0 等同於是區段虛位址 0xFFC10400。 執行的時候,二進位檔就會被載入到 0x00010400 的位址上執行。 複製過程以區段資料長度為準,不必理會區段虛體積,原因是區段記憶體長度一定大於或等於區段資料長度。 區段虛體積是指區段資料中真正有用的資料,這個資訊其實不需要用到。

執行檔與二進位檔的記憶體對照表:

段落長度二進位檔的範圍執行檔的範圍說明
10x82000x00000000~0x000081FF0x00000400~0x000085FF 根據第 1 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是可執行碼。其中,0x000081FF 是 0x00008200-1 的結果。
20x0C000x00008C00~0x000097FF0x00008600~0x000091FF 根據第 2 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料,筆者觀察這部分是應該是 C 語言宣告的變數資料的集合。
其中,0x000097FF 是 0x00008C00+0x00000C00-1 的結果。
30x0E000x00009C00~0x0000A9FF0x00009200~0x00009FFF 根據第 3 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料,筆者觀察這部分有 C 語言與組合語言宣告的字串與變數。
其中,0x00009FFF 是 0x00009C00+0x00000E00-1 的結果。
40x2CD40x0000AC00~0x0000D8D3NO DATA 根據第 4 個區段的描述,此範圍為未初始資料,全部補 0。
這一個段落比較特別,因為再執行檔中沒有這個段落的實際資料,所以資料長度就以區段虛體積的長度為準。
其中,0x0000D8D3 是 0x0000AC00+0x00002CD4-1 的結果。
50x20000x0000DC00~0x0000FBFF0x0000A000~0x0000BFFF 根據第 5 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料。

整合 5 個區段的內容,就可以得到二進位檔,即作業系統的映像檔。 這 5 個區段之間有不連續的記憶體空間,不過無所謂,只要將確定的範圍內的資料複製到二進位檔即可。