堆栈和堆栈帧有什么区别?

时间:2015-01-10 10:37:28

标签: assembly stack

这是我的代码:

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

'递减'寄存器sp 4个字节..但是当我输入

info stack

我仍然在hello.asm看到print():19

我的问题是,什么是信息堆栈显示我,什么是信息寄存器sp显示我?堆栈寄存器和信息堆栈之间有什么关系?

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

调用新函数时,会设置新的堆栈帧。每个堆栈帧代表一个功能。 在该堆栈帧中,当您将变量推入堆栈时,堆栈指针会随着堆栈的增长而发生变化。 print()hello.asm:19是gdb,使用指令指针显示源执行的位置。你在print()函数第19行,这是" push%ebx" 执行callq调用另一个函数后,bt将显示当前堆栈帧已更改。

C-x a C-x 2 CRL-2

gdb中的

会将您的终端拆分为3.底部的命令,中间窗格中的反汇编以及顶部窗格中的注册。通过这种方式,您可以逐步了解您所处的位置以及寄存器状态的变化以及何时更改 - 这非常有用。

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

答案 3 :(得分:1)

“堆栈”是应用程序使用的运行时数据结构,主要有两个用途:

  1. 记录有关被调用函数的信息,以便您可以调用子例程,并从它们返回到调用它们的位置。
  2. 将临时局部变量存储在特定于声明它们的函数的上下文中。
  3. 您的CPU有一个特殊的寄存器,其唯一目的是将堆栈顶部的地址保存在内存中。这是“堆栈指针”,或sp。每次“推送”将使sp递减4(在32位模式下),并将值存储在堆栈顶部,在sp指示的地址处。每个“pop”将执行相反的操作,检索堆栈顶部的值,并将{4}添加到sp

    每次调用另一个函数时,附加信息都会存储在堆栈中,包括返回地址(#1)和局部变量值(#2)。每个函数调用的信息称为“框架”。


    info stack是GDB命令。它将“走”堆栈,寻找这些“堆栈帧”的边界。从框架中,它将显示与其相关联的功能等信息。足够智能,不一定关心函数内的个别推送和弹出;其目的是向您展示调用函数的顺序的更高级别信息。


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

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

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