栈和栈帧有什么区别?

What is the difference between stack and stack frame?

这是我的代码:

17 mov      ebx,msg
18 mov      edx,5   
19 push     ebx

我正在使用 gdb 进行调试,这是我的输出:

Breakpoint 1, print () at hello.asm:17
(gdb) info register sp 
sp: 0xbffff37c
(gdb) info stack
#0  print () at hello.asm:17

(gdb) step
(gdb) info register sp
sp: 0xbffff37c
(gdb) info stack
#0  print () at hello.asm:18

(gdb) step
(gdb) info register sp
sp: 0xbffff378
(gdb) info stack
#0  print () at hello.asm:19

很明显

push ebx

'decrements' 注册 sp 4 个字节.. 但是当我输入

info stack

我仍然在 hello.asm:19

看到 print()

我的问题是,info stack 向我展示的是什么,info register sp 向我展示的是什么?堆栈寄存器和信息堆栈之间的关系是什么?

info stackbacktrace 的别名 - 它向您显示您所处的功能,只要它可以确定它。

Stack 和 esp 寄存器与查找堆栈跟踪的最深位置无关 - 您当前正在执行的函数。要找到它,您需要检查 eip - 指向下一条要执行的指令的指针。只有在那之后,您才能分析堆栈以找到您所在的其他函数的 return 地址。

esp 寄存器保存指向堆栈的指针。堆栈是特殊的内存区域,由用 c 和 c++ 编写的应用程序使用,用于保存 return 地址和函数的局部变量。 当调试器想要确定导致当前进程指令的函数调用链时 - 它通过查看位于堆栈中的 return 地址链来实现。这可能导致您感到困惑。所以,当前指令!=堆栈。 基本上每次执行 "call" 指令时,下一条指令的地址都会被放入堆栈并且堆栈指针会减少,因此当调用 "return" 指令时 - 处理器知道 return 的位置。

调用新函数时,会建立一个新的栈帧。每个栈帧代表一个函数。 在该堆栈框架内,当您将变量压入堆栈时,堆栈指针会随着堆栈因压入而增长而变化。 print() hello.asm:19 是 gdb 使用指令指针向您显示源代码执行的位置。您在第 19 行的 print() 函数中,即 "push %ebx" 执行 callq 调用另一个函数后,bt 将显示当前堆栈帧已更改。

C-x一个 C-x 2 crl-2

在 gdb 中会将您的终端分成 3 个。命令在底部的痛苦中,反汇编在中间窗格中,注册在顶部窗格中。通过这种方式,您可以逐步了解自己的进度以及寄存器状态的变化以及变化的时间 - 这非常有用。

<咆哮> 直到它因为 gdb 的 ncurses 接口(aka tui)被破坏而崩溃,gdb 看门人才不管,不接受修复这些崩溃的补丁。如果它对你来说太多了,你需要使用 gdb 前端,例如 eclipse 或 insight,它们可以向你显示相同的信息并且可能不会由于愚蠢的 ncurses 代码而崩溃

"The stack" 是一个 运行 时间的数据结构,您的应用程序使用它有两个主要目的:

  1. 记录有关被调用函数的信息,以便您可以调用子例程,并且return从它们到它们被调用的地方。
  2. 将临时局部变量存储在特定于声明它们的函数的上下文中。

您的 CPU 有一个特殊的寄存器,其唯一目的是维护内存中堆栈顶部的地址。这就是"stack pointer",或者sp。每个 "push" 都会将 sp 递减 4(在 32 位模式下),并将一个值存储在堆栈顶部,在 sp 指示的地址处。每个 "pop" 将做相反的事情,检索堆栈顶部的值,并将 4 添加到 sp.

每次调用另一个函数时,都会将附加信息存储在堆栈中,包括 return 地址 (#1) 和局部变量的值 (#2)。每个函数调用的信息价值被称为 "frame".


info stack 是一个 GDB 命令。它将"walk" 堆栈寻找这些"stack frames" 的边界。从框架中,它将显示信息,例如与其关联的功能。它足够聪明,不必关心函数内的个别推送和弹出;它的目的是向您展示函数调用顺序的高级信息。


GDB 中的step 命令在源代码行级别工作。通常,这是一行 C 代码。但是,由于您使用的是汇编源代码,因此每一行对应一条指令。

此外,由于您在汇编源代码中工作,函数和堆栈框架的概念可能不适用!使用 -g 编译会在二进制文件中嵌入额外的信息以帮助 GDB 将汇编指令与 C 函数匹配,以及有关局部变量的信息等

我建议你先写一个简单的C程序,调用函数并做一些有趣的事情。用 -g 编译它,然后在 GDB 中逐步执行它。一旦您熟悉了这一点,调试您的汇编代码可能会更容易。