3

I'm on the way to get idea how the stack works on x86 and x64 machines. What I observed however is that when I manually write a code and disassembly it, it differs from what I see in the code people provide (eg. in their questions and tutorials). Here is little example:

Source

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

int main () {
    add(3,4);
   return 0;
}

x86

add(int, int):
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 16
        mov     edx, DWORD PTR [ebp+8]
        mov     eax, DWORD PTR [ebp+12]
        add     edx, eax
        mov     eax, DWORD PTR [ebp-4]
        add     eax, edx
        leave (!)
        ret

main:
        push    ebp
        mov     ebp, esp
        push    4
        push    3
        call    add(int, int)
        add     esp, 8
        mov     eax, 0
        leave (!)
        ret

Now goes x64

add(int, int):
        push    rbp
        mov     rbp, rsp
        (?) where is `sub rsp, X`?
        mov     DWORD PTR [rbp-20], edi
        mov     DWORD PTR [rbp-24], esi
        mov     DWORD PTR [rbp-4], 16
        mov     edx, DWORD PTR [rbp-20]
        mov     eax, DWORD PTR [rbp-24]
        add     edx, eax
        mov     eax, DWORD PTR [rbp-4]
        add     eax, edx
        (?) where is `mov rsp, rbp` before popping rbp?
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 4
        mov     edi, 3
        call    add(int, int)
        mov     eax, 0
        (?) where is `mov rsp, rbp` before popping rbp?
        pop     rbp
        ret

As you can see, my main confusion is that when I compile against x86 - I see what I expect. When it's x64 - I miss leave instruction or exact following sequence: mov rsp, rbp then pop rbp. What's worng?

UPDATE

It seems like leave is missing, just because it wasn't altered previously. But then, goes another question - why there is no allocation for local vars in the frame?


To this question @melpomene gives pretty straightforward answer - because of "red zone". Which basically means the function that calls no further functions (leaf) can use the first 128 bytes below the stack without allocating space. So if I insert a call inside an add() to any other dumb function - sub rsp, X and add rsp, X will be added to prologue and epilogue respectively.

Timur Fayzrakhmanov
  • 17,967
  • 20
  • 64
  • 95

0 Answers0