This page validates as XHTML 1.0, and will look much better in a browser that supports web standards, but it is accessible to any browser or Internet device. It was created using techniques detailed at glish.com/css/.
Procfs watchpoints are a little used but very powerful debugging tool.
They are documented in man -s4 proc .
They allow a program to be interrupted when it performs a memory
read/write/execute within a specified range of addresses. A simple bit
of code that detects writes to an integer could be based on this ..
--------------------------------------------
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/errno.h>
#include <ucontext.h>
#include <procfs.h>
#include <sys/uio.h>
struct thing {
char buffer[64];
int x;
char buffer1[512];
} thingy;
void
sub( int * x)
{
*x=getpid();
}
int
main(int argc, char * argv[])
{
iovec_t iov[2];
struct {
long cmd;
union {
long flags;
prwatch_t prwatch;
} arg;
} ctl;
int pfd;
pfd = open("/proc/self/ctl",O_WRONLY);
if (pfd == -1)
abort()
ctl.cmd = PCWATCH;
ctl.arg.prwatch.pr_wflags = WA_READ|WA_WRITE|WA_EXEC;
ctl.arg.prwatch.pr_vaddr = (caddr_t)&(thingy.x);
ctl.arg.prwatch.pr_size = sizeof(int);
printf("setting watchpoint at address 0x%p size %d\n",
ctl.arg.prwatch.pr_vaddr, ctl.arg.prwatch.pr_size);
(void)write(pfd, (char *)&ctl, sizeof (long)+sizeof (prwatch_t));
sub(&(thingy.x));
}
-------------------------------------------so compiling that into bar and running that under mdb produces..
hoofhearted ksh: mdb bar
> :r
setting watchpoint at address 0x8060de0 size 4
mdb: target stopped at:
sub+0x10: movl %edx,(%eax)
>
> $r
%cs = 0x003b %eax = 0x08060de0 bar`thingy+0x40
%ds = 0x0043 %ebx = 0xbfffb824
%ss = 0x0043 %ecx = 0xbff9da37 libc.so.1`getpid+7
%es = 0x0043 %edx = 0x00000603
%fs = 0x0000 %esi = 0x08047264
%gs = 0x01c3 %edi = 0x080473a0
%eip = 0x08050ab0 sub+0x10
%ebp = 0x08047208
%kesp = 0x00000000
%eflags = 0x00010202
id=0 vip=0 vif=0 ac=0 vm=0 rf=1 nt=0 iopl=0x0
status=<of,df,IF,tf,sf,zf,af,pf,cf>
%esp = 0x08047200
%trapno = 0xe
%err = 0x6
>
So we took a SIGTRAP as soon as we tried to modify thingy.x. If the program had set the breakpoint on its own data then it would probably capture the signal, process it and return. If the breakpoint was set by a debugging process then the debugger would have registered an interest in that signal and could regain control at that point.
So I am using this in my debugging malloc library to watch the areas of the mmap that fall outside of the exact area requested in the call to malloc(). So far I have discovered that strlen has intimate knowledge of malloc and if the base address is aligned it uses a very clever algorithm to find the null terminating byte, but that causes it to use 32 bit load instructions to scan the string, so I have had to change the watchpoint at the end of the returned area to be a write watchpoint until we become 4 byte aligned and then to continue to the end of the page with a read/write watchpoint.
To avoid being accused of endianness I have finally got i86 and amd64 versions working -found a interesting little bug in the kernel whilst I was at it.
The procfs watchpoints work by clearing all permissions of any page that has any part of a watchpoint in it. Then when any access to the page happens we examine the avl tree of per process watch points to see if the accesed address is inside a watched area, if it is we send the SIGTRAP and setup to single step the process on return from the signal and set the page permissions back to normal. After the single instruction has accessed the memory we clear the page permissions and allow the process to run normally again. You can see why it is quite slow!
In the case of sparc the instructions size is 4 bytes and the data access size can be easily calculated from the instruction. On a x86 machine we have to decode the instruction thoroughly to find the instruction and data access size. The small bug I found caused that code to calculate the data access size for a pushl instruction in a 32 bit application running on an amd64 kernel as 8 bytes instead of the correct 4 bytes. So if the data being pushed onto the stack was the last 4 bytes before a watchpoint the kernel thought that you were accessing 8 bytes so triggered the watchpoint, took me all day to find it, I was sure it was my code.
The snails are off for a long holiday to the south coast to meet some new snailey friends,
to celebrate their departure here's an image of the big one and the smaller ones just hanging out...