寄存器理解 及 X86彙編入門

本文整理自多材料源,感謝原址分享,請查看末尾Urlhtml

I, 彙編語言分類:程序員

彙編語言和CPU息息相關,可是不能把彙編語言徹底等同於CPU的機器指令。不一樣架構的CPU指令並不相同,如x86powerpcarm各有各的指令系統;甚至同一種架構的CPU有幾套指令集,典型的如arm除了有32位的指令集外,還有一套16位的thumb指令集。可是做爲開發語言的彙編,本質上是一套語法規則和助記符的集合,它能夠包容不一樣的指令集。若是從CPU體系來劃分,常見的彙編有兩種:IBM PC彙編和ARM彙編。編程

IBM PC彙編也就是Intel的彙編,由於IBM 最先推出PC機,後來的體系不少都要和它兼容,因此也使用了相同的彙編語言。ARM壓根沒考慮過兼容,它的指令集和x86徹底是兩個體系,因此彙編語言也獨立發展出一套。windows

CPU只是限定了機器碼,做爲開發語言的彙編,其實還和編譯器息息相關。彙編語言出現的早,沒有像C語言同樣定義出標準,因此編譯器的廠商各搞一套。到如今,最有名的也是兩家:MASMGNU ASM。前者是微軟的,只支持x86,用在DOS/Windows平臺中;後者是開源產品,主要用在Linux中,基本上支持大部分的CPU架構。這二者的區別在於僞指令的不一樣,僞指令是用來告訴編譯器如何工做的,和編譯器相關,和CPU無關。其實彙編的編譯至關簡單,這兩套僞指令只是符號不相同,含義是大同小異,明白了一種,看另外一種就很容易了。數組

從彙編格式分,還有Intel格式和AT&T格式的區別,前者是Intel的,windows平臺常見,後者最先由貝爾實驗室推出,用於Unix中,GUN彙編器的缺省格式就是AT&T。不過GNU的彙編器和調試器gdb對這兩種格式都支持,能夠隨便切換。MASM只支持Intel格式。Intel格式和AT&T格式的區別只是符號系統的區別,這與x86arm的區別可不同,後者是CPU體系的區別。緩存

所謂 內嵌彙編,它是用於C語言和彙編語言混合編程的,因此和編譯器也關係緊密,目前也是有兩種,GNU的內嵌彙編和MASM的內嵌彙編,它們的語法和普通匯編是有區別的,特別是GNU的內嵌彙編不是很容易看懂,須要專門學習才行。MASM的內嵌彙編和普通匯編的區別則不大。數據結構

關於彙編語言的種類,能夠說有多少種不一樣內核的CPU,就有多少種彙編語言。彙編並非只有8086/8088彙編,還有8051,ARM,Alpha,MIPS彙編等等...
如你所知, 彙編是一種面向機器的編程語言,之因此說面向機器是指它的指令系統與具體的CPU芯片相關聯,一般不一樣CPU硬件有不一樣的彙編系統。8086&8088分別是Intel公司的16位和準16位的CPU,一般使用它做爲教材講解微機機系統原理,是由於80x86系列CPU應用普遍,具備表明性。
架構

8051主要應用在單片機,ARM彙編用於ARM處理器...不須要解釋。less

8086是INTEL公司推出的最先實際應用到微型我的計算機上CPU芯片型號;80x86是在8086基礎上的加強型,包括80286,80386,80486,其後就改稱奔騰了。大的區別上:8086和80286是16位的CPU,80386和80486是32位CPU;80486還多了數學輔助處理器,加強了複雜的數學運算能力。小的區別上就比較多了,如頻率愈來愈快,包括寄存器的增長等。編程語言

和C語言不一樣,彙編語言更多的針對特定CPU內核,所以,不一樣內核的CPU,必須有對應的彙編語言編譯器將彙編語言別寫的程序編譯成對應CPU的機器語言代碼,CPU才能正確識別和執行這些代碼。

 

II, 寄存器概念

寄存器是CPU裏的東西,內存是掛在CPU外面的數據總線上的,

