函數參數壓棧,棧幀ebp,esp怎樣移動的?

來源:互聯網
上載者:User

標籤:

壓棧一次esp-4,ebp不變

 

esp是棧頂指標寄存器,堆棧操作只和esp有關
比如有一個函數a,有兩個參數,一般是這樣的
PUSH 1 參數2壓棧,esp-4
PUSH 2 參數1壓棧,esp-4
CALL a 調用

 

a:
PUSH EBP 儲存ebp
MOV EBP,ESP 改變棧幀,以後訪問參數通過ebp,訪問局部變數通過esp
SUB ESP,8 分配局部變數空間

...
ADD ESP,8
POP EBP 恢複ebp
RETN 8 返回,esp+8

 

C語句對應彙編語句:

例如函數:

int aaa(int a,int b)
{
int c;
c=a+b;
return c;
}

aaa(1,2);

 

調試版aaa的代碼
PUSH EBP
MOV EBP,ESP
SUB ESP,4//分配局部變數空間,一個int是4個位元組
MOV EAX,DWORD PTR SS:[EBP+8]//讀取參數a
ADD EAX,DWORD PTR SS:[EBP+C]//加上參數b
MOV DWORD PTR SS:[EBP-4],EAX//儲存到局部變數c
MOV EAX,DWORD PTR SS:[EBP-4]//eax是傳回值
MOV ESP,EBP//恢複棧頂指標
POP EBP//恢複ebp
RETN//返回

 

調用
PUSH 2//參數2壓棧,esp-4
PUSH 1//參數1壓棧,esp-4
CALL aaa//調用函數
ADD ESP,8//esp+8,平衡堆棧,清除掉參數

發布版的就是這樣了,精簡掉了很多內容
MOV EAX,DWORD PTR SS:[ESP+8]
MOV ECX,DWORD PTR SS:[ESP+4]
ADD EAX,ECX
RETN

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

下面要講的是子程式如何存取參數,因為預設對堆棧操作的寄存器有 ESP 和 EBP,而 ESP是堆棧指標,無法暫借使用,所以一般使用 EBP 來存取堆棧,假定在一個調用中有兩個參數,而且在 push 第一個參數前的堆棧指標 ESP 為 X,那麼壓入兩個參數後的 ESP 為 X-8,程式開始執行 call 指令,call 指令把返回地址壓入堆棧,這時候 ESP 為 X-C,這時已經在子程式中了,我們可以開始使用 EBP 來存取參數了,但為了在返回時恢複 EBP 的值,我們還是再需要一句 push ebp 來先儲存 EBP 的值,這時 ESP 為 X-10,再執行一句 mov ebp,esp,根據可以看出,實際上這時候 [ebp + 8] 就是參數1,[ebp + c]就是參數2。另外,局部變數也是定義在堆棧中的,它們的位置一般放在 push ebp 儲存的 EBP 數值的後面,局部變數1、2對應的地址分別是 [ebp-4]、[ebp-8],下面是一個典型的子程式,可以完成第一個參數減去第二個參數,它的定義是:

 

   MyProc proto Var1,Var2 ;有兩個參數
   local lVar1,lVar2 ;有兩個局部變數

 

   注意,這裡的兩個 local 變數實際上沒有被用到,只是為了示範用,具體實現的代碼是:

 

MyProc proc

 

   push ebp
   mov ebp,esp
   sub esp,8
   mov eax,dword ptr [ebp + 8]
   sub eax,dword ptr [ebp + c]
   add esp,8
   pop ebp
   ret 8

 

MyProc endp

 

   現在對這個子程式分析一下,push ebp/mov ebp,esp 是例行的儲存和設定 EBP 的代碼,sub esp,8 在堆棧中留出兩個局部變數的空間,mov /add 陳述式完成相加,add esp,8 修正兩個局部變數使用的堆棧,ret 8 修正兩個參數使用的堆棧,相當於 ret / add esp,8 兩句代碼的效果。可以看出,這是一個標準的 Stdcall 約定的子程式,使用時最後一個參數先入堆棧,返回時由子程式進行堆棧修正。當然,這個子程式為了示範執行過程,使用了手工儲存 ebp 並設定局部變數的方法,實際上,386 處理器有兩條專用的指令是完成這個功能用的,那就是 Enter 和 Leave,Enter 語句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,這個 xxx 就是 Enter 的,Leave 則完成 add esp,xxx/pop ebp 的功能,所以上面的程式可以改成:

 

MyPorc proc

 

   enter 8,0

 

   mov eax,dword ptr [ebp + 8]
   sub eax,dword ptr [ebp + c]

 

   leave
   ret 8

 

MyProc endp

 

文章出處:飛諾網(www.diybl.com):http://www.diybl.com/course/3_program/hb/hbjs/20071226/93663_2.html

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

一:在分析彙編代碼時總是要遇到無數的 Call ,對於這些 Call ,盡量要根據 Call 之前傳遞的參數和 Call 的傳回值來判斷 Call 的功能。傳遞參數的工作必須由函數調用者和函數本身來協調,電腦提供了一種被稱為棧的資料結構來支援參數傳遞。 
    當參數個數多於一個時,按照什麼順序把參數壓入堆棧。函數調用後,由誰來把堆棧恢複。在進階語言中,通過函數呼叫慣例來說明這兩個問題。常見的呼叫慣例有:

