当编译器被明确告知不要使用ebp作为堆栈帧指针时,如何实现堆栈回溯?
这个问题的答案只出现在关于EBP帧指针寄存器的用途是什么的已接受答案的评论中?。
即使在使用-fomit帧指针编译的代码中,现代调试器也可以进行堆栈回溯。该设置是最近的gcc中的默认设置。
gcc将必要的堆栈展开信息放入.eh_frame_hdr
部分。有关详细信息,请参阅此博客文章。它也用于运行时异常。您可以在Linux系统上的大多数二进制文件中找到它(使用objdump -h
)。/bin/bash
大约为16k,而GNU /bin/true
大约为572B,ffmpeg
大约为108k。
有一个gcc选项可以禁用生成它,但它是一个"正常"数据段,而不是strip
默认删除的调试段。否则,您无法通过没有调试符号的库函数进行回溯。该部分可能比它所替换的push/mov/pop
指令大,但它的运行时成本几乎为零(例如uop缓存)。
我认为存储在该部分中的信息是从返回地址到堆栈帧大小的映射。由于每条call
指令都将下一条指令的地址推送到堆栈上,因此可以从该地址识别父调用方。与推动ebp
在堆栈上创建堆栈帧的链表不同,到下一个返回地址的偏移量存储在.eh_frame_hdr
部分中,因此如果需要回溯的代码需要,可以使用它。