訪問內存時要在CPU的寄存器填上地址,再執行相應的彙編指令,這時CPU會在數據總線上生成讀取或寫入內存數據的時鐘信號,最終內存的內容會被CPU寄存器的內容更新(寫入)或被讀入CPU的寄存器(讀取)
不僅是PC上的CPU,全部的嵌入式CPU,單片機都一個樣

首先明確一點:

CPU  <--- > 寄存器<--- > 緩存<--- >內存

寄存器的工做方式很簡單,只有兩步:(1)找到相關的位,(2)讀取這些位。

 

內存的工做方式就要複雜得多:

 

(1)找到數據的指針。(指針可能存放在寄存器內,因此這一步就已經包括寄存器的所有工做了。)

 

(2)將指針送往內存管理單元(MMU),由MMU將虛擬的內存地址翻譯成實際的物理地址。

 

(3)將物理地址送往內存控制器(memory controller),由內存控制器找出該地址在哪一根內存插槽(bank)上。

 

(4)肯定數據在哪個內存塊(chunk)上,從該塊讀取數據。

 

(5)數據先送回內存控制器,再送回CPU,而後開始使用。

 

內存的工做流程比寄存器多出許多步。每一步都會產生延遲,累積起來就使得內存比寄存器慢得多。

 

爲了緩解寄存器與內存之間的巨大速度差別,硬件設計師作出了許多努力,包括在CPU內部設置緩存、優化CPU工做方式,儘可能一次性從內存讀取指令所要用到的所有數據等等。

 

 

寄存器、存儲器、內存之間的關係:

存儲器 涵蓋了全部關於存儲的範疇,寄存器和內存都屬於該範疇。

寄存器是中央處理器內的組成部份。它跟CPU有關。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數據和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器(PC)。在中央處理器的算術及邏輯部件中,包含的寄存器有累加器(ACC)。

內存,即 內部存儲器 ,通常分爲只讀存儲器和隨即存儲器,以及最強悍的高速緩衝存儲器(CACHE),只讀存儲器應用普遍,它一般是一塊在硬件上集成的可讀芯片,做用是識別與控制硬件,它的特色是隻可讀取,不能寫入。隨機存儲器的特色是可讀可寫,斷電後一切數據都消失,咱們所說的內存條就是指它了。
CACHE是在CPU中速度很是塊,而容量卻很小的一種存儲器,它是計算機存儲器中最強悍的存儲器。因爲技術限制,容量很難提高,通常都不過兆。

所以,堆棧概念不該與寄存器混淆,堆Heap 棧 Stack 概念存在於程序的內存分配環節

 

主要寄存器以下圖所示:

寄存器結構

 

X86處理器中有8個32位的通用寄存器。因爲歷史的緣由,EAX一般用於計算,ECX一般用於循環變量計數。ESP和EBP有專門用途,ESP指示棧指針(用於指示棧頂位置),而EBP則是基址指針(用於指示子程序或函數調用的基址指針)。如圖中所示,EAX、EBX、ECX和EDX的前兩個高位字節和後兩個低位字節能夠獨立使用,其中兩位低字節又被獨立分爲H和L部分,這樣作的緣由主要是考慮兼容16位的程序,具體兼容匹配細節請查閱相關文獻。

應用寄存器時,其名稱大小寫是不敏感的,如EAX和eax沒有區別。

更詳細一些的介紹圖:

 

 

下面經過一個具體的C代碼反彙編的彙編代碼分析加深對這些經常使用代碼的理解,實驗環境是實驗樓32位Linux虛擬機。

具體C代碼以下:

經過  gcc –S –o main.s main.c -m32  指令將代碼編譯成彙編代碼,精簡後的彙編代碼以下:

下面將着重分析上面這段代碼。首先,彙編代碼也是從main開始執行,首先將ebp寄存器值入棧,而後ebp指向esp位置,esp值減4以後將數字10存在esp指向的位置,最後調將eip入棧,同時eip指向函數f的起始位置。

f函數首先也是ebp入棧,而後ebp指向esp位置,esp值減4以後將ebp位置加8位置的值,也就是數字10保存到eax寄存器中,而後將eax中的值也就是10保存到esp中,最後將eip入棧,調用函數g。

