flexw
flexw

Reputation: 73

RSP points not to the top of the stack?

I have a problem understanding how the stack works. First my little code:

void func1 ( int z ) {
  int i = 1;
}

int main ( ) {
  func1 ( 89 );
  return 0;
}

I am using: Ubuntu 16.04 64-bit, gcc version 5.4.0, gdb version 7.11.1.

I was debugging with GDB, to see how the compiler pushes function arguments on the stack.

When I examine the stack at the point of the where RSP points, I get this:

(gdb) x/10xw $rsp
0x7fffffffdf20: 0xffffdf30  0x00007fff  0x00400525  0x00000000
0x7fffffffdf30: 0x00400530  0x00000000  0xf7a2e830  0x00007fff
0x7fffffffdf40: 0x00000000  0x00000000

When I print out the address of newest created variable, I get this:

(gdb) p &i
$4 = (int *) 0x7fffffffdf14 

When I print out the address of the variable, which was hand over to the function, I get this:

(gdb) p &z
$5 = (int *) 0x7fffffffdf0c

The stack is growing to lower numbers.

So I thought that RSP always points to the top of the stack, meaning that when I i call this command x/10xw $rsp I am able to see all the variables from the function, but I can't see them from there.

The first address after this command is way higher than the address of the variable z. Because of that I was guessing that RSP points not on the top of the stack.

What is also wondering me, is that the address of i is higher than the address of z. Since i were later pushed to the stack than z, i must be a lower address than z in my opinion.

I hope someone can explain me why this is so.

EDIT: I have found the answer! It was an optimization from the compiler. In func1() the RSP register had not pointed to the "top" of the stack because it was not necessary. It were just necessary if in func1() a other function were called. So the compiler saw that and didn't decrement the RSP register.

Here ismy assembler code with no function call in func1():

   0x00000000004004d6 <+0>:     push   rbp
   0x00000000004004d7 <+1>:     mov    rbp,rsp
   0x00000000004004de <+8>:     mov    DWORD PTR [rbp-0x14],edi
   0x00000000004004e1 <+11>:    mov    DWORD PTR [rbp-0x4],0x1
   0x00000000004004e8 <+18>:    mov    eax,0x0
   0x00000000004004f3 <+29>:    leave  
   0x00000000004004f4 <+30>:    ret 

So you can see no SUB call for decrementing RSP.

Now the code from func1() with a function call:

   0x00000000004004d6 <+0>:     push   rbp
   0x00000000004004d7 <+1>:     mov    rbp,rsp
   0x00000000004004da <+4>:     sub    rsp,0x20  
   0x00000000004004de <+8>:     mov    DWORD PTR [rbp-0x14],edi
   0x00000000004004e1 <+11>:    mov    DWORD PTR [rbp-0x4],0x1
   0x00000000004004e8 <+18>:    mov    eax,0x0
   0x00000000004004ed <+23>:    call   0x4004f5 <func2>
   0x00000000004004f2 <+28>:    nop
   0x00000000004004f3 <+29>:    leave  
   0x00000000004004f4 <+30>:    ret    

So you can see the SUB call for decrementing RSP. So RSP can point to the "top".

Upvotes: 2

Views: 2411

Answers (1)

John Bode
John Bode

Reputation: 123478

The convention on x86 is that the stack grows "downwards" towards decreasing addresses.

The "top" of the stack is simply the location where something was most recently pushed; it's not based on the relative values of the addresses. A stack can grow "upwards" or "downwards" in the address space - heck, for some implementations (such as a linked list), the addresses don't even have to be sequential.

This page has a fair explanation with diagrams.

Upvotes: 1

Related Questions