8

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

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

4

4 回答 4

15

如果您想拦截对一系列地址的写入,您可以使用mprotect()将有问题的内存标记为不可写,并安装一个信号处理程序sigaction()来捕获生成的 SIGSEGV,执行您的日志记录或其他操作并将页面再次标记为可写.

于 2009-03-03T23:02:10.627 回答
8

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

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

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

此外,还有一个关于掌握 linux 调试技术的优秀 IBM 开发人员资源,它应该提供一些额外的选项:

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

关于这样做的一篇相当不错的文章是 windows 在这里(我知道你在 linux 上运行,但其他人可能会提出这个问题,希望在 windows 中执行此操作):

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

-亚当

于 2009-03-03T22:57:44.460 回答
4

GDB 确实有这个特性:它被称为硬件观察点,它在 Linux/x86 上得到了很好的支持:

(gdb) watch *(int *)0x12345678

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

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

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

于 2009-03-04T06:35:27.277 回答
4

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

正如亚当所说,你想要的是操纵调试寄存器。在 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 方法由一些充当“调试器”的“外部进程”工作,附加到您的程序,为调试寄存器注入新值,并终止您的程序继续设置新的硬件断点。问题是,您可以使用 fork() 自己创建这个“外部进程”(我没有成功使用 pthread),并在代码中内联执行这些简单的步骤。

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

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

于 2010-09-07T08:24:50.703 回答