|
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 的檔案內容:
MAIN.O 的檔案內容:
|
|
|
43.2.1.1 目的檔的檔頭
COFF HEADER 內容定義,共 20 位元組:
欄位名稱 | 位元組數 | 說明 |
MACHINE TYPE | 2 | 機械型號代碼,指CPU 的類型。 |
NUMBER OF SECTIONS | 2 | 區段數目 |
TIME DATE STAMP | 4 | 檔案建立時間(秒),從 1970.01.01 00:00 開始算起。 |
POINTER TO SYMBOL TABLE | 4 | 指標指向符號表位址 |
NUMBER OF SYMBOLS | 4 | 符號表中的符號數目 |
SIZE OF OPTIONAL HRADER | 2 | OPTIONAL HEADER 位元組數 |
CHARACTERISTICS | 2 | 目的檔特徵值 |
MAIN.O 的 COFF 檔頭:
MAIN.O 的 COFF HEADER 的說明:
範圍 | 欄位值(LSB~MSB) | 說明 |
0x00~0x01 | 4C 01 | 0x014C 是 i386 的 機械型號代碼 |
0x02~0x03 | 03 00 | 0x0003 表示有 3 個區段。 |
0x04~0x07 | 00 00 00 00 | 檔案建立時間為 0x00000000 ,表示沒有檔案建立時間記錄 |
0x08~0x0b | F6 00 00 00 | 表示符號表開始位址為 0x000000F6 |
0x0c~0x0f | 0F 00 00 00 | 表示符號表中有 15 個符號 |
0x10~0x11 | 00 00 | 表示 COFF HEADER 中的 OPTIONAL HEADER 大小為 0 位元組。 |
0x12~0x13 | 04 01 | 0x0104 表示是此目的檔為32位元的架構,也沒有 COFF 的 LINE NUMBER。 |
|
|
|
43.2.1.2 目的檔的區段表
MAIN.O 的 SECTION TBALE,又名 SECTION HEADERS。從前面的 COFF HEADER 中,得知
區段數目為 3 個區段,每個區段是 40 位元組,所以這個區域的位元組數為 3*40 共120位元組。
區段檔頭 (SECTION HEADER) 的內容定義:
欄位名稱 | 位元組數 | 說明 |
NAME | 8 | 區段名稱 |
VIRTUAL SIZE | 4 | 區段虛體積,區段載入記憶體時的空間大小。 |
VIRTUAL ADDRESS | 4 | 區段虛位址 |
SIZE OF RAW DATA | 4 | 區段資料大小,指此區段在此目的檔中的實際大小。 |
POINTER OF RAW DATA | 4 | 區段資料位址,指此區段在此目的檔中的位址。 |
POINTER TO RELOCATIONS | 4 | 區段重新定址指標 |
POINTER TO LINE NUMBERS | 4 | LINE NUMBER 指標 |
NUMBER OF RELOCATIONS | 2 | 區段重新定址指標數目 |
NUMBER OF LINE NUMBERS | 2 | LINE NUMBER 數目 |
CHARACTERISTICS | 4 | 區段特徵值 |
|
|
|
43.2.1.2.1 第一個區段檔頭
區段檔頭的內容:
區段檔頭的說明:
範圍 | 內容 | 說明 |
0x14~0x1B | 2E 74 65 78 74 00 00 00 | 區段名稱為 ".text",這是存放可執行的程式碼的區段。 |
0x1C~0x1F | 00 00 00 00 | 區段虛體積為 0。 |
0x20~0x23 | 00 00 00 00 | 區段虛位址為 0。 |
0x24~0x27 | 38 00 00 00 | 區段資料長度為 0x38 位元組。 |
0x28~0x2B | 8C 00 00 00 | 區段資料位址從 0x8C 開始。 |
0x2C~0x2F | C4 00 00 00 | 區段重新定址指標指向 0xC4,此指標用來指定此目的檔的資料要坐落於映像檔的何處使用。 |
0x30~0x33 | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL)。 |
0x34~0x35 | 05 00 | 區段重新定址指標數目為 5。 |
0x36~0x37 | 00 00 | LINE NUMBER 數目為 0。 |
0x38~0x3B | 20 00 30 60 | 區段特徵值為 0x60300020,
其中 0x00000020 的意思是區段含有可執行碼,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料能像程式碼般被執行,也可以被讀取。 |
從第一個區段檔頭,得到程式碼的位址範圍是 0x8C~0xC3 共 0x38 位元組。
目的檔中的執行碼範圍 0x8C~0xC3:
|
|
|
43.2.1.2.2 第二個區段檔頭
區段檔頭的說明:
範圍 | 內容(HEX) | 說明 |
0x3C~0x43 | 2E 64 61 74 61 00 00 00 | 區段名稱為 ".data",這是存放已初始化的變數的區段。 |
0x44~0x47 | 00 00 00 00 | 區段虛體積為 0 |
0x48~0x4B | 00 00 00 00 | 區段虛位址為 0 |
0x4C~0x4F | 00 00 00 00 | 區段資料長度為 0x00 位元組 |
0x50~0x53 | 00 00 00 00 | 區段資料位址從 0x00 開始 |
0x54~0x57 | 00 00 00 00 | 區段重新定址指標值 0x00 |
0x58~0x5B | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL) |
0x5C~0x5D | 00 00 | 區段重新定址指標數目為 0x0000 |
0x5E~0x5F | 00 00 | LINE NUMBER 數目為 0 |
0x60~0x63 | 40 00 30 C0 | 區段特徵值為 0xC0300040,
其中 0x00000040 的意思是區段含有已初始化的資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料可以被讀取。 |
第二個區段沒有任何資料。為什麼區段的內容是空的,卻還是存在於區段表的現象,
有可能是編譯器的基本思考邏輯就是假定一支程式基本上都要有可執行程式碼區段、已
初始化資料區段和未初始化資料區段。所以即使區段內容是空的,區段檔頭的資料結構卻早已生成,
然後就直接的寫進區段表中了。
|
|
|
43.2.1.2.3 第三個區段檔頭
第三個區段並沒有任何實質的內容。為什麼區段的內容是空的,卻還是存在於區段表的理由和第二個區段一樣。
區段檔頭的說明:
範圍 | 內容(HEX) | 說明 |
0x64~0x6B | 2E 62 73 73 00 00 00 00 | 區段名稱為 ".bss",這是存放未初始化的變數的區段。 |
0x6C~0x6F | 00 00 00 00 | 區段虛體積為 0 |
0x70~0x73 | 00 00 00 00 | 區段虛位址為 0 |
0x74~0x77 | 00 00 00 00 | 區段資料長度為 0x00 位元組 |
0x78~0x7C | 00 00 00 00 | 區段資料位址從 0x00 開始 |
0x7C~0x7F | 00 00 00 00 | 區段重新定址指標值 0x00 |
0x80~0x83 | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL) |
0x84~0x85 | 00 00 | 區段重新定址指標數目為 0x0000 |
0x86~0x87 | 00 00 | LINE NUMBER 數目為 0 |
0x88~0x8B | 80 00 30 C0 | 區段特徵值為 0xC0300080,
其中 0x00000080 的意思是區段含有未初始化的資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0x60000000 的意思此區段資料可以被讀取。 |
|
|
|
43.2.1.3 目的檔的符號表
目的檔檔頭內有符號表的位址值 0xF6。
符號數目共 15 個,符號表的長度為 15*18 位元組,即 270 位元組。
符號 (SYMBOL TABLE) 的格式:
欄位名稱 | 位元組數 | 說明 |
NAME | 8 | 區段名稱,如果前4位元組都為 0 時,區段名稱為字串表加後4位元的位址處。 |
VALUE | 4 | 重定址位址值或其他意義。 |
SECTION NUMBER | 2 | 區段號碼。 |
TYPE | 2 | 符號型態,0x20表示是函式,0x00表示不是函式。 |
STORAGE CLASS | 1 | 儲存類別。 |
NUMBER OF AUX SYMBOLS | 1 | 輔助符號表數目。 |
|
|
|
43.2.1.3.1 第 1 個符號
符號內容:
符號說明:
字串 .file 是編譯器的特殊符號名稱。
範圍 | 內容(HEX) | 說明 |
0x0F6~0x0FD | 2E 66 69 6C 65 00 00 00 | 符號表名稱 ".file"。 |
0x0FE~0x101 | 00 00 00 00 | 重定址位址值為 0。 |
0x102~0x103 | FE FF | 區段號碼為 -2 (0xFFFE),意思是提供一般用途的偵錯資訊。 微軟將它與 ".file" 符號共用。 |
0x104~0x105 | 00 00 | 非函式符號。 |
0x106 | 67 | 儲存類別為 0x67。 |
0x107 | 01 | 輔助符號數目為 1。 |
|
|
|
43.2.1.3.2 第 2 個符號
符號內容:
符號說明:
此符號為前一個符號的輔助符號,字串 main.c 是檔案名稱。
範圍 | 內容(HEX) | 說明 |
0x108~0x10F | 6D 61 69 6E 66 2E 63 00 | 符號表名稱 "main.c"。 |
0x110~0x113 | 00 00 00 00 | 重定址位址值為 0。 |
0x114~0x115 | 00 00 | 區段號碼 0x0000。 |
0x116~0x117 | 00 00 | 非函式符號。 |
0x118 | 00 | 儲存類別為 0。 |
0x119 | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.3 第 3 個符號
符號內容:
符號說明:
字串 _Main 要引出的符號,函式 Main。
範圍 | 內容(HEX) | 說明 |
0x11A~0x121 | 5F 4D 61 69 6E 00 00 00 00 | 符號表名稱 "_Main"。 |
0x122~0x125 | 00 00 00 00 | 重定址位址值為 0。 |
0x126~0x127 | 01 00 | 區段號碼 1。 |
0x128~0x129 | 20 00 | 為函式符號。 |
0x12A | 02 | 儲存類別為 2。 |
0x12B | 01 | 輔助符號數目為 1。 |
|
|
|
43.2.1.3.4 第 4 個符號
符號內容:
符號說明:
這是一個空符號。筆者不懂這個符號存在的原因。
範圍 | 內容(HEX) | 說明 |
0x12C~0x133 | 00 00 00 00 00 00 00 00 | 符號表名稱 NULL。 |
0x134~0x137 | 00 00 00 00 | 重定址位址值為 0。 |
0x138~0x139 | 01 00 | 區段號碼 1。 |
0x13A~0x13B | 00 00 | 非函式符號。 |
0x13C | 00 | 儲存類別為 0。 |
0x13D | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.5 第 5 個符號
符號內容:
符號說明:
字串 .text 是可執行程式碼的區段名稱。
範圍 | 內容(HEX) | 說明 |
0x13E~0x145 | 2E 74 65 78 74 00 00 00 | 符號表名稱 ".text"。 |
0x146~0x009 | 00 00 00 00 | 重定址位址值為 0。 |
0x14A~0x00B | 01 00 | 區段號碼 1。 |
0x14C~0x00D | 00 00 | 非函式符號。 |
0x14E | 03 | 儲存類別為 3。 |
0x14F | 01 | 輔助符號數目為 1。 |
|
|
|
43.2.1.3.6 第 6 個符號
符號內容:
符號說明:
筆者不知道這個符號的用途,看起來應該是符號 .text 的輔助符號,但筆者看不懂符號表的名稱。
範圍 | 內容(HEX) | 說明 |
0x150~0x157 | 36 00 00 00 05 00 00 00 | 符號表名稱 6。 |
0x158~0x15B | 00 00 00 00 | 重定址位址值為 0。 |
0x15C~0x15D | 00 00 | 區段號碼 0x0000。 |
0x15E~0x15F | 00 00 | 非函式符號。 |
0x160 | 00 | 儲存類別為 0x00。 |
0x161 | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.7 第 7 個符號
符號內容:
符號說明:
字串 .data 是已初始化資料的區段名稱。
範圍 | 內容(HEX) | 說明 |
0x162~0x169 | 2E 64 61 74 61 00 00 00 | 符號表名稱 ".data" |
0x16A~0x16D | 00 00 00 00 | 重定址位址值為 0。 |
0x16E~0x16F | 02 00 | 區段號碼 2。 |
0x170~0x171 | 00 00 | 非函式符號。 |
0x172 | 03 | 儲存類別為 3。 |
0x173 | 01 | 輔助符號數目為 1。 |
|
|
|
43.2.1.3.8 第 8 個符號
符號內容:
符號說明:
這個符號是個空符號 (NULL)。為什麼會有這個符號的原因,筆者猜,可能是區段的符號 .data 內假定至少有一
個符號,但事實上並沒有符號在 .data 的區段內,所以編譯器就寫一個空符號在這裡。無論如何,這個目的檔的相容性看起來是沒有問題的。
範圍 | 內容(HEX) | 說明 |
0x174~0x17B | 00 00 00 00 00 00 00 00 | 符號表名稱 NULL。 |
0x17C~0x17F | 00 00 00 00 | 重定址位址值為 0。 |
0x180~0x181 | 00 00 | 區段號碼 0。 |
0x182~0x183 | 00 00 | 非函式符號。 |
0x184 | 00 | 儲存類別為 0。 |
0x185 | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.9 第 9 個符號
符號內容:
符號說明:
字串 .bss 是已初始化資料的區段名稱。
範圍 | 內容(HEX) | 說明 |
0x186~0x18D | 2E 62 73 73 00 00 00 00 | 符號表名稱 ".bss"。 |
0x18E~0x191 | 00 00 00 00 | 重定址位址值為 0。 |
0x192~0x003 | 03 00 | 區段號碼 3。 |
0x194~0x005 | 00 00 | 非函式符號。 |
0x196 | 03 | 儲存類別為 0。 |
0x197 | 01 | 輔助符號數目為 1。 |
|
|
|
43.2.1.3.10 第 10 個符號
符號內容:
符號說明:
這個符號是個空符號 (NULL)。
範圍 | 內容(HEX) | 說明 |
0x198~0x19F | 00 00 00 00 00 00 00 00 | 符號表名稱 NULL |
0x1A0~0x1A3 | 00 00 00 00 | 重定址位址值為 0 |
0x1A4~0x1A5 | 00 00 | 區段號碼 0x0000 |
0x1A6~0x1A7 | 00 00 | 非函式符號 |
0x1A8 | 00 | 儲存類別為 0x00 |
0x1A9 | 00 | 輔助符號數目為 0 |
|
|
|
43.2.1.3.11 第 11 個符號
符號內容:
符號說明:
這個符號的字串是在字串表的第 4 位元組,即字串 _RootTaskName ,是根任務的名稱字串。
範圍 | 內容(HEX) | 說明 |
0x1AA~0x1B1 | 00 00 00 00 04 00 00 00 | 符號表名稱是在字串表的第 4 位元組,即字串 _RootTaskName。 |
0x1B2~0x1B5 | 80 00 00 00 | 重定址位址值為 0x00000080 |
0x1B6~0x1B7 | 00 00 | 區段號碼 0。 |
0x1B8~0x1B9 | 00 00 | 非函式符號。 |
0x1BA | 02 | 儲存類別為 2。 |
0x1BB | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.12 第 12 個符號
符號內容:
符號說明:
字串 _OsStart 是啟動根任務函式的名稱,這是是一個函式符號。
範圍 | 內容(HEX) | 說明 |
0x1BC~0x1C3 | 5F 4F 73 53 74 61 72 74 | 符號表名稱 "_OsStart"。 |
0x1C4~0x1C7 | 00 00 00 00 | 重定址位址值為 0。 |
0x1C8~0x1C9 | 00 00 | 區段號碼 0。 |
0x1CA~0x1CB | 20 00 | 為函式符號。 |
0x1CC | 02 | 儲存類別為 0。 |
0x1CD | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.13 第 13 個符號
符號內容:
符號說明:
這個符號的字串是在字串表的第 18 位元組,即字串 _RootTask ,是根任務函式的名稱字串。
範圍 | 內容(HEX) | 說明 |
0x1CE~0x1D5 | 00 00 00 00 12 00 00 00 | 符號表名稱是在字串表的第 18 位元組,即字串 _RootTask。 |
0x1D6~0x1D9 | 00 00 00 00 | 重定址位址值為 0。 |
0x1DA~0x1DB | 00 00 | 區段號碼 0。 |
0x1DC~0x1DD | 20 00 | 函式符號。 |
0x1DE | 02 | 儲存類別為 2。 |
0x1DF | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.14 第 14 個符號
符號內容:
符號說明:
這個符號的字串是在字串表的第 28 位元組,即字串 _RootTaskCreat ,是根任務堆疊的名稱字串。
範圍 | 內容(HEX) | 說明 |
0x1E0~0x1E7 | 00 00 00 00 1C 00 00 00 | 符號表名稱是在字串表的第 32 位元組,即字串 _RootTaskCreat。 |
0x1E8~0x1EB | 00 00 00 00 | 重定址位址值為 0。 |
0x1EC~0x1ED | 00 00 | 區段號碼 0。 |
0x1EE~0x1EF | 20 00 | 函式符號。 |
0x1F0 | 02 | 儲存類別為 2。 |
0x1F1 | 00 | 輔助符號數目為 0。 |
|
|
|
43.2.1.3.15 第 15 個符號
符號內容:
符號說明:
字串 _OsInit 是多工核心初始化函式的名稱,這是是一個函式符號。
範圍 | 內容(HEX) | 說明 |
0x1F2~0x1F9 | 5F 4F 73 49 6E 69 74 00 | 符號表名稱 "_OsInit"。 |
0x1FA~0x1FD | 00 00 00 00 | 重定址位址值為 0 |
0x1FE~0x1FF | 00 00 | 區段號碼 0x0000 |
0x200~0x201 | 20 00 | 函式符號 |
0x202 | 02 | 儲存類別為 0x00 |
0x203 | 00 | 輔助符號數目為 0 |
至此,所有的符號都已經找到。
可以觀察到符號表中,有些符號是給區段名稱用的符號、有些符號是空的符號。
在符號的次序上,分檔案符號、區段符號、變數符號和函式符號。
基本上,所有的要引出或引入的符號都可以在符號表中找到。
在目的檔連結成執行檔的過程中,符號表的符號都會替換成真實的位址值,整併到一個完整的映像中,形成最後的執行檔。
|
|
|
43.2.1.3 目的檔的字串表
字串表緊接在符號表的後面,字串表的長度紀錄在字串表的第 1 個位元組的位址上,字串表的位址算法是 0xF6+15*18,
即第 0x204 位元組的 0x29 。字串表是符號表的輔助,當符號表的字串長度太長時,就要把該字串拿到字串表來放,而符
號表本身就會有指標找到字串表的位置取用字串。這個目的檔的字串表裡面共有 3 個字串,分別是給符號 11、13、14 使用。
|
|
|
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 NUMBER | 2 |
BYTES ON LAST PAGE | 2 |
PAGES IN FILE | 2 |
RELOCATIONS | 2 |
SIZE OF HEADER IN PARAGRAPH | 2 |
MINIMUN EXTRA PARAGRAPHS NEEDED | 2 |
MAXIMUN EXTRA PARAGRAPHS NEEDED | 2 |
INITIAL SS VALUE | 2 |
INITIAL SP VALUE | 2 |
CHECKSUM | 2 |
INITIAL IP VALUE | 2 |
INITIAL CS VALUE | 2 |
FILE ADDRESS OF RELOCATION TABLE | 2 |
OVERLAY NUMBER | 2 |
RESERVED 4 WORDS | 8 |
OEM IDENTIFIER | 2 |
OEM INFORMATION | 2 |
RESERVED 10 WORDS | 20 |
FILE ADDRESS OF NEW EXE HEADER | 4 |
|
|
|
43.3.2 PE 檔頭
可攜式執行碼的檔頭定義(PE HEADER):
欄位名稱 | 位元組數 | 說明 |
MACHINE TYPE | 2 | 機械型號代碼,指CPU 的類型。 |
NUMBER OF SECTIONS | 2 | 區段數目 |
TIME DATE STAMP | 4 | 檔案建立時間(秒),從 1970.01.01 00:00 開始算起。 |
POINTER TO SYMBOL TABLE | 4 | 指標指向符號表位址 |
NUMBER OF SYMBOLS | 4 | 符號表中的符號數目 |
SIZE OF OPTIONAL HRADER | 2 | OPTIONAL HEADER 位元組數 |
CHARACTERISTICS | 2 | 目的檔特徵值 |
|
|
|
43.3.3 選擇性檔頭
選擇性檔頭的定義(OPTIONAL HEADER):
這個定義是以 PE32 為準,若是 PE32+ 會有些不一樣。
欄位名稱 | 位元組數 |
MAGIC NUMBER | 2 |
MAJOR LINKER VERSION | 1 |
MINOR LINKER VERSION | 1 |
SIZE OF CODE | 4 |
SIZE OF INITIALIZED DATA | 4 |
SIZE OF UNINITIALIZED DATA | 4 |
ADDRESS OF ENTRY POINT | 4 |
BASE OF CODE | 4 |
BASE OF DATA | 4 |
IMAGE BASE | 4 |
SECTION ALIGNMENT | 4 |
FILE ALIGNMENT | 4 |
MAJOR OPERATION SYSTEM VERSION | 2 |
MINOR OPERATION SYSTEM VERSION | 2 |
MAJOR IMAGE VERSION | 2 |
MINOR IMAGE VERSION | 2 |
MAJOR SUBSYSTEM VERSION | 2 |
MINOR SUBSYSTEM VERSION | 2 |
RESERVED DOUBLE WORD | 4 |
SIZE OF IMAGE | 4 |
SIZE OF HEADERS | 4 |
CHECKSUM | 4 |
SUBSYSTEM | 2 |
DLL CHARACTERISTICS | 2 |
SIZE OF STACK RESERVE | 4 |
SIZE OF STACK COMMIT | 4 |
SIZE OF HEAP RESERVE | 4 |
SIZE OF HEAP COMMIT | 4 |
LOADER FLAGS | 4 |
NUMBER OF RVA AND SIZES | 4 |
DATA DIRECTORY[DIRECTORY_ENTRIES] | SIZE OF IMAGE_DATA_DIRECTORY |
|
|
|
43.3.4 區段檔頭
區段檔頭的定義(SECTION HEADER):
欄位名稱 | 位元組數 | 說明 |
NAME | 8 | 區段名稱 |
VIRTUAL SIZE | 4 | 區段虛體積,區段載入記憶體時的空間大小。 |
VIRTUAL ADDRESS | 4 | 區段虛位址 |
SIZE OF RAW DATA | 4 | 區段資料大小,指此區段在此目的檔中的實際大小。 |
POINTER OF RAW DATA | 4 | 區段資料位址,指此區段在此目的檔中的位址。 |
POINTER TO RELOCATIONS | 4 | 區段重新定址指標 |
POINTER TO LINE NUMBERS | 4 | LINE NUMBER 指標 |
NUMBER OF RELOCATIONS | 2 | 區段重新定址指標數目 |
NUMBER OF LINE NUMBERS | 2 | LINE NUMBER 數目 |
CHARACTERISTICS | 4 | 區段特徵值 |
|
|
|
43.4 執行檔範例
在這裡就以 OSKERNEL.EXE 檔轉成OSKERNEL.BIN 的過程說明轉檔過程。
關於 OSKERNEL.EXE的完整內容,請參考 FORMOSA_V2\SAMPLE_FILES\OSKERNEL.EXE。
|
|
|
43.4.1 執行檔的 MZ 檔頭
OSKERNEL.EXE 執行檔的檔頭內容:
OSKERNEL.EXE 執行檔的 MZ 檔頭內容解說:
範圍 | 內容 | 說明 |
0x00~0x01 | 4D 5A | 這是DOS的執行檔的特徵數值,它的意義是'MZ'二字,是這個
執行檔格式的創始人的名字的簡寫。 |
0x02~0x03 | 90 00 | 0x0090 BYTES IN LAST BLOCK |
0x04~0x05 | 03 00 | 0x0003 BLOCKS IN FILE |
0x06~0x07 | 00 00 | RELOCATE ENTRY NUMBER |
0x08~0x09 | 04 00 | PARAGRAPH NUMBER |
0x0A~0x0B | 00 00 | MINIMUN EXTRA PARAGRAPH |
0x0C~0x0D | FF FF | MAXIMUN EXTRA PARAGRAPH |
0x0E~0x0F | 00 00 | STACK SEGMENT |
0x10~0x11 | B8 00 | STACK POINTER |
0x12~0x13 | 00 00 | CHECKSUM |
0x14~0x15 | 00 00 | INSTRUCTION POINTER |
0x16~0x17 | 00 00 | CODE SEGMENT |
0x18~0x19 | 40 00 | FIRST EXECUTABLE CODE ADDRESS,STUB PROGRAM。 |
0x1A~0x1B | 00 00 | OVERLAY NUMBER |
0x1C~0x23 | 8's 00 | 4 RESERVED WORDS |
0x24~0x25 | 00 00 | OEM ID |
0x26~0x27 | 00 00 | OEM INFO |
0x28~0x3B | 20's 00 | 10 RESERVED WORDS |
0x3C~0x3F | 08 00 00 00 | PE 的開始位址是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 檔頭內容:
PE 檔頭內的 COFF 檔頭內容說明:
範圍 | 內容 | 說明 |
0x80~0x83 | 50 45 00 00 | PE開頭的特徵數值 0x00004550,意思是 PE 二字。 |
0x84~0x85 | 4C 01 | 機械代碼 0x014C 的意思是 i386。 |
0x86~0x87 | 05 00 | 區段數目為 5 個區段 |
0x88~0x8B | AA E7 66 4B | 時間紀錄為 0x4B66E7AA 秒,從1970.01.01 00:00 開始算起的時間值,日期為 2010.02.03。 |
0x8C~0x8F | 00 00 00 00 | 符號表指標值為 0,空指標,意思是沒有符號表。 |
0x90~0x93 | 00 00 00 00 | 符號數目為 0,即沒有符號。 |
0x94~0x95 | E0 00 | 可選擇性檔頭為0xE0。整個PE檔頭的體積是 0x18+0xE0,共 0xF8 位元組。
區段表的位址緊接在 PE 檔頭後面,所以區段表的位址等於 PE 檔頭的起始位址(0x80)加上 PE 檔頭體積 (0xF8),0x0178。 |
0x96~0x97 | 0F 03 | PE檔的特徵值為0x030F。
0x0200 是沒有DEBUG資訊,
0x0100 是指程式碼 32 位元,
0x0008 是指沒有符號表,
0x0004 是指沒有LINE NUMBER,
0x0002 是指此映像檔有連結成功,
0x0001 是指此映像檔必須被載入到被設定的地址執行,因為沒有提供可執行地址。
本作業系統執行的位址是 0x00010400,所以起動磁區必須把作業系統的映像複製到這個位址上。
|
從 PE 檔頭的資訊,得知區段的數目為 5 個,區段表的位址是 0x178。
雖然選擇性檔頭有 0xE0 位元組,在這裡並不需要對它裡面的資訊做任何處理,直接跳過即可。
這是因為我們只是想要把執行檔轉成二進位檔,這樣的動作所需要的資訊通通藏在區段表。
區段表的內容的解析才是真正的重點。
|
|
|
43.4.2.2 OPTIONAL 檔頭
OPTIONAL 檔頭在本作業系統的轉檔過程中,並未用到,不過筆者還是解析檔頭內各欄位的內容。
PE 檔頭內的 OPTIONAL 檔頭內容:
PE 檔頭內的 OPTIONAL 檔頭內容說明:
範圍 | 內容 | 說明 |
0x098~0x099 | 0B 01 | MAGIC NUMBER,0x010B 表示格式為 PE32。 |
0x09A | 02 | MAJOR LINK VERSION 0x02。 |
0x09B | 38 | MINOR LINK VERSION 0x38。 |
0x09C~0x09F | 00 82 00 00 | SIZE OF CODE,0x0008200。 |
0x0A0~0x0A3 | 00 1C 00 00 | SIZE OF INITIALIZED SZIE,0x00001C00。 |
0x0A4~0x0A7 | 00 2E 00 00 | SIZE OF UNINITIALIZED SIZE,0x00002E00。 |
0x0A8~0x0AB | 00 04 C1 FF | ADDRESS OF ENTRY POINT,0xFFC10400。 |
0x0AC~0x0AF | 00 04 C1 FF | BASE OF CODE,0xFFC10400。 |
0x0B0~0x0B3 | 00 90 C1 FF | BASE OD DATA,0xFFC19000。 |
0x0B4~0x0B7 | 00 00 40 00 | IMAGE BASE,0x00400000。 |
0x0B8~0x0BB | 00 10 00 00 | SECTION ALIGNMENT,0x00001000。 |
0x0BC~0x0BF | 00 02 00 00 | FILE ALIGNMENT,0x00000200。 |
0x0C0~0x0C1 | 00 04 | MAJOR OPERATION SYSTEM VERSION。 |
0x0C2~0x0C3 | 00 00 | MINOR OPERATION SYSTEM VERSION。 |
0x0C4~0x0C5 | 00 01 | MAJOR IMAGE VERSION,0x0001。 |
0x0C6~0x0C7 | 00 00 | MINOR IMAGE VERSION,0x0000。 |
0x0C8~0x0C9 | 04 00 | MAJOR SUBSYSTEM VERSION,0x0400。 |
0x0CA~0x0CB | 00 00 | MINOR SUBSYSTEM VERSION,0x0000。 |
0x0CC~0x0CF | 00 00 00 00 | RESERVED DOUBLE WORDS,0x00000000。 |
0x0D0~0x0D3 | 00 F0 C1 FF | SIZE OF IMAGE,0xFFC1F000。 |
0x0D4~0x0D7 | 00 04 00 00 | SIZE OF HEADERS,0x00000400。 |
0x0D8~0x0DB | BB 28 01 00 | CHECKSUM,0x000128BB。 |
0x0DC~0x0DD | 03 00 | SUBSYSTEM,0x0003。 |
0x0DE~0x0DF | 00 00 | DLL CHARACTERISTICS,0x0000。 |
0x0E0~0x0E3 | 00 00 20 00 | SIZE OF STACK RESERVED,0x00200000。 |
0x0E4~0x0E7 | 00 10 00 00 | SIZE OF STACK COMMIT,0x000001000。 |
0x0E8~0x0EB | 00 00 10 00 | SIZE OF HEAP RESERVED,0x00100000。 |
0x0EC~0x0EF | 00 10 00 00 | SIZE OF HEAP COMMIT,00001000。 |
0x0F0~0x0F3 | 00 00 00 00 | LOADER FLAGS,0x00000000。 |
0x0F4~0x0F7 | 10 00 00 00 | NUMBER OF RVA AND SIZES,0x00000010。表示有 16 個目錄,但筆者觀察只有第二個目錄不是空目錄。 |
0x100~0x107 | 00 E0 C1 FF 14 00 00 00 | 2ND DIRECTORY,RVA 0xFFC1E000,SIZE 0x00000014。 |
|
|
|
43.4.3 執行檔的區段表
區段表 (SECTION TABLE)紀錄著執行檔中的可執行的程式碼的全部內容。
只要把每個區段的內容併接起來就可以得到二進位檔的映像。
|
|
|
43.4.3.1 第一個區段檔頭
區段檔頭的資料內容:
區段檔頭的說明:
範圍 | 內容 | 說明 |
0x178~0x17F | 2E 74 65 78 74 00 00 00 | 區段名稱為 ".text" |
0x180~0x183 | 5C 81 00 00 | 區段虛體積為 0x0000815C 位元組。本區段真正有用的資料長度。 |
0x184~0x187 | 00 04 C1 FF | 區段虛位址為 0xFFC10400。本區段在執行時的起始位址為 0x00010400,在二進位檔中的位址是 0。 |
0x188~0x18B | 00 82 00 00 | 區段資料長度為 0x00008200 位元組。本區段在執行檔中所佔據的體積。 |
0x18C~0x18F | 00 04 00 00 | 區段資料位址從 0x00000400 開始。本區段在執行檔中開始的位址。 |
0x190~0x193 | 00 00 00 00 | 區段重新定址指標為空指標 (NULL)。 |
0x194~0x197 | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL)。 |
0x198~0x199 | 00 00 | 區段重新定址指標數目為 0。 |
0x19A~0x19B | 00 00 | LINE NUMBER 數目為 0。 |
0x19C~0x19F | 20 00 50 60 | 區段特徵值為 0x60500020。
0x00000020 表示區段有可執行碼,
0x00500000 表示區段資料對齊 16 位元組的邊界,
0x60000000 表示區段資料能像程式碼般被執行,也可以被讀取。 |
|
|
|
43.4.3.2 第二個區段檔頭
區段檔頭的資料內容:
區段檔頭的說明:
範圍 | 內容 | 說明 |
0x1A0~0x1A7 | 2E 64 61 74 61 00 00 00 | 區段名稱為 ".data" |
0x1A8~0x1AB | 40 0A 00 00 | 區段虛體積為 0x00000A40 位元組。本區段真正有用的資料長度。 |
0x1AC~0x1AF | 00 90 C1 FF | 區段虛位址為 0xFFC19000。本區段在執行時的起始位址為 0x00019000,在二進位檔中的位址是 0x00008C00。 |
0x1B0~0x1B3 | 00 0C 00 00 | 區段資料長度為 0x00000C00 位元組。本區段在執行檔中所佔據的體積。 |
0x1B4~0x1B7 | 00 86 00 00 | 區段資料位址從 0x00008600 開始。本區段在執行檔中開始的位址。 |
0x1B8~0x1BB | 00 00 00 00 | 區段重新定址指標為空指標 (NULL)。 |
0x1BC~0x1BF | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL)。 |
0x1C0~0x1C1 | 00 00 | 區段重新定址指標數目為 0。 |
0x1C2~0x1C3 | 00 00 | LINE NUMBER 數目為 0。 |
0x1C4~0x1C7 | 40 00 60 C0 | 區段特徵值為 0xC0600040,
其中 0x00000040 的意思是區段含有已初始化資料,
0x00600000 的意思是此區段資料對齊 32 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。 |
|
|
|
43.4.3.3 第三個區段檔頭
區段檔頭的資料內容:
區段檔頭的說明:
範圍 | 內容 | 說明 |
0x1C8~0x1CF | 2E 72 64 61 74 61 00 00 | 區段名稱為 ".rdata" |
0x1D0~0x1D3 | B4 0C 00 00 | 區段虛體積為 0x00000CB4 位元組。本區段真正有用的資料長度。 |
0x1D4~0x1D7 | 00 A0 C1 FF | 區段虛位址為 0xFFC1A000。本區段在執行時的起始位址為 0x0001A000,在二進位檔中的位址是 0x00009C00。 |
0x1D8~0x1DB | 00 0E 00 00 | 區段資料長度為 0x00000E00 位元組。本區段在執行檔中所佔據的體積。 |
0x1DC~0x1DF | 00 92 00 00 | 區段資料位址從 0x00009200 開始。本區段在執行檔中開始的位址。 |
0x1E0~0x1E3 | 00 00 00 00 | 區段重新定址指標為空指標 (NULL)。 |
0x1E4~0x1E7 | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL)。 |
0x1E8~0x1E9 | 00 00 | 區段重新定址指標數目為 0。 |
0x1EA~0x1EB | 00 00 | LINE NUMBER 數目為 0。 |
0x1EB~0x1EF | 40 00 60 40 | 區段特徵值為 0x40600040。
0x00000040 的意思是區段含有已初始化資料,
0x00600000 的意思是此區段資料對齊 32 位元組的邊界,
0x40000000 的意思此區段資料只可以被讀取。 |
|
|
|
43.4.3.4 第四個區段檔頭
區段檔頭的資料內容:
區段檔頭的說明:
範圍 | 內容 | 說明 |
0x1F0~0x1F7 | 2E 62 73 73 00 00 00 00 | 區段名稱為 ".bss" |
0x1F8~0x1FB | D4 2C 00 00 | 區段虛體積為 0x00002CD4 位元組。本區段真正有用的資料長度。 |
0x1FC~0x1FF | 00 B0 C1 FF | 區段虛位址為 0xFFC1B000。本區段在執行時的起始位址為 0x0001B000,在二進位檔中的位址是 0x0000AC00。 |
0x200~0x203 | 00 00 00 00 | 區段資料長度為 0x00000000 位元組。本區段在執行檔中所佔據的體積。 |
0x204~0x207 | 00 00 00 00 | 區段資料位址從 0x00000000 開始。本區段在執行檔中開始的位址。 |
0x208~0x20B | 00 00 00 00 | 區段重新定址指標為空指標 (NULL)。 |
0x20C~0x20F | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL) |
0x210~0x211 | 00 00 | 區段重新定址指標數目為 0 |
0x212~0x213 | 00 00 | LINE NUMBER 數目為 0 |
0x214~0x217 | 80 00 30 C0 | 區段特徵值為 0xC0300080。
其中 0x00000080 的意思是區段含有未初始化資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。 |
|
|
|
43.4.3.5 第五個區段檔頭
OSKERNEL.EXE 的第 5 個區段檔頭的說明:
範圍 | 內容 | 說明 |
0x218~0x21F | 2E 69 64 61 74 61 00 00 | 區段名稱為 ".idata" |
0x220~0x223 | 14 00 00 00 | 區段虛體積為 0x00000014 位元組。本區段真正有用的資料長度。 |
0x224~0x227 | 00 E0 C1 FF | 區段虛位址為 0xFFC1E000。本區段在執行時的起始位址為 0x0001E000,在二進位檔中的位址是 0x0000DC00。 |
0x228~0x22B | 00 20 00 00 | 區段資料長度為 0x00002000 位元組。本區段在執行檔中所佔據的體積。 |
0x22C~0x22F | 00 A0 00 00 | 區段資料位址從 0x0000A000 開始。本區段在執行檔中開始的位址。 |
0x230~0x233 | 00 00 00 00 | 區段重新定址指標為空指標 (NULL)。 |
0x234~0x237 | 00 00 00 00 | LINE NUMBER 指標為空指標 (NULL)。 |
0x238~0x029 | 00 00 | 區段重新定址指標數目為 0。 |
0x23A~0x02B | 00 00 | LINE NUMBER 數目為 0。 |
0x23C~0x02F | 40 00 30 C0 | 區段特徵值為 0xC0300040。
其中 0x00000040 的意思是區段含有已初始化資料,
0x00300000 的意思是此區段資料對齊 4 位元組的邊界,
0xC0000000 的意思此區段資料可以被讀寫,但不能被執行。 |
|
|
|
43.4.3.6 區段表的結論
從區段表的內容,可以找到各區段的資料的位址以及要寫到二進位檔的位址。
各區段的資料是由區段資料長度和區段資料位址取得,再依照區段虛位址和區段虛體積,將區
段資料填寫到二進位檔,即可得到作業系統的二進位檔。
二進位檔的位址 0 等同於是區段虛位址 0xFFC10400。
執行的時候,二進位檔就會被載入到 0x00010400 的位址上執行。
複製過程以區段資料長度為準,不必理會區段虛體積,原因是區段記憶體長度一定大於或等於區段資料長度。
區段虛體積是指區段資料中真正有用的資料,這個資訊其實不需要用到。
執行檔與二進位檔的記憶體對照表:
段落 | 長度 | 二進位檔的範圍 | 執行檔的範圍 | 說明 |
1 | 0x8200 | 0x00000000~0x000081FF | 0x00000400~0x000085FF |
根據第 1 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是可執行碼。其中,0x000081FF 是 0x00008200-1 的結果。 |
2 | 0x0C00 | 0x00008C00~0x000097FF | 0x00008600~0x000091FF |
根據第 2 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料,筆者觀察這部分是應該是 C 語言宣告的變數資料的集合。
其中,0x000097FF 是 0x00008C00+0x00000C00-1 的結果。 |
3 | 0x0E00 | 0x00009C00~0x0000A9FF | 0x00009200~0x00009FFF |
根據第 3 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料,筆者觀察這部分有 C 語言與組合語言宣告的字串與變數。
其中,0x00009FFF 是 0x00009C00+0x00000E00-1 的結果。 |
4 | 0x2CD4 | 0x0000AC00~0x0000D8D3 | NO DATA |
根據第 4 個區段的描述,此範圍為未初始資料,全部補 0。
這一個段落比較特別,因為再執行檔中沒有這個段落的實際資料,所以資料長度就以區段虛體積的長度為準。
其中,0x0000D8D3 是 0x0000AC00+0x00002CD4-1 的結果。 |
5 | 0x2000 | 0x0000DC00~0x0000FBFF | 0x0000A000~0x0000BFFF |
根據第 5 個區段的描述,將執行檔範圍的資料複製到二進位檔的此位址範圍。
此範圍的資料是已初始化資料。 |
整合 5 個區段的內容,就可以得到二進位檔,即作業系統的映像檔。
這 5 個區段之間有不連續的記憶體空間,不過無所謂,只要將確定的範圍內的資料複製到二進位檔即可。
|
|
|