我希望能够检测何时发生对内存地址的写入 - 例如通过设置附加到中断的回调。有谁知道怎么做?
我希望能够在运行时执行此操作(可能gdb具有此功能,但我的特殊功能 应用程序导致gdb崩溃。)
答案 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附加的忙循环。从那里你可以看到谁试图写下你的记忆。