g函數也是相同的操做,ebp入棧,ebp指向esp位置,ebp地址減8處的值10放進eax,而後eax中的數值增長5,而後出棧到ebp,ebp只想24地址處。而後ret,也就是esp處值出棧到eip,eip=15。

而後又回到f函數的15指令處執行,eax寄存器的值增長4,變成19,而後執行leave指令,也就是esp指向ebp處,而後esp處值出棧到ebp,而後esp處值出棧到eip,程序下面跳轉至24行指令。

指令又回到main函數執行,首先eax值加8,變成27,而後執行leave指令,也就是esp指向ebp處,而後esp處值出棧到ebp,而後esp處值出棧到eip,程序下面跳轉至main函數開始前的地方繼續執行。

 

 

III,  內存和尋址模式

III.1聲明靜態數據區

能夠在X86彙編語言中用匯編指令.DATA聲明靜態數據區(相似於全局變量),數據以單字節、雙字節、或雙字(4字節)的方式存放,分別用DB,DW, DD指令表示聲明內存的長度。在彙編語言中,相鄰定義的標籤在內存中是連續存放的。

.DATA      
var DB 64   ;聲明一個字節,並將數值64放入此字節中
var2 DB ? ; 聲明一個爲初始化的字節.
  DB 10 ; 聲明一個沒有label的字節,其值爲10.
X DW ? ; 聲明一個雙字節,未初始化.
Y DD 30000     ; 聲明一個4字節,其值爲30000.

還能夠聲明連續的數據和數組,聲明數組時使用DUP關鍵字

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.

III,2 尋址模式

現代X86處理器具備232字節的尋址空間。在上面的例子中,咱們用標籤(label)表示內存區域,這些標籤在實際彙編時,均被32位的實際地址代替。除了支持這種直接的內存區域描述,X86還提供了一種靈活的內存尋址方式,即利用最多兩個32位的寄存器和一個32位的有符號常數相加計算一個內存地址,其中一個寄存器能夠左移一、2或3位以表述更大的空間。下面例子是彙編程序中常見的方式

mov eax, [ebx] ; 將ebx值指示的內存地址中的4個字節傳送到eax中
mov [var], ebx ; 將ebx的內容傳送到var的值指示的內存地址中.
mov eax, [esi-4] ; 將esi-4值指示的內存地址中的4個字節傳送到eax中
mov [esi+eax], cl ; 將cl的值傳送到esi+eax的值指示的內存地址中
mov edx, [esi+4*ebx]     ; 將esi+4*ebx值指示的內存中的4個字節傳送到edx

下面是違反規則的例子:

mov eax, [ebx-ecx] ; 只能用加法
mov [eax+esi+edi], ebx     ; 最多隻能有兩個寄存器參與運算

III,3 長度規定

在聲明內存大小時,在彙編語言中,通常用DB,DW,DD都可聲明的內存空間大小,這種現實聲明可以很好地指導彙編器分配內存空間,可是,對於

mov [ebx], 2

若是沒有特殊的標識,則不肯定常數2是單字節、雙字節,仍是雙字。對於這種狀況,X86提供了三個指示規則標記,分別爲BYTE PTR, WORD PTR, and DWORD PTR,如上面例子寫成:mov BYTE PTR [ebx], 2, mov WORD PTR [ebx], 2, mov DWORD PTR [ebx], 2,則意思很是清晰。

 

IV.  彙編指令

彙編指令一般能夠分爲數據傳送指令、邏輯計算指令和控制流指令。本節將講述其中最重要的指令,如下標記分別表示寄存器、內存和常數。

<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> 內存地址 (e.g., [eax], [var + 4], or dword ptr [eax+ebx])
<con32> 32爲常數
<con16> 16位常數
<con8> 8位常數
<con> 任何8位、16位或32位常數

IV.  1 數據傳送指令

mov — Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)

mov指令將第二個操做數(能夠是寄存器的內容、內存中的內容或值)複製到第一個操做數(寄存器或內存)。mov不能用於直接從內存複製到內存,其語法以下所示:

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指示內存中的一個字節中

push— Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)

