我读过 LD_PRELOAD 可用于“检查点”程序......允许我们随意将程序“重置”回之前的状态。
这是一个粗略的简化。这种“检查点”机制不可能恢复任何打开的文件描述符或任何互斥体,因为它们的状态部分位于内核内部。
我很清楚我们如何记录内存状态。...
我想不通的是我们的预加载函数如何确定调用堆栈和指令指针;
指令指针在预加载的函数内部,并且在例如register void *rip __asm__("rip")
on中很容易获得x86_64
。但是你(很可能)不关心那个地址——你可能关心你的函数的调用者。这也很容易获得__builtin_return_address()
(至少在使用 GCC 时)。
并且调用堆栈的其余部分保存在内存中(更准确地说是在堆栈区域中),因此如果您知道内存的内容,就知道调用堆栈。
实际上,当您使用例如where
带有core
转储的 GDB 命令时,这正是 GDB 所做的——它从 中读取内存内容并从中core
恢复调用堆栈。
更新:
我在原来的帖子里写过,我知道如何检查内存,但实际上我只知道如何检查堆。如何查看所有堆栈帧的全部内容?
无论内存“属于”堆、堆栈还是代码,检查内存的工作方式都是一样的。您只需取消引用一个指针,然后瞧——您将获得该位置的内存内容。
你的意思可能是:
- 如何找到堆栈的位置和
- 如何解码
第一个问题的答案是特定于操作系统的,您没有用任何操作系统标记您的问题。
假设您在 Linux 上,定位堆栈的一种方法是解析条目以/proc/self/maps
查找“覆盖”当前堆栈(即“覆盖”任何局部变量的地址)的条目(连续地址范围)。
对于第二个问题,答案是:
- 这很复杂1和
- 您实际上不需要对其进行解码以保存/恢复其状态。
1要弄清楚如何解码堆栈,您可以查看调试器的源代码(例如 GDB 和 LLDB)。
这也是非常特定于操作系统和处理器的。
您需要了解调用约定。x86_64
您需要了解展开描述符。要查找局部变量,您需要了解DWARF
调试格式。
我提到它很复杂吗?