diff --git a/babl/babl-memory.c b/babl/babl-memory.c index 6ceca90..154dd21 100644 --- a/babl/babl-memory.c +++ b/babl/babl-memory.c @@ -16,6 +16,8 @@ * . */ +#define _GNU_SOURCE 1 + #include "config.h" #include #include @@ -23,6 +25,17 @@ #include #include "babl-internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + static BablMallocFunc malloc_f = malloc; static BablFreeFunc free_f = free; @@ -32,12 +45,24 @@ static void *first_free_used = NULL; void babl_set_malloc (BablMallocFunc malloc_function) { + fprintf (stderr, + "babl_set_malloc:\n" + " first_malloc_used: %p\n" + " old malloc_f: %p\n" + " malloc_function: %p\n", + first_malloc_used, malloc_f, malloc_function); malloc_f = malloc_function; } void babl_set_free (BablFreeFunc free_function) { + fprintf (stderr, + "babl_set_free:\n" + " first_free_used: %p\n" + " old free_f: %p\n" + " free_function: %p\n", + first_free_used, free_f, free_function); free_f = free_function; } @@ -78,6 +103,68 @@ mem_stats (void) #endif +static void set_watchpoint(void* addr) { + /* Thanks to jankratochvil in #gdb@freenode who pointed this out: + http://sources.redhat.com/cgi-bin/cvsweb.cgi/~checkout~/tests/ptrace-tests/tests/watchpoint.c?cvsroot=systemtap + */ + + pid_t child; + int status; + unsigned long dr7; + long l; + + child = fork (); + switch (child) + { + case -1: + assert (0); + case 0: + assert (ptrace (PTRACE_TRACEME, 0, NULL, NULL) == 0); + assert (raise (SIGTRAP) == 0); + + return; + default: + break; + } + + assert (waitpid (child, &status, 0) == child); + assert (WIFSTOPPED (status)); + assert (WSTOPSIG (status) == SIGTRAP); + + errno = 0; + l = ptrace (PTRACE_POKEUSER, child, + offsetof (struct user, u_debugreg[0]), (unsigned long) addr); + assert_perror (errno); + assert (l == 0); + + dr7 = (DR_RW_WRITE << DR_CONTROL_SHIFT); + +#ifdef DR_LEN_8 + /* + * For a 32-bit build, DR_LEN_8 might be defined by the header. + * On a 64-bit kernel, we might even be able to use it. + * But we can't tell, and we don't really need it, so just use DR_LEN_4. + */ + if (sizeof (long) > 4) + dr7 |= (DR_LEN_8 << DR_CONTROL_SHIFT); + else +#endif + dr7 |= (DR_LEN_4 << DR_CONTROL_SHIFT); + dr7 |= (1UL << DR_LOCAL_ENABLE_SHIFT); + dr7 |= (1UL << DR_GLOBAL_ENABLE_SHIFT); + + l = ptrace (PTRACE_POKEUSER, child, offsetof (struct user, u_debugreg[7]), dr7); + assert_perror (errno); + assert (l == 0); + + errno = 0; + l = ptrace (PTRACE_CONT, child, 0l, 0l); + assert_perror (errno); + assert (l == 0); + + exit(0); +} + static void functions_sanity (void) { @@ -86,11 +173,36 @@ functions_sanity (void) { if (first_malloc_used == NULL) { + fprintf (stderr, + "First use of malloc:\n" + " first_malloc_used: %p\n" + " malloc_f: %p\n" + " first_free_used: %p\n" + " free_f: %p\n", + first_malloc_used, malloc_f, first_free_used, free_f); + /* + fprintf (stderr, "Please attach debugger to pid %lu and press enter", + (unsigned long)getpid()); + { + char buf[30]; + fgets(buf, sizeof(buf), stdin); + } + */ first_malloc_used = malloc_f; first_free_used = free_f; + fprintf (stderr, "Watching for writes to %p\n", &malloc_f); + set_watchpoint(&malloc_f); } else { + fprintf (stderr, + "babl memory function(s) attempted switched on the fly:\n" + " first_malloc_used: %p\n" + " malloc_f: %p\n" + " first_free_used: %p\n" + " free_f: %p\n", + first_malloc_used, malloc_f, first_free_used, free_f); + abort (); babl_fatal ("babl memory function(s) attempted switched on the fly"); } }