【问题标题】:Understanding gdb format了解 gdb 格式
【发布时间】:2020-02-08 13:02:51
【问题描述】:

我正在尝试熟悉gdb,并根据其格式和显示的内容提出了一些问题:

─── Assembly ────
 0x00000000004004ed  main+0  push   %rbp
 0x00000000004004ee  main+1  mov    %rsp,%rbp
!0x00000000004004f1  main+4  movl   $0x539,-0x4(%rbp)
  • 左列的内存地址是什么意思,为什么每条指令在下一个地址“之间”都是可变宽度的?
  • 第二列是什么意思?

.

─── Registers ───────────────────────
rax 0x00000000004004ed         
rbx 0x0000000000000000      
rcx 0x0000000000000000
  • 寄存器旁边的值是它的内存位置,还是注册表中包含的值?

.

─── Stack ───────────────────
[0] from 0x000000000040058c in main+47 at main.c:7
  • 这一行告诉我们什么:堆栈是否从内存地址0x000000000040058c 开始,main+47 指的是什么?

【问题讨论】:

    标签: c debugging assembly gdb stack


    【解决方案1】:

    左列的内存地址是什么意思,为什么是 每条指令在下一个地址之间都有一个可变宽度“之间”?

    x86 机器码指令是可变长度的。所以有些指令占用一个字节,而例如 movabs $0x12345678abcdef, %rax 占用 10 个字节。硬限制是 15 个字节,但只有用冗余前缀有意填充才能一直到 15。

    许多其他架构都是 RISC 并且具有固定宽度的指令。

    第二列是什么意思?

    它告诉你符号main 的相对地址。请注意,内存中的实际位置不是在编译时分配的。

    (编者注:这不是 PIE 可执行文件,因此绝对地址实际上在链接时设置的。我们可以知道,因为地址是地址空间的低 32 位中的0x00400...,不是0x55555555....)

    寄存器旁边的值是它的内存位置,还是值 包含在注册表中?

    寄存器不存储在内存中(罕见的架构除外);寄存器没有地址,是与内存分开的空间。它也没有显示恰好持有有效地址的寄存器指向的值。

    显示的值是电阻器本身的值。请注意rbxrcx 都显示0x0

    这一行告诉我们什么:堆栈是否从内存地址开始 0x000000000040058c,main+47指的是什么?

    (编者注:这部分是错误的,但我不确定用其他东西替换它到底是什么。但 0x40058c 绝对不是 RSP 的合理值。main+47 是 @ 内部某处的代码地址987654329@,和 GDB 一样symbol+number)。

    这是堆栈的位置。你的代码很小,所以main 只占用少于 48 个地址的空间。请注意,内存通常是按块分配的,因此堆栈不会出现在 main+7 或紧跟在 movl 指令之后的任何位置。

    【讨论】:

    • 谢谢 -- 你能否澄清你的意思:Note that memory is normally allocated in blocks, so the stack would not appear at main+7, or whatever immediately follows the movl instruction.
    • 最后,最后一件事。为什么栈的内存地址会随着指令的执行而变化(栈地址一直向下移动到当前执行的asm指令之后的位置)。
    • 内存管理器必须跟踪分配的内存和可用的内存。逐个字节地这样做是非常低效的。所以内存管理器分配更大的块(大小取决于系统),一旦你需要比块大小多一个字节,你就会得到一个全新的块。任何其他内存请求(堆栈、堆、另一个任务等)都从另一个块的开头开始。至于栈的移动:当你调用时,创建一个动态变量等等,东西都被压入栈中,栈指针也随之移动。
    • @TagC198:这个答案的最后一部分是错误的; main+47main 可能使用的堆栈空间无关。 Main 的机器码大小至少为 48 字节,因为 main+47main 的一部分。另请注意,如果 GCC 打算通过移动 RSP 在 main() 内分配堆栈空间,它可能会在将 0x539 存储到 RSP 以下的内存之前使用 sub $something, %rsp 这样做。 (x86-64 System V ABI 在 RSP 下方定义了一个 128 字节的红色区域,代码可以安全地使用它,而不会被信号处理程序或 w/e 异步破坏。)
    【解决方案2】:

    @daShier 的回答大部分是正确的,但关于这部分是完全错误的:

    这一行告诉我们什么:堆栈是否从内存地址开始 0x000000000040058c,main+47指的是什么?

    我认为这是堆栈上的一个 qword 值(由 RSP 指向)。它可能是 main 的返回地址,或者可能只是 main 推送它时 RBP 中的一个值。

    (但是返回地址是合理的:main 开始于0x4004ed0x40058c 不远)。

    main + 47 = 0x40051c是main里面的代码地址,对应main.c第7行的C源码。 (main.c:7)。 symbol+number 是 GDB 以人类可读的方式打印地址的方式,相对于它们上方最接近的符号。即他们在什么功能。 ; 我认为这是您在复制/粘贴时停止的断点。它告诉你现在执行在哪里。或者是在何时拍摄堆栈上的数据快照。

    我不确定您是如何让 GDB 打印该堆栈转储的。它与info stackbacktrace 的格式略有不同。 TUI 模式layout reg 或任何其他布局不包括堆栈窗格。


    但无论如何,0x000000000040058c 肯定不是堆栈地址;它与main 在同一个4kiB 虚拟内存页面中,所以它在.text 部分中。 (事实上​​,它只比main + 47 多了 0x70 个字节)。该虚拟页面将是可执行且不可写的。

    RSP(堆栈指针)值类似于0x7ffff7fd4100,靠近虚拟地址空间的低 48 位顶部。 (x86-64 上虚拟地址空间的可用(规范)部分的用户空间部分的顶部)。


    正如我所说,main+47 只是main 中的一个代码地址。它与 47 或 48 字节的堆栈空间无关。

    【讨论】:

    • "我认为这是堆栈上的一个 qword 值" -- 它是当前帧中的 current $pc(程序在行 main.c:7 停止) .如果程序在 main 调用的函数中停止,将会有更多级别。
    • @EmployedRussian:但不是现在的$pcmain+47吗?根据我的数学是0x40051c,而不是0x40058c。您是指当前函数的堆栈帧中的当前返回地址吗?
    • 反汇编(&main == 4004ed)和堆栈(&main + 47 == 40058c 暗示&main == 0x40055d)是在不同时间记录的,并且可能用于不同的程序。您可以判断,因为反汇编显示!0x00000000004004f1 为当前指令(由! 表示),并且无法位于main+4 并且在main 中也有返回地址。
    • @EmployedRussian 哦,很好看。那么,这是一个非常令人困惑/误导的问题!所以它可能只是一个回溯。
    猜你喜欢
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    相关资源
    最近更新 更多