00001
00002
00003 #include "IgTools/IgHook/interface/IgHook.h"
00004 #include <cstdio>
00005 #include <cstdlib>
00006 #include <cstring>
00007 #include <cstdarg>
00008 #include <cerrno>
00009 #include <cassert>
00010 #include <dlfcn.h>
00011 #include <unistd.h>
00012 #include <sys/mman.h>
00013 #include <sys/time.h>
00014
00015 #if __APPLE__
00016 #include <mach/mach.h>
00017 #endif
00018
00019 #if defined __i386__
00020 # define TRAMPOLINE_JUMP 5 // jump to hook/old code
00021 # define TRAMPOLINE_SAVED 10 // 5+margin for saved prologue
00022 #elif defined __ppc__
00023 # define TRAMPOLINE_JUMP 16 // jump to hook/old code
00024 # define TRAMPOLINE_SAVED 4 // one prologue instruction to save
00025 #else
00026 # error sorry this platform is not supported
00027 #endif
00028
00029 #define TRAMPOLINE_SIZE (TRAMPOLINE_JUMP+TRAMPOLINE_SAVED+TRAMPOLINE_JUMP)
00030
00031 #if !defined MAP_ANONYMOUS && defined MAP_ANON
00032 # define MAP_ANONYMOUS MAP_ANON
00033 #endif
00034
00035 #if !__linux
00036 # define dlvsym(h,fn,v) dlsym(h,fn)
00037 #endif
00038
00039
00040
00041
00042
00043
00044
00045
00046
00049 static void
00050 debug (const char *format, ...)
00051 {
00052 static const char *debugging = getenv ("IGHOOK_DEBUGGING");
00053 if (debugging)
00054 {
00055 timeval tv;
00056 gettimeofday (&tv, 0);
00057 fprintf (stderr, "*** IgHook(%lu, %.3f): ",
00058 (unsigned long) getpid(),
00059 tv.tv_sec + 1e-6*tv.tv_usec);
00060
00061 va_list args;
00062 va_start (args, format);
00063 vfprintf (stderr, format, args);
00064 va_end (args);
00065 }
00066 }
00067
00069
00073 static IgHook::Status
00074 allocate (void *&ptr)
00075 {
00076 #if 0 && defined __i386__
00077
00078 if (! (ptr = malloc (TRAMPOLINE_SIZE)))
00079 {
00080 debug ("malloc() failed, can't crete trampoline\n");
00081 return IgHook::ErrAllocateTrampoline;
00082 }
00083 return IgHook::Success;
00084 #elif 1 || __ppc__
00085
00086
00087
00088
00089
00090
00091
00092
00093 unsigned int pagesize = getpagesize ();
00094 #if __APPLE__ && __ppc__
00095
00096
00097
00098
00099
00100 vm_address_t limit = 0xfeffffff;
00101 vm_address_t address = 0xfe000000;
00102 kern_return_t retcode;
00103 do retcode = vm_allocate (mach_task_self (), &address, pagesize, FALSE);
00104 while (retcode != KERN_SUCCESS && (address += pagesize) < limit);
00105 void *addr = (address < limit ? (void *) address : MAP_FAILED);
00106 #else
00107
00108
00109 void *addr = mmap (0, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC,
00110 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
00111 #endif
00112 if (addr != MAP_FAILED)
00113 {
00114 unsigned int *page = (unsigned int *) addr;
00115 *page = pagesize;
00116 ptr = ++page;
00117 return IgHook::Success;
00118 }
00119 else
00120 {
00121 ptr = 0;
00122 return IgHook::ErrAllocateTrampoline;
00123 }
00124 #endif
00125 }
00126
00128 static void
00129 release (void *ptr)
00130 {
00131 #if defined __i386__
00132 free (ptr);
00133 #else
00134 unsigned int *page = (unsigned int *) ptr;
00135 --page;
00136 munmap (page, *page);
00137 #endif
00138 }
00139
00145 static IgHook::Status
00146 protect (void *address, bool writable)
00147 {
00148 assert (sizeof (address) <= sizeof (unsigned long));
00149
00150 int pagesize = getpagesize ();
00151 address = (void *) (((unsigned long) address) & ~(pagesize-1));
00152 #if __APPLE__
00153
00154
00155
00156
00157
00158
00159 mach_port_t self = mach_task_self ();
00160 vm_address_t vmaddr = (vm_address_t) address;
00161 vm_prot_t protection = VM_PROT_READ | VM_PROT_WRITE;
00162 kern_return_t retcode = vm_protect (self, vmaddr, pagesize, false, protection);;
00163 if (writable && retcode != KERN_SUCCESS)
00164 {
00165 protection = VM_PROT_READ | VM_PROT_COPY;
00166 retcode = vm_protect (self, vmaddr, pagesize, FALSE, protection);
00167 }
00168 if (retcode != KERN_SUCCESS)
00169 {
00170 debug ("vm_protect(%p, %d, %d): %d\n",
00171 address, pagesize, protection, retcode);
00172 return IgHook::ErrMemoryProtection;
00173 }
00174 #else
00175 int protection = PROT_READ | PROT_EXEC | (writable ? PROT_WRITE : 0);
00176 if (mprotect (address, pagesize, protection))
00177 {
00178 debug ("mprotect(%p, %d, %d): %d\n",
00179 address, pagesize, protection, errno);
00180 return IgHook::ErrMemoryProtection;
00181 }
00182 #endif
00183
00184 return IgHook::Success;
00185 }
00186
00190 static void
00191 flush (void *address)
00192 {
00193 msync (address, TRAMPOLINE_SIZE, MS_INVALIDATE);
00194 }
00195
00197 static void *
00198 skip (void *&ptr, int n)
00199 { ptr = (unsigned char *) ptr + n; return ptr; }
00200
00202
00208 static IgHook::Status
00209 lookup (const char *fn, const char *v, const char *lib, void *&sym)
00210 {
00211 sym = 0;
00212
00213 if (lib)
00214 {
00215 void *handle = dlopen (lib, RTLD_LAZY | RTLD_GLOBAL);
00216 if (! handle)
00217 {
00218 debug ("dlopen('%s'): %s\n", lib, dlerror ());
00219 return IgHook::ErrLibraryNotFound;
00220 }
00221
00222 sym = v ? dlvsym (handle, fn, v) : dlsym (handle, fn);
00223 if (! sym)
00224 {
00225 debug ("dlsym('%s', '%s'): %s\n", lib, fn, dlerror ());
00226 return IgHook::ErrSymbolNotFoundInLibrary;
00227 }
00228 }
00229 else
00230 {
00231 void *program = dlopen (0, RTLD_LAZY | RTLD_GLOBAL);
00232 sym = v ? dlvsym (program, fn, v) : dlsym (program, fn);
00233 dlclose (program);
00234 if (! sym) sym = v ? dlvsym (program, fn, v) : dlsym (RTLD_NEXT, fn);
00235 if (! sym)
00236 {
00237 debug ("dlsym(self, '%s'): %s\n", fn, dlerror ());
00238 return IgHook::ErrSymbolNotFoundInSelf;
00239 }
00240 }
00241
00242 return IgHook::Success;
00243 }
00244
00249 static int
00250 parse (const char *func, void *address)
00251 {
00252 int n = 0;
00253
00254 #if defined __i386__
00255 unsigned char *insns = (unsigned char *) address;
00256 if (insns [0] == 0xe9)
00257 {
00258 debug ("%s (%p): hook trampoline already installed, ignoring\n",
00259 func, address);
00260 return -1;
00261 }
00262
00263 while (n < 5)
00264 {
00265 if (insns [0] >= 0x50 && insns [0] <= 0x57)
00266 ++n, ++insns;
00267
00268 else if (insns [0] == 0x89 && insns [1] == 0xe5)
00269 n += 2, insns += 2;
00270
00271 else if (insns [0] == 0x89 && insns [1] == 0xda)
00272 n += 2, insns += 2;
00273
00274 else if (insns [0] == 0x83 && insns [1] == 0xec)
00275 n += 3, insns += 3;
00276
00277 else if (insns [0] == 0x81 && insns [1] == 0xec)
00278 n += 6, insns += 6;
00279
00280 else if (insns [0] == 0x8b && insns [2] == 0x24)
00281 n += 4, insns += 4;
00282
00283 else if (insns [0] == 0x8d && insns [1] == 0x55)
00284 n += 3, insns += 3;
00285
00286 else if (insns [0] >= 0xb8 && insns [0] <= 0xbf)
00287 n += 5, insns += 5;
00288
00289 else if (insns [0] == 0xff && insns [1] == 0x25)
00290 n += 6, insns += 6;
00291
00292 else if (insns [0] == 0x65 && insns [1] == 0x83 && insns [2] == 0x3d)
00293 n += 8, insns += 8;
00294
00295 else
00296 {
00297 debug ("%s (%p) + 0x%x: unrecognised prologue (found 0x%x)\n",
00298 func, address, insns - (unsigned char *) address, *insns);
00299 return -1;
00300 }
00301 }
00302 #elif defined __ppc__
00303
00304 assert (sizeof (unsigned int) == 4);
00305 unsigned int *insns = (unsigned int *) address;
00306 unsigned int instr = *insns;
00307 if ((instr & 0xfc1fffff) == 0x7c0903a6)
00308 {
00309 debug ("%s (%p): mfctr can't be instrumented\n", func, address);
00310 return -1;
00311 }
00312
00313 n = 4;
00314 #endif
00315
00316 return n;
00317 }
00318
00324 static int
00325 redirect (void *&from, void *to)
00326 {
00327 #if defined __i386__
00328
00329 unsigned char *start = (unsigned char *) from;
00330 unsigned char *insns = (unsigned char *) from;
00331 unsigned long diff = (unsigned long) to - ((unsigned long) from + 5);
00332 *insns++ = 0xe9;
00333 *insns++ = diff & 0xff;
00334 *insns++ = (diff >> 8) & 0xff;
00335 *insns++ = (diff >> 16) & 0xff;
00336 *insns++ = (diff >> 24) & 0xff;
00337 from = insns;
00338 return insns - start;
00339
00340 #elif defined __ppc__
00341
00342
00343
00344
00345
00346
00347 unsigned int *start = (unsigned int *) from;
00348 unsigned int *insns = (unsigned int *) from;
00349
00350 assert (sizeof (unsigned int) == 4);
00351 assert (! ((unsigned int) to & 0x3));
00352
00353 *insns++ = 0x48000002 | ((unsigned int) to & 0x3ffffff);
00354 from = insns;
00355 return (insns - start) * 4;
00356 #endif
00357 }
00358
00367 static int
00368 prereentry (void *&from, void *to)
00369 {
00370 #if defined __ppc__
00371
00372
00373
00374
00375
00376
00377
00378 assert (sizeof (to) == sizeof (unsigned int));
00379 assert (sizeof (unsigned int) == 4);
00380 unsigned int *start = (unsigned int *) from;
00381 unsigned int *insns = (unsigned int *) from;
00382 *insns++ = 0x3c000000 | (((unsigned int) to & 0xffff0000) >> 16);
00383 *insns++ = 0x60000000 | (((unsigned int) to & 0x0000ffff) >> 0);
00384 *insns++ = 0x7c0903a6;
00385 from = insns;
00386 return (insns - start) * 4;
00387 #else
00388
00389 return 0;
00390 #endif
00391 }
00392
00394 static int
00395 postreentry (void *&from, void *to)
00396 {
00397 #if defined __i386__
00398
00399 return redirect (from, to);
00400 #elif defined __ppc__
00401
00402 assert (sizeof (unsigned int) == 4);
00403 unsigned int *start = (unsigned int *) from;
00404 unsigned int *insns = (unsigned int *) from;
00405 *insns++ = 0x4e800420;
00406 from = insns;
00407 return (insns - start) * 4;
00408 #endif
00409 }
00410
00419 static void
00420 prepare (void *address, void *replacement, void **chain, void *old, int prologue)
00421 {
00422
00423 prereentry (address, replacement);
00424 postreentry (address, replacement);
00425
00426
00427 if (chain) *chain = address;
00428 prereentry (address, ((unsigned char *) old) + prologue);
00429 memcpy (address, old, prologue);
00430 skip (address, prologue);
00431 skip (old, prologue);
00432 postreentry (address, old);
00433 }
00434
00440 static void
00441 patch (void *address, void *trampoline, int prologue)
00442 {
00443
00444 unsigned char *insns = (unsigned char *) address;
00445 for (int i = redirect (address, trampoline); i < prologue; ++i)
00446 {
00447 #if defined __i386__
00448 insns [i] = 0x90;
00449 #else
00450
00451 abort ();
00452 #endif
00453 }
00454 }
00455
00456
00457
00458
00459 IgHook::Status
00460 IgHook::hook (const char *function,
00461 const char *version,
00462 const char *library,
00463 void *replacement,
00464 int options ,
00465 void **chain ,
00466 void **original ,
00467 void **trampoline)
00468 {
00469
00470 if (options != 0)
00471 return ErrBadOptions;
00472
00473
00474 if (chain) *chain = 0;
00475 if (original) *original = 0;
00476 if (trampoline) *trampoline = 0;
00477
00478
00479 Status s;
00480 void *sym = 0;
00481 if ((s = lookup (function, version, library, sym)) != Success)
00482 return s;
00483
00484 if (original) *original = sym;
00485
00486
00487 int prologue = parse (function, sym);
00488 if (prologue < 0)
00489 return ErrPrologueNotRecognised;
00490 else if (prologue > TRAMPOLINE_SAVED)
00491 return ErrPrologueTooLarge;
00492
00493
00494 void *tramp = 0;
00495 if ((s = allocate (tramp)) != Success)
00496 return s;
00497
00498 if (trampoline)
00499 *trampoline = tramp;
00500
00501 if (version)
00502 debug ("%s/%s (%p): instrumenting %d bytes into %p\n",
00503 function, version, sym, prologue, tramp);
00504 else
00505 debug ("%s (%p): instrumenting %d bytes into %p\n",
00506 function, sym, prologue, tramp);
00507
00508 prepare (tramp, replacement, chain, sym, prologue);
00509
00510
00511 if ((s = protect (sym, true)) != Success)
00512 {
00513 release (tramp);
00514 return s;
00515 }
00516
00517 patch (sym, tramp, prologue);
00518
00519
00520
00521 protect (sym, false);
00522 flush (tramp);
00523 flush (sym);
00524
00525 return Success;
00526 }