RSP指向不是堆栈的顶部?

时间:2016-10-31 19:21:00

标签: c linux gcc stack gdb

我在理解堆栈的工作原理时遇到了问题。首先是我的小代码:

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

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

我正在使用: Ubuntu 16.04 64位, gcc版本5.4.0, gdb版本7.11.1。

我正在使用GDB进行调试,以了解编译器如何在堆栈上推送函数参数。

当我在RSP指向的位置检查堆栈时,我得到了这个:

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

当我打印出最新创建的变量的地址时,我得到了这个:

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

当我打印出该函数的地址时,我得到了这个:

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

筹码越来越少。

所以我认为RSP始终指向堆栈的顶部,这意味着当我调用此命令x/10xw $rsp时,我能够看到函数中的所有变量,但我可以'从那里看他们。

此命令后的第一个地址高于变量z的地址。因此,我猜测RSP点不在堆栈顶部。

我还想知道,i的地址高于z的地址。 由于i后来被推到了堆栈而不是z,因此我认为i的地址必须低于z。

我希望有人可以解释为什么会这样。

编辑:我找到了答案! 这是编译器的优化。在func1()中,RSP寄存器没有指向堆栈的“顶部”,因为它没有必要。如果在func1()中调用了其他函数,则这是必要的。所以编译器看到了并且没有递减RSP寄存器。

这是我在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 

所以你看不到SUB要求递减RSP

现在来自func1()的代码带有函数调用:

   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    

因此,您可以看到SUB要求递减RSP。所以RSP可以指向“顶部”。

1 个答案:

答案 0 :(得分:1)

x86上的约定是堆栈向递减地址“向下”增长。

堆栈的“顶部”只是最近推送的位置;它不是基于地址的相对值。堆栈可以在地址空间中“向上”或“向下”增长 - 对于某些实现(例如链表),地址甚至不必是顺序的。

This page对图表有一个公平的解释。