不使用 ebp 实现堆栈回溯
Implementing stack backtrace without using ebp
当明确告知编译器不要使用 ebp 作为堆栈帧指针时,如何实现堆栈回溯?
这个问题的答案只出现在 What is the purpose of the EBP frame pointer register? 上接受的答案的评论中。
现代调试器甚至可以在使用 -fomit-frame-pointer 编译的代码中执行堆栈回溯。该设置是最近 gcc 中的默认设置。
gcc 将必要的堆栈展开信息放入 .eh_frame_hdr
部分。 See this blog post for more details。它也用于运行时异常。您会在 Linux 系统上的大多数二进制文件中找到它(使用 objdump -h
)。 /bin/bash
约为 16k,而 GNU /bin/true
约为 572B,ffmpeg
.
约为 108k
有一个 gcc 选项可以禁用它的生成,但它是一个 "normal" 数据部分,而不是 strip
默认删除的调试部分。否则,您无法通过没有调试符号的库函数进行回溯。该部分可能比它替换的 push/mov/pop
指令更大,但它的运行时成本几乎为零(例如 uop 缓存)。
我认为该部分中存储的信息是从 return 地址到堆栈帧大小的映射。由于每个 call
指令都会将后续指令的地址压入堆栈,因此您可以从该地址识别父调用者。不是将 ebp
压入栈帧链表 在 栈上,而是将下一个 return 地址的偏移量存储在 .eh_frame_hdr
中部分,因此可以在需要回溯的代码需要时使用它。
当明确告知编译器不要使用 ebp 作为堆栈帧指针时,如何实现堆栈回溯?
这个问题的答案只出现在 What is the purpose of the EBP frame pointer register? 上接受的答案的评论中。
现代调试器甚至可以在使用 -fomit-frame-pointer 编译的代码中执行堆栈回溯。该设置是最近 gcc 中的默认设置。
gcc 将必要的堆栈展开信息放入 .eh_frame_hdr
部分。 See this blog post for more details。它也用于运行时异常。您会在 Linux 系统上的大多数二进制文件中找到它(使用 objdump -h
)。 /bin/bash
约为 16k,而 GNU /bin/true
约为 572B,ffmpeg
.
有一个 gcc 选项可以禁用它的生成,但它是一个 "normal" 数据部分,而不是 strip
默认删除的调试部分。否则,您无法通过没有调试符号的库函数进行回溯。该部分可能比它替换的 push/mov/pop
指令更大,但它的运行时成本几乎为零(例如 uop 缓存)。
我认为该部分中存储的信息是从 return 地址到堆栈帧大小的映射。由于每个 call
指令都会将后续指令的地址压入堆栈,因此您可以从该地址识别父调用者。不是将 ebp
压入栈帧链表 在 栈上,而是将下一个 return 地址的偏移量存储在 .eh_frame_hdr
中部分,因此可以在需要回溯的代码需要时使用它。