tags:

views:

499

answers:

5

I want to be able to detect when a write to memory address occurs -- for example by setting a callback attached to an interrupt. Does anyone know how?

I'd like to be able to do this at runtime (possibly gdb has this feature, but my particular application causes gdb to crash).

+5  A: 

What you need is access to the X86 debug registers: http://en.wikipedia.org/wiki/Debug_register

You'll need to set the breakpoint address in one of DR0 to DR3, and then the condition (data write) in DR7. The interrupt will occur and you can run your debug code to read DR6 and find what caused the breakpoint.

If GDB doesn't work, you might try a simpler/smaller debugger such as http://sourceforge.net/projects/minibug/ - if that isn't working, you can at least go through the code and understand how to use the debugging hardware on the processor yourself.

Also, there's a great IBM developer resource on mastering linux debugging techniques which should provide some additional options:

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

A reasonably good article on doing this is windows is here (I know you're running on linux, but others might come along to this question wanting to do it in windows):

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

Adam Davis
The debug registers can only be accessed at privilege level 0, i.e. in the kernel. See http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm
Adam Rosenfield
+7  A: 

If you want to intercept writes to a range of addresses, you can use mprotect() to mark the memory in question as non-writeable, and install a signal handler using sigaction() to catch the resulting SIGSEGV, do your logging or whatever and mark the page as writeable again.

Khoth
+3  A: 

GDB does have that feature: it is called hardware watchpoints, and it is very well supported on Linux/x86:

(gdb) watch *(int *)0x12345678

If your application crashes GDB, build current GDB from CVS Head.

If that GDB still fails, file a GDB bug.

Chances are we can fix GDB faster than you can hack around SIGSEGV handler (provided a good test case), and fixes to GDB help you with future problems as well.

Employed Russian
+1, I just found a new use for GDB :)
Tim Post
A: 

why dont you use ptrace to acheive the same? let me know if you want to know more about it?

Md.Ayyaz A Mulla
A: 

mprotect does have a disadvantage: your memory must be page-boundary aligned. I had my problematic memory on the stack and was not able to use mprotect().

As Adam said, what you want is to manipulate the debug registers. On windows, I used this: hxxp://www.morearty.com/code/breakpoint/ and it worked great. I also ported it to Mach-O (Mac OS X), and it worked great, too. It was also easy, because Mach-O has thread_set_state(), which is equivalent to SetThreadContext().

The Problem with linux is that it doesn't have such equivalents. I found ptrace, but I thought, this can't be it, there must be something simpler. But there isn't. Yet. I think they are working on a hw_breakpoint API for both kernel and user space. (see hxxp://lwn.net/Articles/317153/)

But when I found this: http://blogs.sun.com/nike/entry/memory_debugger_for_linux I gave it a try and it wasn't that bad. The ptrace method works by some "outside process" acting as a "debugger", attaching to your program, injecting new values for the debug registers, and terminating with your program continuing with a new hw breakpoint set. The thing is, you can create this "outside process" yourself by using fork(), (I had no success with a pthread), and doing these simple steps inline in your code.

The addwatchpoint code must be adapted to work with 64 bit linux, but that's just changing USER_DR7 etc. to offsetof(struct user, u_debugreg[7]). Another thing is that after a PTRACE_ATTACH, you have to wait for the debuggee to actually stop. But instead of retrying a POKEUSER in a busy loop, the correct thing to do would be a waitpid() on your pid.

The only catch with the ptrace method is that your program can have only one "debugger" attached at a time. So a ptrace attach will fail if your program is already running under gdb control. But just like the example code does, you can register a signal handler for SIGTRAP, run without gdb, and when you catch the signal, enter a busy loop waiting for gdb to attach. From there you can see who tried to write your memroy.

(Change all hxxp: to http:, as I am not allowed to use hyperlinks as a new user)

Peter Remmers