x86 彙編/機器語言轉換
x86 彙編指令與底層的機器指令之間存在一一對應關係。這意味著我們可以使用查詢表將彙編指令轉換為機器指令。本頁將討論一些從組合語言到機器語言的轉換。
x86 架構是一種複雜指令集計算機 (CISC) 架構。其中一個含義是 x86 架構的指令長度各不相同。這可能會使彙編、反彙編和指令解碼的過程變得更加複雜,因為需要計算每個指令的指令長度。
x86 指令的長度可以是 1 到 15 個位元組。根據指令的可用操作模式、所需運算元的數量等,每個指令的長度都單獨定義。
這是 8086 在主記憶體中順序排列的通用指令格式
| 字首 (可選) |
| 操作碼 (第一個位元組) | D | W |
| 操作碼 2 (偶爾的第二個位元組) |
| MOD | Reg | R/M |
| 位移量或資料 (偶爾: 1、2 或 4 個位元組) |
- 字首
- 可選的字首,它們會改變指令的操作
- D
- (1 位) 方向。1 = 暫存器是目標,0 = 暫存器是源。
- W
- (1 位) 操作大小。1 = 字,0 = 位元組。
- 操作碼
- 操作碼是一個 6 位的量,它決定程式碼屬於哪個指令族
- MOD (Mod)
- (2 位) 暫存器模式。
- Reg
- (3 位) 暫存器。每個暫存器都有一個識別符號。
- R/M (r/m)
- (3 位) 暫存器/記憶體運算元
並非所有指令都有 W 或 D 位;在某些情況下,操作的寬度無關緊要或隱式,對於其他操作,資料方向無關緊要。
請注意,英特爾指令格式是小端格式,這意味著最低有效位元組最靠近絕對地址 0。因此,字的儲存順序是低位元組在前;值 1234H 在記憶體中儲存為 34H 12H。按照慣例,最高有效位始終顯示在位元組內的左側,因此 34H 為 00110100B。
在最初的 2 個位元組之後,每個指令可以有許多額外的定址/立即數資料位元組。
| Mod | 位移量 |
| 00 | 如果 r/m 為 110,則位移量 (16 位) 是地址;否則,沒有位移量 |
| 01 | 8 位位移量,符號擴充套件為 16 位 |
| 10 | 16 位位移量 (示例:MOV [BX + SI]+ 位移量,al) |
| 11 | r/m 被視為第二個“reg”欄位 |
| Reg | W = 0 | W = 1 | 雙字 | |
| 000 | AL | AX | EAX | |
| 001 | CL | CX | ECX | |
| 010 | DL | DX | EDX | |
| 011 | BL | BX | EBX | |
| 100 | AH | SP | ESP | |
| 101 | CH | BP | EBP | |
| 110 | DH | SI | ESI | |
| 111 | BH | DI | EDI |
| r/m | 運算元地址 |
| 000 | (BX) + (SI) + 位移量 (0、1 或 2 個位元組長) |
| 001 | (BX) + (DI) + 位移量 (0、1 或 2 個位元組長) |
| 010 | (BP) + (SI) + 位移量 (0、1 或 2 個位元組長) |
| 011 | (BP) + (DI) + 位移量 (0、1 或 2 個位元組長) |
| 100 | (SI) + 位移量 (0、1 或 2 個位元組長) |
| 101 | (DI) + 位移量 (0、1 或 2 個位元組長) |
| 110 | (BP) + 位移量,除非 mod = 00 (參見 mod 表) |
| 111 | (BX) + 位移量 (0、1 或 2 個位元組長) |
請注意 MOD 00、r/m 110 的特殊含義。通常,這應該被認為是運算元 [BP]。但是,位移量被視為絕對地址。要編碼值 [BP],可以使用 mod = 01、r/m = 110、8 位位移量 = 0。
讓我們將以下指令轉換為機器程式碼
XOR CL, [12H]
請注意,這是將 CL 與地址 12H 的內容進行異或運算 - 方括號是常見的間接定址指示符。XOR 的操作碼是“001100dw”。D 為 1,因為 CL 暫存器是目標。W 為 0,因為我們有一個位元組的資料。因此,我們的第一個位元組是“00110010”。
現在,我們知道 CL 的程式碼是 001。因此,Reg 的值為 001。地址被指定為一個簡單的位移量,因此 MOD 值為 00,R/M 為 110。因此,位元組 2 是 (00 001 110b)。
位元組 3 和 4 包含有效地址,低位位元組在前,0012H 作為 12H 00H,或 (00010010b) (00000000b)
總的來說,
XOR CL, [12H] = 00110010 00001110 00010010 00000000 = 32H 0EH 12H 00H
現在,如果我們要使用立即數運算元,如下所示
XOR CL, 12H
在本例中,由於沒有方括號,因此 12H 是立即數:它是要與之進行異或運算的數字。立即數 XOR 的操作碼是 1000000w;在本例中,我們使用了一個位元組,因此 w 為 0。因此,我們的第一個位元組是 (10000000b)。
第二個位元組,對於立即數操作,採用“mod 110 r/m”的形式。由於目標是一個暫存器,因此 mod 為 11,使 r/m 欄位成為一個暫存器值。我們已經知道 CL 的暫存器值為 001,因此我們的第二個位元組是 (11 110 001b)。
第三個位元組 (如果這是一個字操作,那麼第四個位元組也是) 是立即數資料。由於它是一個位元組,因此只有一個位元組的資料,12H = (00010010b)。
總的來說,
XOR CL, 12H = 10000000 11110001 00010010 = 80 F1 12
32 位指令的編碼方式與 16 位指令非常相似,只是 (預設情況下) 它們作用於雙字量,而不是字。此外,它們支援更加靈活的記憶體定址格式,這得益於添加了一個 SIB “比例-索引-基址”位元組,該位元組位於 ModR/M 位元組之後。
繼續前面的絕對定址示例,我們採用以下輸入
XOR CL, [12H]
...然後我們得到如下 32 位機器程式碼
首先是操作碼位元組,它保持不變,為 32H。查閱英特爾 IA-32 手冊,第 2C 卷,第 5 章,“XOR” - 我們看到這個操作碼定義了:a) 它需要 2 個運算元,b) 運算元有方向,第一個運算元是目標,c) 第一個運算元是 8 位寬度的暫存器,d) 第二個運算元也是 8 位,但可以是暫存器或記憶體地址,以及 e) 目標暫存器 CL 將被覆蓋以包含操作的結果。這符合我們上面的情況,因為第一個運算元是 CL(“L”表示“C”暫存器的低 8 位),而第二個運算元是對記憶體中地址 12H 處儲存的值的引用 (一個直接/絕對指標或地址引用)。看起來我們不需要任何字首位元組來獲得我們想要的運算元大小。
現在我們知道我們需要一個 ModR/M 位元組,因為操作碼需要它;a) 它需要多個運算元,b) 它們沒有在操作碼或任何字首中定義,c) 沒有立即數運算元。因此,我們再次查閱英特爾手冊,第二卷 A,第二章,2.1.5 節“ModR/M 和 SIB 位元組的定址模式編碼”,表 2-2“使用 ModR/M 位元組的 32 位定址形式”。我們知道第一個運算元將是我們的目標暫存器 CL,因此我們看到它對映到 REG=001b。接下來,我們尋找與第二個運算元匹配的有效地址公式,該運算元是一個沒有暫存器的位移(因此沒有段、基址、比例因子或索引)。最接近的匹配將是 disp32,但由於腳註,閱讀表格很棘手。基本上,我們的公式不在該表格中,我們想要的公式需要一個 SIB 位元組,記為 [--][--],它告訴我們需要指定 Mod=00b、R/M=100b 來啟用 SIB 位元組。因此,我們的第二個位元組是 00001100b 或 0CH。
我們知道 SIB 位元組(如果使用)始終緊隨 ModR/M 位元組,因此我們繼續到英特爾手冊中的下一張表 2-3“使用 SIB 位元組的 32 位定址形式”,並尋找比例因子、索引和基址值的組合,這些組合將為我們提供所需的 disp32 公式。請注意有一個腳註 [*],它基本上告訴我們指定 Scale=00b、Index=100b、Base=101b,這意味著 disp32 沒有索引、沒有比例因子和沒有基址。因此,我們的第三個位元組現在是 25H。
我們知道位移位元組(如果使用)始終緊隨 ModR/M 和 SIB 位元組,因此我們在這裡只需以小端序指定我們的 32 位無符號整數,這意味著我們的下一個四個位元組是 12000000H。
最後,我們得到了機器程式碼。
XOR CL, [12H] = 00110010 00001100 00100101 00010010 00000000 00000000 00000000 = 32 0C 25 12 00 00 00
此指令在 32 位保護模式和 64 位長模式下都有效。