assembly - GDB - 它如何知道函数调用堆栈?

使用gdb调试汇编程序时,bt会打印调用栈。

问题是:

  • (a) gdb 是否根据当前函数存储在寄存器中的 rbp 值以及先前 rbp 值存储在堆栈中知道这一点?
  • 如果是,(b-1) gdb 如何根据 rbp 值知道它是哪个函数? (b-2) 编译时指定-g选项时,栈基和函数之间的映射是否存储在可执行文件中? (b-3) 以及如何使用 readelf 读取映射数据?哪一部分?
  • 如果不是,(c) 那么 gdb 如何跟踪函数调用堆栈?

最佳答案

像 GDB 这样的调试器有两种主要的遍历堆栈的方法来打印回溯。他们要么假定帧指针寄存器 (RBP) 中的值是指向堆栈帧链表开始的指针,要么使用存储在可执行文件中的特殊展开信息来描述如何遍历(展开)堆栈。

使用帧指针

当使用帧指针时,假定它指向当前函数保存其调用者帧指针值的位置。它还假定就在保存的帧指针之前是存储当前函数的返回地址的地方。这就是它如何知道调用函数的 RBP 值是什么,以及调用当前函数的函数,它可以很容易地从返回地址确定。然后,它可以通过遍历链接的 RBP 值找到堆栈中所有先前的堆栈帧和函数。

但是,这假定函数以这种方式使用帧指针,并且通常不能保证它们会这样做。基本上它假设函数序言和结尾看起来像这样:

func:
    push %rbp         # save previous frame pointer
    mov  %rsp, %rbp   # new frame pointer points to previous value
    sub  $24, %rsp    # allocate stack space for this funciton

    ...

    pop %rbp          # restore previous frame pointer
    ret

但是在优化时,许多编译器不会这样做,因为它们很少需要使用帧指针,而是会像对待任何其他通用寄存器一样对待 RBP,并将其用于其他用途。

使用展开信息

因此,为了在不使用 RBP 作为帧指针的函数之间生成回溯,调试器可能会使用展开信息。这是存储在可执行文件(和动态库)中的特殊数据,它准确地描述了如何在函数执行过程中的任何时间点虚拟撤消函数执行的所有堆栈操作。展开信息的格式和位置因可执行格式和 CPU 类型而异。对于 ELF x86-64 可执行文件,展开信息以基于 DWARF 的格式存储在 .eh_frame 部分中调试格式的展开信息。这种格式太复杂,无法在此处描述,但您可以阅读 System V AMD64 ABI了解更多详情。

https://stackoverflow.com/questions/38158006/

相关文章:

r - 如何使用来自多个其他列的所有非 NA 值创建新列?

regex - 如何在 Chrome Dev Tools 中过滤没有扩展的 XHR 请求?

apache-camel - 如何在 apache camel 中全局设置交换属性

angularjs - 具有自动完成功能的 Angular 下拉菜单

php - Eloquent 模型添加没有 "created_at"和 "updated_at"字段

git - 从 merge 请求创建补丁

Neo4j - 根据关系属性查找两个节点之间的最短路径

javascript - 元素 innerHTML 摆脱事件监听器

types - Julia 中的大宽度整数?

ruby-on-rails - Rails 加入或包含有条件的 belongs_to