可以捕获写入地址(x86 - linux)

时间:2009-03-03 22:54:13

标签: c++ c linux x86

我希望能够检测何时发生对内存地址的写入 - 例如通过设置附加到中断的回调。有谁知道怎么做?

我希望能够在运行时执行此操作(可能gdb具有此功能,但我的特殊功能 应用程序导致gdb崩溃。)

4 个答案:

答案 0 :(得分:14)

如果要拦截对一系列地址的写入,可以使用mprotect()将所讨论的内存标记为不可写,并使用sigaction()安装信号处理程序以捕获生成的SIGSEGV ,做你的日志或其他什么,并将页面标记为可写。

答案 1 :(得分:8)

您需要访问X86调试寄存器:http://en.wikipedia.org/wiki/Debug_register

您需要在DR0到DR3之一中设置断点地址,然后在DR7中设置条件(数据写入)。将发生中断,您可以运行调试代码来读取DR6并找到导致断点的原因。

如果GDB不起作用,您可以尝试更简单/更小的调试器,例如http://sourceforge.net/projects/minibug/ - 如果这不起作用,您至少可以查看代码并了解如何使用调试硬件处理器自己。

此外,还有一个很好的IBM开发人员资源来掌握Linux调试技术,它应该提供一些额外的选项:

http://www.ibm.com/developerworks/linux/library/l-debug/

关于这样做的一篇相当不错的文章就是Windows(我知道你在Linux上运行,但其他人可能会想到在windows中这样做):

http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

- 亚当

答案 2 :(得分:4)

GDB确实具有该功能:它被称为硬件观察点,并且在Linux / x86上得到了很好的支持:

(gdb) watch *(int *)0x12345678

如果您的应用程序崩溃了GDB,请从CVS Head构建当前的GDB。

如果该GDB仍然失败,请提交GDB bug

我们可以更快地修复GDB,而不是破解SIGSEGV处理程序(提供了一个很好的测试用例),并且修复GDB也可以帮助您解决未来的问题。

答案 3 :(得分:3)

mprotect确实有一个缺点:你的内存必须是页面边界对齐的。我在堆栈上遇到了有问题的内存,无法使用mprotect()。

正如Adam所说,你想要的是操纵调试寄存器。在Windows上,我使用了这个:http://www.morearty.com/code/breakpoint/并且效果很好。我也将它移植到Mach-O(Mac OS X),它也很棒。它也很简单,因为Mach-O有thread_set_state(),它相当于SetThreadContext()。

linux的问题在于它没有这样的等价物。我找到了ptrace,但我想,这不可能,必须有更简单的东西。但事实并非如此。然而。我认为他们正在为内核和用户空间开发hw_breakpoint API。 (见http://lwn.net/Articles/317153/

但是当我发现这个时:http://blogs.oracle.com/nike/entry/memory_debugger_for_linux我试了一下它并没有那么糟糕。 ptrace方法通过一些“外部进程”充当“调试器”,附加到程序,为调试寄存器注入新值,并在程序中继续使用新的hw断点集。问题是,您可以使用fork()自己创建这个“外部进程”,(我没有使用pthread成功),并在代码中内联这些简单步骤。

addwatchpoint代码必须适用于64位linux,但这只是将USER_DR7等更改为offsetof(struct user,u_debugreg [7])。另一件事是在PTRACE_ATTACH之后,你必须等待调试对象实际停止。但是,不要在繁忙的循环中重试POKEUSER,正确的做法是在你的pid上使用waitpid()。

ptrace方法的唯一问题是你的程序一次只能附加一个“调试器”。因此,如果您的程序已在gdb控件下运行,则ptrace attach将失败。但就像示例代码一样,您可以为SIGTRAP注册一个信号处理程序,在没有gdb的情况下运行,当您捕获信号时,输入一个等待gdb附加的忙循环。从那里你可以看到谁试图写下你的记忆。