X86汇编概要
來自:https://www.cnblogs.com/jiftle/p/8453106.html
本文翻譯自:http://www.cs.virginia.edu/~evans/cs216/guides/x86.html
參考資料:標(biāo)識寄存器?
?
本文描述基本的32位X86匯編語言的一個(gè)子集,其中涉及匯編語言的最核心部分,包括寄存器結(jié)構(gòu),數(shù)據(jù)表示,基本的操作指令(包括數(shù)據(jù)傳送指令、邏輯計(jì)算指令、算數(shù)運(yùn)算指令),以及函數(shù)的調(diào)用規(guī)則。個(gè)人認(rèn)為:在理解了本文后,基本可以無障礙地閱讀絕大部分標(biāo)準(zhǔn)X86匯編程序。當(dāng)然,更復(fù)雜的指令請參閱Intel相關(guān)文檔。
1 寄存器.
主要寄存器如下圖所示:
X86處理器中有8個(gè)32位的通用寄存器。由于歷史的原因,EAX通常用于計(jì)算,ECX通常用于循環(huán)變量計(jì)數(shù)。ESP和EBP有專門用途,ESP指示棧指針(用于指示棧頂位置),而EBP則是基址指針(用于指示子程序或函數(shù)調(diào)用的基址指針)。如圖中所示,EAX、EBX、ECX和EDX的前兩個(gè)高位字節(jié)和后兩個(gè)低位字節(jié)可以獨(dú)立使用,其中兩位低字節(jié)又被獨(dú)立分為H和L部分,這樣做的原因主要是考慮兼容16位的程序,具體兼容匹配細(xì)節(jié)請查閱相關(guān)文獻(xiàn)。
應(yīng)用寄存器時(shí),其名稱大小寫是不敏感的,如EAX和eax沒有區(qū)別。
2 內(nèi)存和尋址模式
2.1聲明靜態(tài)數(shù)據(jù)區(qū)
可以在X86匯編語言中用匯編指令.DATA聲明靜態(tài)數(shù)據(jù)區(qū)(類似于全局變量),數(shù)據(jù)以單字節(jié)、雙字節(jié)、或雙字(4字節(jié))的方式存放,分別用DB,DW, DD指令表示聲明內(nèi)存的長度。在匯編語言中,相鄰定義的標(biāo)簽在內(nèi)存中是連續(xù)存放的。
| .DATA | ? | ? | ? |
| var | DB?64?? | ;聲明一個(gè)字節(jié),并將數(shù)值64放入此字節(jié)中 | |
| var2 | DB?? | ; 聲明一個(gè)為初始化的字節(jié). | |
| ? | DB?10 | ; 聲明一個(gè)沒有l(wèi)abel的字節(jié),其值為10. | |
| X | DW?? | ;?聲明一個(gè)雙字節(jié),未初始化. | |
| Y | DD?30000???? | ; 聲明一個(gè)4字節(jié),其值為30000. |
還可以聲明連續(xù)的數(shù)據(jù)和數(shù)組,聲明數(shù)組時(shí)使用DUP關(guān)鍵字
| Z | DD 1, 2, 3 | ; Declare three 4-byte values, initialized to 1, 2, and 3. The value of location?Z + 8?will be 3. |
| bytes?? | DB 10 DUP(?) | ; Declare 10 uninitialized bytes starting at location?bytes. |
| arr | DD 100 DUP(0)???? | ; Declare 100 4-byte words starting at location?arr, all initialized to 0 |
| str | DB 'hello',0 | ; Declare 6 bytes starting at the address str, initialized to the ASCII character values for?hello?and the null (0) byte. |
2.2 尋址模式
現(xiàn)代X86處理器具有232字節(jié)的尋址空間。在上面的例子中,我們用標(biāo)簽(label)表示內(nèi)存區(qū)域,這些標(biāo)簽在實(shí)際匯編時(shí),均被32位的實(shí)際地址代替。除了支持這種直接的內(nèi)存區(qū)域描述,X86還提供了一種靈活的內(nèi)存尋址方式,即利用最多兩個(gè)32位的寄存器和一個(gè)32位的有符號常數(shù)相加計(jì)算一個(gè)內(nèi)存地址,其中一個(gè)寄存器可以左移1、2或3位以表述更大的空間。下面例子是匯編程序中常見的方式
| mov eax, [ebx] | ; 將ebx值指示的內(nèi)存地址中的4個(gè)字節(jié)傳送到eax中 |
| mov [var], ebx | ;?將ebx的內(nèi)容傳送到var的值指示的內(nèi)存地址中. |
| mov eax, [esi-4] | ; 將esi-4值指示的內(nèi)存地址中的4個(gè)字節(jié)傳送到eax中 |
| mov [esi+eax], cl | ; 將cl的值傳送到esi+eax的值指示的內(nèi)存地址中 |
| mov edx, [esi+4*ebx]???? | ; 將esi+4*ebx值指示的內(nèi)存中的4個(gè)字節(jié)傳送到edx |
下面是違反規(guī)則的例子:
| mov eax, [ebx-ecx] | ; 只能用加法 |
| mov [eax+esi+edi], ebx???? | ; 最多只能有兩個(gè)寄存器參與運(yùn)算 |
2.3 長度規(guī)定
在聲明內(nèi)存大小時(shí),在匯編語言中,一般用DB,DW,DD均可聲明的內(nèi)存空間大小,這種現(xiàn)實(shí)聲明能夠很好地指導(dǎo)匯編器分配內(nèi)存空間,但是,對于
mov [ebx], 2
如果沒有特殊的標(biāo)識,則不確定常數(shù)2是單字節(jié)、雙字節(jié),還是雙字。對于這種情況,X86提供了三個(gè)指示規(guī)則標(biāo)記,分別為BYTE PTR,?WORD PTR, and?DWORD PTR,如上面例子寫成:mov?BYTE PTR?[ebx], 2,?mov WORD PTR [ebx], 2,?mov?DWORD PTR?[ebx], 2,則意思非常清晰。
3 匯編指令
匯編指令通常可以分為數(shù)據(jù)傳送指令、邏輯計(jì)算指令和控制流指令。本節(jié)將講述其中最重要的指令,以下標(biāo)記分別表示寄存器、內(nèi)存和常數(shù)。
| <reg32>???? | 32位寄存器 (EAX,?EBX,?ECX,?EDX,?ESI,?EDI,?ESP, or?EBP) |
| <reg16> | 16位寄存器 (AX,?BX,?CX, or?DX) |
| <reg8> | 8位寄存器(AH,?BH,?CH,?DH,?AL,?BL,?CL, or?DL) |
| <reg> | 任何寄存器 |
| ? | ? |
| <mem> | 內(nèi)存地址 (e.g.,?[eax],?[var + 4], or?dword ptr [eax+ebx]) |
| <con32> | 32為常數(shù) |
| <con16> | 16位常數(shù) |
| <con8> | 8位常數(shù) |
| <con> | 任何8位、16位或32位常數(shù) |
3.1 數(shù)據(jù)傳送指令
mov?— Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)
mov指令將第二個(gè)操作數(shù)(可以是寄存器的內(nèi)容、內(nèi)存中的內(nèi)容或值)復(fù)制到第一個(gè)操作數(shù)(寄存器或內(nèi)存)。mov不能用于直接從內(nèi)存復(fù)制到內(nèi)存,其語法如下所示:
mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<const>
mov <mem>,<const>
Examples
mov eax, ebx?— 將ebx的值拷貝到eax
mov byte ptr [var], 5?— 將5保存找var指示內(nèi)存中的一個(gè)字節(jié)中
push— Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)
push指令將操作數(shù)壓入內(nèi)存的棧中,棧是程序設(shè)計(jì)中一種非常重要的數(shù)據(jù)結(jié)構(gòu),其主要用于函數(shù)調(diào)用過程中,其中ESP只是棧頂。在壓棧前,首先將ESP值減4(X86棧增長方向與內(nèi)存地址編號增長方向相反),然后將操作數(shù)內(nèi)容壓入ESP指示的位置。其語法如下所示:
push <reg32>
push <mem>
push <con32>
Examples
push eax?— 將eax內(nèi)容壓棧
push [var]?— 將var指示的4直接內(nèi)容壓棧
pop— Pop stack
pop指令與push指令相反,它執(zhí)行的是出棧的工作。它首先將ESP指示的地址中的內(nèi)容出棧,然后將ESP值加4. 其語法如下所示:
pop <reg32>
pop <mem>
Examples
pop edi?— pop the top element of the stack into EDI.
pop [ebx]?— pop the top element of the stack into memory at the four bytes starting at location EBX.
lea— Load effective address
?lea實(shí)際上是一個(gè)載入有效地址指令,將第二個(gè)操作數(shù)表示的地址載入到第一個(gè)操作數(shù)(寄存器)中。其語法如下所示:
Syntax
lea <reg32>,<mem>
Examples
lea eax, [var]?— var指示的地址載入eax中.
lea edi, [ebx+4*esi]?— ebx+4*esi表示的地址載入到edi中,這實(shí)際是上面所說的尋址模式的一種表示方式.
3.2 算術(shù)和邏輯指令
add— Integer Addition
add指令將兩個(gè)操作數(shù)相加,且將相加后的結(jié)果保存到第一個(gè)操作數(shù)中。其語法如下所示:
add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<con>
add <mem>,<con>
Examples
add eax, 10?— EAX ← EAX + 10
add BYTE PTR [var], 10?— 10與var指示的內(nèi)存中的一個(gè)byte的值相加,并將結(jié)果保存在var指示的內(nèi)存中
sub— Integer Subtraction
sub指令指示第一個(gè)操作數(shù)減去第二個(gè)操作數(shù),并將相減后的值保存在第一個(gè)操作數(shù),其語法如下所示:
sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<con>
sub <mem>,<con>
Examples
sub al, ah?— AL ← AL - AH
sub eax, 216?— eax中的值減26,并將計(jì)算值保存在eax中
inc, dec— Increment, Decrement
inc,dec分別表示將操作數(shù)自加1,自減1,其語法如下所示:
inc <reg>
inc <mem>
dec <reg>
dec <mem>
Examples
dec eax?— eax中的值自減1.
inc DWORD PTR [var]?—?var指示內(nèi)存中的一個(gè)4-byte值自加1
imul— Integer Multiplication
整數(shù)相乘指令,它有兩種指令格式,一種為兩個(gè)操作數(shù),將兩個(gè)操作數(shù)的值相乘,并將結(jié)果保存在第一個(gè)操作數(shù)中,第一個(gè)操作數(shù)必須為寄存器;第二種格式為三個(gè)操作數(shù),其語義為:將第二個(gè)和第三個(gè)操作數(shù)相乘,并將結(jié)果保存在第一個(gè)操作數(shù)中,第一個(gè)操作數(shù)必須為寄存器。其語法如下所示:
imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<con>
imul <reg32>,<mem>,<con>
Examples
imul eax, [var]?— eax→ eax * [var] imul esi, edi, 25?— ESI → EDI * 25idiv— Integer Division
idiv指令完成整數(shù)除法操作,idiv只有一個(gè)操作數(shù),此操作數(shù)為除數(shù),而被除數(shù)則為EDX:EAX中的內(nèi)容(一個(gè)64位的整數(shù)),操作的結(jié)果有兩部分:商和余數(shù),其中商放在eax寄存器中,而余數(shù)則放在edx寄存器中。其語法如下所示:
Syntax
idiv <reg32>
idiv <mem>
Examples
idiv ebx idiv DWORD PTR [var] ? and, or, xor— Bitwise logical and, or and exclusive or 邏輯與、邏輯或、邏輯異或操作指令,用于操作數(shù)的位操作,操作結(jié)果放在第一個(gè)操作數(shù)中。其語法如下所示: and <reg>,<reg>and <reg>,<mem>
and <mem>,<reg>
and <reg>,<con>
and <mem>,<con>
or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<con>
or <mem>,<con>
xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<con>
xor <mem>,<con>
Examples
and eax, 0fH?— 將eax中的錢28位全部置為0,最后4位保持不變.
xor edx, edx?— 設(shè)置edx中的內(nèi)容為0.
not— Bitwise Logical Not
位翻轉(zhuǎn)指令,將操作數(shù)中的每一位翻轉(zhuǎn),即0->1, 1->0。其語法如下所示:
not <reg>
not <mem>
Example
not BYTE PTR [var]?—?將var指示的一個(gè)字節(jié)中的所有位翻轉(zhuǎn).
neg— Negate
取負(fù)指令。語法為:
neg <reg>
neg <mem>
Example
neg eax?— EAX → - EAX
shl, shr— Shift Left, Shift Right
位移指令,有兩個(gè)操作數(shù),第一個(gè)操作數(shù)表示被操作數(shù),第二個(gè)操作數(shù)指示位移的數(shù)量。其語法如下所示:
shl <reg>,<con8>
shl <mem>,<con8>
shl <reg>,<cl>
shl <mem>,<cl>
shr <reg>,<con8>
shr <mem>,<con8>
shr <reg>,<cl>
shr <mem>,<cl>
Examples
shl eax, 1?— Multiply the value of EAX by 2 (if the most significant bit is 0),左移1位,相當(dāng)于乘以2 shr ebx, cl?— Store in EBX the floor of result of dividing the value of EBX by 2n?where?n?is the value in?CL. 3.3 控制轉(zhuǎn)移指令 X86處理器維持著一個(gè)指示當(dāng)前執(zhí)行指令的指令指針(IP),當(dāng)一條指令執(zhí)行后,此指針自動(dòng)指向下一條指令。IP寄存器不能直接操作,但是可以用控制流指令更新。 一般用標(biāo)簽(label)指示程序中的指令地址,在X86匯編代碼中,可以在任何指令前加入標(biāo)簽。如: mov esi, [ebp+8] begin: xor ecx, ecxmov eax, [esi]如第二條指令用begin指示,這種標(biāo)簽的方法在某種程度上簡化了匯編程序設(shè)計(jì),控制流指令通過標(biāo)簽實(shí)現(xiàn)程序指令跳轉(zhuǎn)。
jmp?— Jump
控制轉(zhuǎn)移到label所指示的地址,(從label中取出執(zhí)行執(zhí)行),如下所示:
jmp <label>
Example
jmp begin?— Jump to the instruction labeled?begin.
jcondition— Conditional Jump
條件轉(zhuǎn)移指令,條件轉(zhuǎn)移指令依據(jù)機(jī)器狀態(tài)字中的一些列條件狀態(tài)轉(zhuǎn)移。機(jī)器狀態(tài)字中包括指示最后一個(gè)算數(shù)運(yùn)算結(jié)果是否為0,運(yùn)算結(jié)果是否為負(fù)數(shù)等。機(jī)器狀態(tài)字具體解釋請見微機(jī)原理、計(jì)算機(jī)組成等課程。語法如下所示:
je <label>?(jump when equal)
jne <label>?(jump when not equal)
jz <label>?(jump when last result was zero)
jg <label>?(jump when greater than)
jge <label>?(jump when greater than or equal to)
jl <label>?(jump when less than)
jle <label>(jump when less than or equal to)
Example
cmp eax, ebx
jle done? , 如果eax中的值小于ebx中的值,跳轉(zhuǎn)到done指示的區(qū)域執(zhí)行,否則,執(zhí)行下一條指令。
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<con>
Example
cmp DWORD PTR [var], 10
jeq loop,?
在X86中,棧增長方向與內(nèi)存編號增長方向相反。
Caller Rules
調(diào)用者規(guī)則包括一系列操作,描述如下:
1)在調(diào)用子程序之前,調(diào)用者應(yīng)該保存一系列被設(shè)計(jì)為調(diào)用者保存的寄存器的值。調(diào)用者保存寄存器有eax,ecx,edx。由于被調(diào)用的子程序會修改這些寄存器,所以為了在調(diào)用子程序完成之后能正確執(zhí)行,調(diào)用者必須在調(diào)用子程序之前將這些寄存器的值入棧。
2)在調(diào)用子程序之前,將參數(shù)入棧。參數(shù)入棧的順序應(yīng)該是從最后一個(gè)參數(shù)開始,如上圖中parameter3先入棧。
3)利用call指令調(diào)用子程序。這條指令將返回地址放置在參數(shù)的上面,并進(jìn)入子程序的指令執(zhí)行。(子程序的執(zhí)行將按照被調(diào)用者的規(guī)則執(zhí)行)
當(dāng)子程序返回時(shí),調(diào)用者期望找到子程序保存在eax中的返回地址。為了恢復(fù)調(diào)用子程序執(zhí)行之前的狀態(tài),調(diào)用者應(yīng)該執(zhí)行以下操作:
1)清除棧中的參數(shù);
2)將棧中保存的eax值、ecx值以及edx值出棧,恢復(fù)eax、ecx、edx的值(當(dāng)然,如果其它寄存器在調(diào)用之前需要保存,也需要完成類似入棧和出棧操作)
Example?
如下代碼展示了一個(gè)調(diào)用子程序的調(diào)用者應(yīng)該執(zhí)行的操作。此匯編程序調(diào)用一個(gè)具有三個(gè)參數(shù)的函數(shù)_myFunc,其中第一個(gè)參數(shù)為eax,第二個(gè)參數(shù)為常數(shù)216,第三個(gè)參數(shù)為var指示的內(nèi)存中的值。
在調(diào)用返回時(shí),調(diào)用者必須清除棧中的相應(yīng)內(nèi)容,在上例中,參數(shù)占有12個(gè)字節(jié),為了消除這些參數(shù),只需將ESP加12即可。
?_myFunc的值保存在eax中,ecx和edx中的值也許已經(jīng)被改變,調(diào)用者還必須在調(diào)用之前保存在棧中,并在調(diào)用結(jié)束之后,出棧恢復(fù)ecx和edx的值。
被調(diào)用者規(guī)則
被調(diào)用者應(yīng)該遵循如下規(guī)則:
1)將ebp入棧,并將esp中的值拷貝到ebp中,其匯編代碼如下:
push ebpmov ebp, esp上述代碼的目的是保存調(diào)用子程序之前的基址指針,基址指針用于尋找棧上的參數(shù)和局部變量。當(dāng)一個(gè)子程序開始執(zhí)行時(shí),基址指針保存棧指針指示子程序的執(zhí)行。為了在子程序完成之后調(diào)用者能正確定位調(diào)用者的參數(shù)和局部變量,ebp的值需要返回。
2)在棧上為局部變量分配空間。
3)保存callee-saved寄存器的值,callee-saved寄存器包括ebx,edi和esi,將ebx,edi和esi壓棧。
4)在上述三個(gè)步驟完成之后,子程序開始執(zhí)行,當(dāng)子程序返回時(shí),必須完成如下工作:
4.1)將返回的執(zhí)行結(jié)果保存在eax中
4.2)彈出棧中保存的callee-saved寄存器值,恢復(fù)callee-saved寄存器的值(ESI和EDI)
4.3)收回局部變量的內(nèi)存空間。實(shí)際處理時(shí),通過改變EBP的值即可:mov?esp,?ebp。?
4.4)通過彈出棧中保存的ebp值恢復(fù)調(diào)用者的基址寄存器值。
4.5)執(zhí)行ret指令返回到調(diào)用者程序。
After these three actions are performed, the body of the subroutine may proceed. When the subroutine is returns, it must follow these steps:
Example
子程序首先通過入棧的手段保存ebp,分配局部變量,保存寄存器的值。
在子程序體中,參數(shù)和局部變量均是通過ebp進(jìn)行計(jì)算。由于參數(shù)傳遞在子程序被調(diào)用之前,所以參數(shù)總是在ebp指示的地址的下方(在棧中),因此,上例中的第一個(gè)參數(shù)的地址是ebp+8,第二個(gè)參數(shù)的地址是ebp+12,第三個(gè)參數(shù)的地址是ebp+16;而局部變量在ebp指示的地址的上方,所有第一個(gè)局部變量的地址是ebp-4,而第二個(gè)這是ebp-8.
轉(zhuǎn)載于:https://www.cnblogs.com/timeObjserver/p/9455186.html
總結(jié)
- 上一篇: python中关于input和raw_i
- 下一篇: Codeforces 372 B. Co