二:堆棧架構也稱為活動記錄,它為程式的返回地址,傳遞進來的參數,儲存的寄存器喝局部變數儲存的堆棧空間。堆棧架構是按以下的步驟建立的。

1 :參數被壓入堆棧。

2 :過程被調用,返回地址被壓入堆棧。

3 :過程開始執行時, EBP 被壓入堆棧。

4 :使 EBP 和 ESP 的值相等,從這裡開始, EBP 就作為定址參數的基址指標。

5 :可以從 ESP 中減掉一個數值來給過程的局部變數建立空間。

 

【例】按 __stdcall 約定調用函數 test2(Par1, Par2)   


        push par2  ;  參數 2              ; 參數被壓入堆棧(從右向左) 
        push par1  ;  參數 1              ;
        call test2;                            ; 過程被調用 
        {                                ; 過程開始執行 
             push ebp                    ; EBP 被壓入堆棧,保護現場原先的 EBP 指標 
             mov  ebp, esp            ; 使 EBP=ESP,  設定新的 EBP 指標,指向棧頂,

; 從這裡開始, EBP 就作為定址參數的基址指標。 
             mov  eax, [ebp+0C]  ;  調用參數 2
             mov  ebx, [ebp+08]   ;  調用參數 1
             sub  esp, 8              ;  若函數要用局部變數,則要在堆棧中留出點空間

; 從 ESP 中減掉一個數值來給過程的局部變數建立空間 
             …
             add  esp, 8                 ;  釋放局部變數佔用的堆棧 
             pop  ebp                    ;  恢複現場的 ebp 指標 
             ret  8                      ;  返回(相當於 ret; add esp,8 ) 
        }

三 : 其堆棧調用:(編譯原理的教程中說的更清楚)

 

 

四 : 例子

 

 

00401000  /$ 6A04        push    4               ; /Arg2 = 00000004

00401002  |. 6A03         push    3                ; |Arg1 = 00000003

00401004  |.  E8 16000000   call   0040101F          ; \local.0040101F

00401009  |.  8BD8         mov     ebx, eax

0040100B  |. 6A00         push    0                ; /ExitCode = 0

0040100D  \.  FF15 00204000 call    dword ptr [<&KERNEL32.ExitProces>; \ExitProcess

 

 

 

 

 

00401013      00            db      00

00401014      00            db      00

00401015      00            db      00

00401016      00            db      00

00401017      00            db      00

00401018      00            db      00

00401019      00            db      00

0040101A      00            db      00

0040101B      00            db      00

0040101C      00            db      00

0040101D      00            db      00

0040101E      00            db      00

 

 

 

 

 

 

0040101F  /$  55           push    ebp

00401020  |.  8BEC         mov    ebp, esp

; 使 EBP=ESP=0012FFB4

; 設定新的 EBP 指標,指向棧頂,

; 從這裡開始, EBP 就作為定址參數的基址指標。

00401022  |.  83EC 04       sub     esp, 4

; 擴充棧空間 ESP=0012FFB0

00401025  |.  8B450C       mov    eax, dword ptr [ebp+C] ;

                                                               ; 當前堆棧 3 個雙字位移的數值

                                                               ; ebp+C=0012FFB4+C=0012FFC0

                                                               ;EAX=[0012FFC0]=00000004

00401028  |.  8B5D 08       mov     ebx, dword ptr [ebp+8]

                                                               ; 當前堆棧 2 個雙字位移的數值

                                                               ; ebp+8==0012FFB4+8=0012FFBC

                                                               ;EBX=[0012FFBC]=00000003

0040102B  |.  895D FC      mov     dword ptr [ebp-4], ebx

                                                               ;[ebp-4] 就是局部變數 =[0012FFB4-4]=[0012FFB0]

                                                               ; 自動減 4, 也就是將 EBX 中的值 00000003

; 放入堆棧的 0012FFB0 地址處

; 參數 1 放局部變數裡

0040102E  |.  0345 FC       add     eax, dword ptr [ebp-4]

                                                               ; 將局部變數與 EAX 相加後放入 EAX 中

                                                               ;EAX=00000007

                                                               ; 參數 2 與局部變數相加

00401031  |. 83C4 04       add     esp, 4

                                                               ;  釋放局部變數佔用的堆棧 ,ESP: 0012FFB4

00401034  |.  5D            pop     ebp

                                                               ; 恢複原有的 EBP, ESP:0012FFB8

00401035  \.  C2 0800       retn    8

                                                               ;  返回 ( 相當於 ret; add esp,8)

                                                               ; ESP: 0012FFC4

 

 

 

堆棧的情況:

0012FFB0   00000003        ; 局部變數

0012FFB4   0012FFF0              ; 儲存 EBP , push    ebp

0012FFB8   00401009       ; 壓入返回地址

; 返回到 local.< 模組進入點 >+9 來自 local.0040101F

0012FFBC   00000003        ; push    3 ,每次堆棧地址加 32 位,雙字

0012FFC0   00000004        ; push    4

 

 

 

 

五 : 說明

Intel 的堆棧是在記憶體中是向下擴充的。先進棧的資料記憶體位址最高,後進棧的資料記憶體位址減少。且資料是按小尾類型儲存,例如:數值 12345678H 存放的形式(假設按字):先存 1234 ,後存放 5678

函數參數壓棧,棧幀ebp,esp怎樣移動的?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.