push指令將操做數壓入內存的棧中,棧是程序設計中一種很是重要的數據結構,其主要用於函數調用過程當中,其中ESP只是棧頂。在壓棧前,首先將ESP值減4(X86棧增加方向與內存地址編號增加方向相反),而後將操做數內容壓入ESP指示的位置。其語法以下所示:

push <reg32> push <mem> push <con32>

 

Examples
push eax — 將eax內容壓棧
push [var] — 將var指示的4直接內容壓棧

pop— Pop stack

pop指令與push指令相反,它執行的是出棧的工做。它首先將ESP指示的地址中的內容出棧,而後將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其實是一個載入有效地址指令,將第二個操做數表示的地址載入到第一個操做數(寄存器)中。其語法以下所示:

Syntax
lea <reg32>,<mem>

Examples
lea eax, [var] — var指示的地址載入eax中.
lea edi, [ebx+4*esi] — ebx+4*esi表示的地址載入到edi中,這實際是上面所說的尋址模式的一種表示方式.

 

IV.  2 算術和邏輯指令

add— Integer Addition

add指令將兩個操做數相加,且將相加後的結果保存到第一個操做數中。其語法以下所示:

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指示的內存中的一個byte的值相加,並將結果保存在var指示的內存中

sub— Integer Subtraction

sub指令指示第一個操做數減去第二個操做數,並將相減後的值保存在第一個操做數,其語法以下所示:

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,並將計算值保存在eax中

inc, dec— Increment, Decrement

inc,dec分別表示將操做數自加1,自減1,其語法以下所示:

inc <reg> inc <mem> dec <reg> dec <mem>

Examples
dec eax — eax中的值自減1.
inc DWORD PTR [var] — var指示內存中的一個4-byte值自加1

imul— Integer Multiplication

整數相乘指令,它有兩種指令格式,一種爲兩個操做數,將兩個操做數的值相乘,並將結果保存在第一個操做數中,第一個操做數必須爲寄存器;第二種格式爲三個操做數,其語義爲:將第二個和第三個操做數相乘,並將結果保存在第一個操做數中,第一個操做數必須爲寄存器。其語法以下所示:

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 * 25

idiv— Integer Division

idiv指令完成整數除法操做,idiv只有一個操做數,此操做數爲除數,而被除數則爲EDX:EAX中的內容(一個64位的整數),操做的結果有兩部分:商和餘數,其中商放在eax寄存器中,而餘數則放在edx寄存器中。其語法以下所示:

 Syntax idiv <reg32> idiv <mem> 

Examples

 idiv ebx idiv DWORD PTR [var]

 

and, or, xor— Bitwise logical and, or and exclusive or
邏輯與、邏輯或、邏輯異或操做指令,用於操做數的位操做,操做結果放在第一個操做數中。其語法以下所示:
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 — 設置edx中的內容爲0. 

 

 

not— Bitwise Logical Not

位翻轉指令,將操做數中的每一位翻轉,即0->1, 1->0。其語法以下所示:

not <reg>
not <mem>

Example
not BYTE PTR [var] — 將var指示的一個字節中的全部位翻轉.

neg— Negate

取負指令。語法爲:

neg <reg>
neg <mem>

Example

neg eax — EAX → - EAX shl, shr— Shift Left, Shift Right

 

 

 

位移指令,有兩個操做數,第一個操做數表示被操做數,第二個操做數指示位移的數量。其語法以下所示:

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位,至關於乘以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.

 

 
IV.  3 控制轉移指令
X86處理器維持着一個指示當前執行指令的指令指針(IP),當一條指令執行後,此指針自動指向下一條指令。IP寄存器不能直接操做,可是能夠用控制流指令更新。
通常用標籤(label)指示程序中的指令地址,在X86彙編代碼中,能夠在任何指令前加入標籤。如:
       mov esi, [ebp+8] begin: xor ecx, ecx mov eax, [esi]

 

如第二條指令用begin指示,這種標籤的方法在某種程度上簡化了彙編程序設計,控制流指令經過標籤實現程序指令跳轉。

jmp — Jump

控制轉移到label所指示的地址,(從label中取出執行執行),以下所示:

jmp <label>

Example

jmp begin — Jump to the instruction labeled begin. 

 

