使用gdb调试汇编程序时,bt
会打印调用栈。
问题是:
rbp
值以及先前 rbp 值存储在堆栈中知道这一点?rbp
值知道它是哪个函数? (b-2) 编译时指定-g
选项时,栈基和函数之间的映射是否存储在可执行文件中? (b-3) 以及如何使用 readelf
读取映射数据?哪一部分?最佳答案
像 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/