【问题标题】:RSP points not to the top of the stack?RSP 不指向栈顶?
【发布时间】:2023-03-11 12:00:04
【问题描述】:

我无法理解堆栈的工作原理。首先是我的小代码:

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 的地址。 由于iz 更晚被推入堆栈,所以在我看来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 可以指向“顶部”。

【问题讨论】:

  • Linux 的 64 位调用约定使用寄存器作为前六个参数。不涉及堆栈。
  • 值将存储在哪些寄存器中?你知道一个链接或我能读到的东西吗?
  • 谢谢。但是为什么当我打电话给 p &i 或 p &z 时,我会在 RSP 附近得到一个地址?看起来像他们在堆栈上一样
  • i 自然是在堆栈上。我编译并反汇编了您的代码 sn-p,gcc 确实将 rdi 寄存器复制到堆栈变量(使用 -O0)。我想知道为什么。也许一些默认处理来保留寄存器值?

标签: c linux gcc stack gdb


【解决方案1】:

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

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

This page 用图表解释得很清楚。

【讨论】:

  • 好的。这意味着这两个都是有效的调用:mov DWORD PTR [rbp-0x14],edi 和 mov DWORD PTR [rbp+0x14],edi。 ?但是如果创建了一个新的堆栈帧会发生什么?我认为 RSP 的值被用作未使用地址可用的方向。我想我还得读很多书……
  • @FelixWe:看看我在回答中链接到的页面 - 它应该有助于回答您的问题。
  • 我可以自己回答这个问题。这篇文章为我指明了正确的方向。我已经为我的问题添加了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 1970-01-01
  • 2012-12-13
  • 2020-03-09
  • 1970-01-01
  • 2016-11-09
  • 1970-01-01
相关资源
最近更新 更多