jcondition— Conditional Jump

條件轉移指令,條件轉移指令依據機器狀態字中的一些列條件狀態轉移。機器狀態字中包括指示最後一個算數運算結果是否爲0,運算結果是否爲負數等。機器狀態字具體解釋請見微機原理、計算機組成等課程。語法以下所示:

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中的值,跳轉到done指示的區域執行,不然,執行下一條指令。 

 

cmp— Compare
cmp指令比較兩個操做數的值,並根據比較結果設置機器狀態字中的條件碼。此指令與sub指令相似,可是cmp不用將計算結果保存在操做數中。其語法以下所示:
cmp <reg>,<reg> cmp <reg>,<mem> cmp <mem>,<reg> cmp <reg>,<con>

 

Example

cmp DWORD PTR [var], 10 jeq loop, 

 

 

 

比較var指示的4字節內容是否爲10,若是不是,則繼續執行下一條指令,不然,跳轉到loop指示的指令開始執行
 
call, ret— Subroutine call and return
這兩條指令實現子程序(過程、函數等意思)的調用及返回。call指令首先將當前執行指令地址入棧,而後無條件轉移到由標籤指示的指令。與其它簡單的跳轉指令不一樣,call指令保存調用以前的地址信息(當call指令結束後,返回到調用以前的地址)。
ret指令實現子程序的返回機制,ret指令彈出棧中保存的指令地址,而後無條件轉移到保存的指令地址執行。
call,ret是函數調用中最關鍵的兩條指令。具體細節見下面一部分的講解。語法爲:
call <label>
ret
 
V, 調用規則
爲了增強程序員之間的協做及簡化程序開發進程,設定一個函數調用規則很是必要,函數調用規則規定函數調用及返回的規則,只要遵守這種規則寫的程序都可以正確執行,從而程序員沒必要關心諸如參數如何傳遞等問題;另外一方面,在彙編語言中能夠調用符合這種規則的高級語言所寫的函數,從而將彙編語言程序與高級語言程序有機結合在一塊兒。
調用規則分爲兩個方面,及調用者規則和被調用者規則,如一個函數A調用一個函數B,則A被稱爲調用者(Caller),B被稱爲被調用者(Callee)。
下圖顯示一個調用過程當中的內存中的棧佈局:
棧佈局

在X86中,棧增加方向與內存編號增加方向相反。

Caller Rules

調用者規則包括一系列操做,描述以下:

1)在調用子程序以前,調用者應該保存一系列被設計爲調用者保存的寄存器的值。調用者保存寄存器有eax,ecx,edx。因爲被調用的子程序會修改這些寄存器,因此爲了在調用子程序完成以後能正確執行,調用者必須在調用子程序以前將這些寄存器的值入棧。

2)在調用子程序以前,將參數入棧。參數入棧的順序應該是從最後一個參數開始,如上圖中parameter3先入棧。

3)利用call指令調用子程序。這條指令將返回地址放置在參數的上面,並進入子程序的指令執行。(子程序的執行將按照被調用者的規則執行)

當子程序返回時,調用者指望找到子程序保存在eax中的返回地址。爲了恢復調用子程序執行以前的狀態,調用者應該執行如下操做:

1)清除棧中的參數;

2)將棧中保存的eax值、ecx值以及edx值出棧,恢復eax、ecx、edx的值(固然,若是其它寄存器在調用以前須要保存,也須要完成相似入棧和出棧操做)

Example

以下代碼展現了一個調用子程序的調用者應該執行的操做。此彙編程序調用一個具備三個參數的函數_myFunc,其中第一個參數爲eax,第二個參數爲常數216,第三個參數爲var指示的內存中的值。

push [var] ; Push last parameter first
push 216   ; Push the second parameter
push eax   ; Push first parameter last

call _myFunc ; Call the function (assume C naming)

add esp, 12

 

在調用返回時,調用者必須清除棧中的相應內容,在上例中,參數佔有12個字節,爲了消除這些參數,只需將ESP加12便可。

 _myFunc的值保存在eax中,ecx和edx中的值也許已經被改變,調用者還必須在調用以前保存在棧中,並在調用結束以後,出棧恢復ecx和edx的值。

