Wednesday, January 5, 2022

[SOLVED] Stack frame creation in 64 bit machine

Issue

I'm just learning some low level analysis of the programs. In 32 bit compilation with gcc, I found that the stack frame is created in the following order:

  1. Push the function arguments in reverse order.
  2. Save the return address
  3. Save the frame pointer
  4. Create the local variables

So the address of the arguments should be highest, as stack grows in reverse order. But when I tried the same with a 64 bit compilation, I cant understand how it's created, it's just the opposite of what I found in 32 bit compilation. Here is the code and memory details:

void test(int a, int b, int c, int d)
{
    int flag;
    char buf[10];
    num = 100;
}

int main()
{
        test(1, 2, 3, 4);
}

Right now for simplicity let's take only the arguments and return address.

32-bit compilation:

0xffffd130: 0x00000001  0xffffd1f4  0xffffd1fc  0xf7e3ad1d
0xffffd140: 0xffffd158  0x0804842d  0x00000001  0x00000002
0xffffd150: 0x00000003  0x00000004  0x00000000  0xf7e22933
0xffffd160: 0x00000001  0xffffd1f4  0xffffd1fc  0xf7fdb6b0


   0x08048421 <+30>:    mov    DWORD PTR [esp],0x1
   0x08048428 <+37>:    call   0x80483f0 <test>
   0x0804842d <+42>:    leave  

Here everything is proper. I can see the arguments at higher address, immediately followed by the return address 0x0804842d which is at lower address. Now,

64-bit compilation:

0x7fffffffdf80: 0x00000004  0x00000003  0x00000002  0x00000001
0x7fffffffdf90: 0x00400530  0x00000000  0x00400400  0x00000000
0x7fffffffdfa0: 0xffffdfb0  0x00007fff  0x0040052a  0x00000000
0x7fffffffdfb0: 0x00000000  0x00000000  0xf7a3baf5  0x00007fff

   0x0000000000400525 <+24>:    call   0x4004f0 <test>
   0x000000000040052a <+29>:    pop    rbp
   0x000000000040052b <+30>:    ret    

Here I can see that the arguments are at a lower address and the return address 0x0040052a is at a higher address. What is the problem here? is the stack growing in an opposite direction (lower to higher address) or the creation of stack frame is different from the above mentioned sequence? Please help me to understand. Thanks.


Solution

On x86-64 the standard way of passing arguments is through the use of registers, not the stack (unless you got more than 6). See http://www.x86-64.org/documentation/abi.pdf

I highly recommend not doing any kind of experimentation without reading proper documents first (like the one I just linked).

Anyway, you could easily see the arguments were not passed on the stack if you disassembled main:

   0x0000000000400509 <+0>: push   %rbp
   0x000000000040050a <+1>: mov    %rsp,%rbp
   0x000000000040050d <+4>: mov    $0x4,%ecx
   0x0000000000400512 <+9>: mov    $0x3,%edx
   0x0000000000400517 <+14>:    mov    $0x2,%esi
   0x000000000040051c <+19>:    mov    $0x1,%edi
   0x0000000000400521 <+24>:    callq  0x4004f0 <test>
   0x0000000000400526 <+29>:    pop    %rbp
   0x0000000000400527 <+30>:    retq   

And you could also see how they end up on the stack within test:

   0x00000000004004f0 <+0>: push   %rbp
   0x00000000004004f1 <+1>: mov    %rsp,%rbp
   0x00000000004004f4 <+4>: mov    %edi,-0x14(%rbp)
   0x00000000004004f7 <+7>: mov    %esi,-0x18(%rbp)
   0x00000000004004fa <+10>:    mov    %edx,-0x1c(%rbp)
   0x00000000004004fd <+13>:    mov    %ecx,-0x20(%rbp)
   0x0000000000400500 <+16>:    movl   $0x64,-0x4(%rbp)
   0x0000000000400507 <+23>:    pop    %rbp
   0x0000000000400508 <+24>:    retq   


Answered By - employee of the month