跳轉到內容

x86 彙編/機器語言轉換

來自華夏公益教科書,自由的教科書

與機器碼的關係

[編輯 | 編輯原始碼]

x86 彙編指令與底層的機器指令之間存在一一對應關係。這意味著我們可以使用查詢表將彙編指令轉換為機器指令。本頁將討論一些從組合語言到機器語言的轉換。

CISC 和 RISC

[編輯 | 編輯原始碼]

x86 架構是一種複雜指令集計算機 (CISC) 架構。其中一個含義是 x86 架構的指令長度各不相同。這可能會使彙編、反彙編和指令解碼的過程變得更加複雜,因為需要計算每個指令的指令長度。

x86 指令的長度可以是 1 到 15 個位元組。根據指令的可用操作模式、所需運算元的數量等,每個指令的長度都單獨定義。

8086 指令格式 (16 位)

[編輯 | 編輯原始碼]

這是 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 / Reg / R/M 表格

[編輯 | 編輯原始碼]
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

x86 指令 (32/64 位)

[編輯 | 編輯原始碼]

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 位長模式下都有效。

華夏公益教科書