VI, 被調用者規則

被調用者應該遵循以下規則:

1)將ebp入棧,並將esp中的值拷貝到ebp中,其彙編代碼以下:

    push ebp mov  ebp, esp

 

上述代碼的目的是保存調用子程序以前的基址指針,基址指針用於尋找棧上的參數和局部變量。當一個子程序開始執行時,基址指針保存棧指針指示子程序的執行。爲了在子程序完成以後調用者能正肯定位調用者的參數和局部變量,ebp的值須要返回。

2)在棧上爲局部變量分配空間。

3)保存callee-saved寄存器的值,callee-saved寄存器包括ebx,edi和esi,將ebx,edi和esi壓棧。

4)在上述三個步驟完成以後,子程序開始執行,當子程序返回時,必須完成以下工做:

  4.1)將返回的執行結果保存在eax中

  4.2)彈出棧中保存的callee-saved寄存器值,恢復callee-saved寄存器的值(ESI和EDI)

  4.3)收回局部變量的內存空間。實際處理時,經過改變EBP的值便可:mov esp, ebp。 

  4.4)經過彈出棧中保存的ebp值恢復調用者的基址寄存器值。

  4.5)執行ret指令返回到調用者程序。

After these three actions are performed, the body of the subroutine may proceed. When the subroutine is returns, it must follow these steps:

  1. Leave the return value in EAX.

Example

.486 .MODEL FLAT .CODE PUBLIC _myFunc _myFunc PROC ; Subroutine Prologue
  push ebp     ; Save the old base pointer value.
  mov ebp, esp ; Set the new base pointer value.
  sub esp, 4   ; Make room for one 4-byte local variable.
  push edi     ; Save the values of registers that the function
  push esi     ; will modify. This function uses EDI and ESI.
  ; (no need to save EBX, EBP, or ESP)

  ; Subroutine Body
  mov eax, [ebp+8]   ; Move value of parameter 1 into EAX
  mov esi, [ebp+12]  ; Move value of parameter 2 into ESI
  mov edi, [ebp+16]  ; Move value of parameter 3 into EDI

  mov [ebp-4], edi   ; Move EDI into the local variable
  add [ebp-4], esi   ; Add ESI into the local variable
  add eax, [ebp-4]   ; Add the contents of the local variable
                     ; into EAX (final result)

  ; Subroutine Epilogue 
  pop esi      ; Recover register values
  pop edi mov esp, ebp ; Deallocate local variables
  pop ebp ; Restore the caller's base pointer value
  ret _myFunc ENDP END

 

子程序首先經過入棧的手段保存ebp,分配局部變量,保存寄存器的值。

在子程序體中,參數和局部變量均是經過ebp進行計算。因爲參數傳遞在子程序被調用以前,因此參數老是在ebp指示的地址的下方(在棧中),所以,上例中的第一個參數的地址是ebp+8,第二個參數的地址是ebp+12,第三個參數的地址是ebp+16;而局部變量在ebp指示的地址的上方,全部第一個局部變量的地址是ebp-4,而第二個這是ebp-8.

 

具體的CPU-Register-Cache關係請看連接

 

參考文獻: 1. MIPS編程入門  https://www.cnblogs.com/thoupin/p/4018455.html

                  2.Linux系統分析入門--簡單彙編代碼分析   https://blog.csdn.net/ven_kon/article/details/57080849

      3 .X86彙編快速入門 https://www.cnblogs.com/YukiJohnson/archive/2012/10/27/2741836.html

      4. 內存、棧、堆的一點小總結 https://blog.csdn.net/hust_sheng/article/details/47947037

     5.寄存器、存儲器、內存的區別  https://blog.csdn.net/u012137644/article/details/21864955?locationNum=3

      6. Linux 2.x 內核對內存的管理 https://blog.csdn.net/yang_yulei/article/details/24385573

     7.計算機中內存、cache和寄存器之間的關係及區別  https://blog.csdn.net/hellojoy/article/details/54744231

                 8.  堆,棧區別理解  https://www.cnblogs.com/pomp/archive/2007/10/19/930145.html

相關文章
相關標籤/搜索