簡體   English   中英

執行新程序時,ESP和EBP寄存器如何工作?

[英]How does ESP and EBP registers act when a new program is executed?

我最近瀏覽了一 理查德·布魯姆Richard Blum)編寫匯編語言書 ,其中涉及C程序到匯編轉換的主題。

考慮下面的C程序:

#include <stdio.h>

int main(){
     int a=100;
     int b=25;
     if (a>b)
       printf("The higher value is %d\n", a);
     else
       printf("The higher value is %d\n", b);
     return 0;
}

當我使用-S參數編譯上述程序時:

gcc -S abc.c

我得到以下結果:

        .file   "abc.c"
        .section        .rodata
.LC0:
        .string "The higher value is %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        leal    4(%esp), %ecx
        .cfi_def_cfa 1, 0
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        .cfi_escape 0x10,0x5,0x2,0x75,0
        movl    %esp, %ebp
        pushl   %ecx
        .cfi_escape 0xf,0x3,0x75,0x7c,0x6
        subl    $20, %esp
        movl    $100, -16(%ebp)
        movl    $25, -12(%ebp)
        movl    -16(%ebp), %eax
        cmpl    -12(%ebp), %eax
        jle     .L2
        subl    $8, %esp
        pushl   -16(%ebp)
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        jmp     .L3
.L2:
        subl    $8, %esp
        pushl   -12(%ebp)
        pushl   $.LC0
        call    printf
        addl    $16, %esp
.L3:
        movl    $0, %eax
        movl    -4(%ebp), %ecx
        .cfi_def_cfa 1, 0
        leave
        .cfi_restore 5
        leal    -4(%ecx), %esp
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005"
        .section        .note.GNU-stack,"",@progbits

我無法理解的是:

片段

.LFB0:
        .cfi_startproc
        leal    4(%esp), %ecx
        .cfi_def_cfa 1, 0
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        .cfi_escape 0x10,0x5,0x2,0x75,0
        movl    %esp, %ebp
        pushl   %ecx
        .cfi_escape 0xf,0x3,0x75,0x7c,0x6
        subl    $20, %esp

我無法預測ESPEBP寄存器會發生什么情況。 關於EBP ,我可以在某種程度上了解它被用作本地堆棧,因此可以通過推入堆棧來保存它的價值。

您能詳細說明上面的代碼段嗎?

這是函數輸入序列的一種特殊形式,適用於main()函數。 編譯器知道main()實際上稱為main(int argc, char **argv, char **envp) ,並根據非常特殊的行為來編譯此函數。 因此,到達此代碼時,堆棧上的內容是四個長尺寸值,其順序為: envpargvargcreturn_address

因此,這意味着進入順序代碼正在執行以下操作(重寫為使用Intel語法,坦率地說,它比AT&T語法有意義):

; Copy esp+4 into ecx.  The value at [esp] has the return address,
; so esp+4 is 'argc', or the start of the function's arguments.
lea ecx, [esp+4]

; Round esp down (align esp down) to the nearest 16-byte boundary.
; This ensures that regardless of what esp was before, esp is now
; starting at an address that can store any register this processor
; has, from the one-byte registers all the way up to the 16-byte xmm
; registers
and esp, 0xFFFFFFF0

; Since we copied esp+4 into ecx above, that means that [ecx] is 'argc',
; [ecx+4] is 'argv', and [ecx+8] is 'envp'.  For whatever reason, the
; compiler decided to push a duplicate copy of 'argv' onto the function's
; new local frame.
push dword ptr [ecx+4]

; Preserve 'ebp'.  The C ABI requires us not to damage 'ebp' across
; function calls, so we save its old value on the stack before we
; change it.
push ebp

; Set 'ebp' to the current stack pointer to set up the function's
; stack frame for real.  The "stack frame" is the place on the stack
; where this function will store all its local variables.
mov ebp, esp

; Preserve 'ecx'.  Ecx tells us what 'esp' was before we munged 'esp'
; in the 'and'-instruction above, so we'll need it later to restore
; 'esp' before we return.
push ecx

; Finally, allocate space on the stack frame for the local variables,
; 20 bytes worth.  'ebp' points to 'esp' plus 24 by this point, and
; the compiler will use 'ebp-16' and 'ebp-12' to store the values of
; 'a' and 'b', respectively.  (So under 'ebp', going down the stack,
; the values will look like this:  [ecx, unused, unused, a, b, unused].
; Those unused slots are probably used by the .cfi pseudo-ops for
; something related to exception handling.)
sub esp, 20

在函數的另一端,使用逆運算將堆棧放回調用函數之前的狀態。 檢查他們在做什么以及了解開始時正在發生的事情可能會有所幫助:

; Return values are always passed in 'eax' in the x86 C ABI, so set
; 'eax' to the return value of 0.
mov eax, 0

; We pushed 'ecx' onto the stack a while back to save it.  This
; instruction pulls 'ecx' back off the stack, but does so without
; popping (which would alter 'esp', which doesn't currently point
; to the right location).
mov ecx, [ebp+4]

; Magic instruction! The 'leave' instruction is designed to shorten
; instruction sequences by "undoing" the stack in a single op.
; So here, 'leave' means specifically to do the following two
; operations, in order:  esp = ebp / pop ebp
leave

; 'esp' is now set to what it was before we pushed 'ecx', and 'ebp'
; is back to the value that was used when this function was called.
; But that's still not quite right, so we set 'esp' specifically to
; 'ecx+4', which is the exact opposite of the very first instruction
; in the function.
lea esp, [ecx+4]

; Finally, the stack is back to the way it was when we were called,
; so we can just return.
ret

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM