使用带参数的函数在堆栈中ebp和esp的行为

use*_*712 2 parameters x86 assembly stack calling-convention

我想了解有关堆栈的更多信息。特别是调用带有参数的函数时会发生什么。为此,我编写以下代码:

    #include <stdio.h>
    int sum(int d, int e, int f){
        int result = d + e + f;
        return result;
    }

   int main(void){
   int a = 5;
   int b = 4;
   int c = 2;

   int erg = sum(a,b,c);
   printf("Result is: %d", erg);

   }
Run Code Online (Sandbox Code Playgroud)

我得到以下汇编代码(我将仅添加main函数的一部分,因为首先我想了解本节):

    push ebp,
    mov ebp, esp
    and esp, -16
    sub esp, 32
    mov DWORD PTR[esp+28], 5
    mov DWORD PTR[esp+24], 4
    mov DWORD PTR[esp+20], 2
    mov eax, DWORD PTR[esp+20]
    mov DWORD PTR[esp+8], eax
    mov eax, DWORD PTR[esp+24]
    mov DWORTD PTR[esp+4], eax
    mov eax, DWORD PTR[esp+28]
    mov DWORD PTR[esp], eax
    call sum
    ........
    ........
Run Code Online (Sandbox Code Playgroud)

因此,对于这一部分,我为自己画了一些草图。请看一看:)我的问题:那时候我在哪里ebp?由于我的汇编代码的第2行,它必须位于一样的地方[esp],对吗?

现在,我的第二个问题后面是求和函数的一部分。

所以这是该代码的汇编代码:

    push ebp
    mov ebp, esp
    sub esp, 16
    mov eax, DWORD PTR[ebp+12]
    mov edx, DWORD PTR[ebp+8]
    mov edx, eax
    ------------
Run Code Online (Sandbox Code Playgroud)

因此,我了解到我们始终可以在[eb+12]和中找到参数[ebp+8]。(我跳过了第三个参数,因为我想使它保持简单)。现在,我的问题是:如果我假设esp= ebp并查看草图,那么我[esp+12]现在或现在都没有任何东西[ebp+12]。但是尽管如此,它还是被使用了。我怎么能想象得到?

有人能帮我吗?我读了很多论文,但是似乎没人能勾勒出这些东西。因此,很难理解这一点。

谢谢!

这是我的草图:

在此处输入图片说明

nrz*_*nrz 5

第一功能执行过程中esp,并ebp指令后立即仅具有相同的值mov ebp, esp。此后and esp, -16零的最低4个比特(最低四位)的esp,并espebp发散,除非的最低比特esp是零已经。然后sub esp, 32减去32 esp并在这里espebp发散肯定。

push ebp                   ; esp = esp - 4; [esp] = ebp.
mov ebp, esp               ; ebp = esp. create the stack frame. 
and esp, -16               ; make lowest 4 bits of esp zeros, for alignment.
sub esp, 32                ; esp = esp - 32. make space for local variables.
mov DWORD PTR[esp+28], 5   ; a = 5
mov DWORD PTR[esp+24], 4   ; b = 4
mov DWORD PTR[esp+20], 2   ; c = 2
mov eax, DWORD PTR[esp+20] ; eax = c (eax = 2)
mov DWORD PTR[esp+8], eax  ; [esp+8] = dword 2
mov eax, DWORD PTR[esp+24] ; eax = b (eax = 4)
mov DWORTD PTR[esp+4], eax ; [esp+4] = dword 4
mov eax, DWORD PTR[esp+28] ; eax = a (eax = 5)
mov DWORD PTR[esp], eax    ; [esp] = dword 5
call sum                   ; the above lines define the parameters for the
                           ; function sum, that is called now.
Run Code Online (Sandbox Code Playgroud)

然后关于第二个问题:

push ebp                   ; esp = esp - 4; [esp] = ebp.
mov ebp, esp               ; ebp = esp.
sub esp, 16                ; esp = esp - 16. create space for local variables.
                           ; at this point:
                           ; [ebp]    == old value of ebp.
                           ; [ebp+4]  == return address pushed by call,
                           ;             to be used by the next ret.
                           ; [ebp+8]  == dword 5 (a)
                           ; [ebp+12] == dword 4 (b)
                           ; [ebp+16] == dword 2 (c)
mov eax, DWORD PTR[ebp+12] ; eax = 4
mov edx, DWORD PTR[ebp+8]  ; edx = 5. gets overwritten by the next instruction.          
mov edx, eax               ; edx = eax = 4
Run Code Online (Sandbox Code Playgroud)

不要假设esp== ebp。在这第二个功能也espebp发散在指令的执行sub esp,16。学习使用调试器(例如GDB)并单步执行代码,esp并在每条指令后跟踪寄存器(尤其是)和内存的值。您也可以像上面一样在脑海中调试代码,但是,如果您是汇编的初学者,则使用调试器通常会更容易,并且不容易出错。