CMS 3D CMS Logo

IgHook.cc

Go to the documentation of this file.
00001 //<<<<<< INCLUDES                                                       >>>>>>
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 //<<<<<< PRIVATE DEFINES                                                >>>>>>
00040 //<<<<<< PRIVATE CONSTANTS                                              >>>>>>
00041 //<<<<<< PRIVATE TYPES                                                  >>>>>>
00042 //<<<<<< PRIVATE VARIABLE DEFINITIONS                                   >>>>>>
00043 //<<<<<< PUBLIC VARIABLE DEFINITIONS                                    >>>>>>
00044 //<<<<<< CLASS STRUCTURE INITIALIZATION                                 >>>>>>
00045 //<<<<<< PRIVATE FUNCTION DEFINITIONS                                   >>>>>>
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     // IA32 branch can jump anywhere
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     // Allocate at end of memory so the address sign extends -- "ba"
00086     // can only take 24-bit immediate offset plus two zero bits
00087     // stripped off at the right end.  It can be either in the low 25
00088     // bits which can be crowded, or 26-bit address that sign extends
00089     // -- i.e. in high memory.  Thus the top six bits of the address
00090     // are required to be 1, i.e. 0xfe000000 .. 0xffffffff.
00091 
00092     // FIXME: a page per trampoline is a bit excessive...
00093     unsigned int pagesize = getpagesize ();
00094 #if __APPLE__ && __ppc__
00095     // Ask for a page in a specific place.  Note that this uses Mach's
00096     // vm_allocate, not mmap(), as mmap() + MAP_FIXED will happily map
00097     // over an existing memory mapping, and there does not seem to be
00098     // a convenient (unix-only) API to query whether the memory region
00099     // is already taken.
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     // Just ask for a page.  Let system position it, so we don't unmap
00108     // or remap over address space accidentally.
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     // (https://lists.apple.com/archives/Darwin-kernel/2005/Feb/msg00045.html)
00154     // The dynamic loader (dyld) loads pages into unmodifiable system-wide
00155     // shared map.  The first time we touch a page we need to make a
00156     // writable process-local copy of the page; on subsequent uses we
00157     // can just go ahead and flip the page writable.  We can't give
00158     // executable permissions on the page however.
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) /* push %e*x */
00266             ++n, ++insns;
00267 
00268         else if (insns [0] == 0x89 && insns [1] == 0xe5) /* mov %esp, %ebp */
00269             n += 2, insns += 2;
00270 
00271         else if (insns [0] == 0x89 && insns [1] == 0xda) /* mov %ebx, %edx */
00272             n += 2, insns += 2;
00273 
00274         else if (insns [0] == 0x83 && insns [1] == 0xec) /* sub $0x*, %esp */
00275             n += 3, insns += 3;
00276 
00277         else if (insns [0] == 0x81 && insns [1] == 0xec) /* sub $0x*, %esp (32-bit) */
00278             n += 6, insns += 6;
00279 
00280         else if (insns [0] == 0x8b && insns [2] == 0x24) /* mov 0x4(%esp,1),%e*x */
00281             n += 4, insns += 4;
00282 
00283         else if (insns [0] == 0x8d && insns [1] == 0x55) /* lea $0x*(%ebp),%edx */
00284             n += 3, insns += 3;
00285 
00286         else if (insns [0] >= 0xb8 && insns [0] <= 0xbf) /* mov $0xNN,%e*x */
00287             n += 5, insns += 5;
00288 
00289         else if (insns [0] == 0xff && insns [1] == 0x25) /* jmp *addr */
00290             n += 6, insns += 6;
00291 
00292         else if (insns [0] == 0x65 && insns [1] == 0x83 && insns [2] == 0x3d)
00293             n += 8, insns += 8;                          /* cmpl $0x*,%gs:0x* */
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     // FIXME: check for various branch-relative etc. instructions
00304     assert (sizeof (unsigned int) == 4);
00305     unsigned int *insns = (unsigned int *) address;
00306     unsigned int instr = *insns;
00307     if ((instr & 0xfc1fffff) == 0x7c0903a6) // check it's not mfctr
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     // NB: jump offsets are calculated from *after* the jump instruction
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     // The low six bits are "ba" instruction (opcode 18 = 0x12),
00342     // then immediate address with the low two bits stripped off,
00343     // and top two bits are "01" (no link, absolute).  This only
00344     // works if the address is appropriate.  The 24-bit immediate
00345     // address is sign extended to 32 bits, so either it must be
00346     // in the low 23-bit address space, or in the high area.
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     // *insns++ = 0x40000012 | (((unsigned int) to >> 2) << 5); // ba to
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     // Set ctr using r0 as a temporary register.  The assumption here
00372     // is that this instruction sequence comes as immediate target of
00373     // a call in the re-entry part of the trampoline, meaning that we
00374     // are allowed to trash r0 (it's a volatile register so caller
00375     // must have saved it).  The instruction copied from the original
00376     // prologue comes after this one and must not trash ctr (parse()
00377     // ensures that) but may trash r0 after us.
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); // lis r0,addrhi
00383     *insns++ = 0x60000000 | (((unsigned int) to & 0x0000ffff) >> 0);  // ori r0,r0,addrlo
00384     *insns++ = 0x7c0903a6; // mtctr r0  
00385     from = insns;
00386     return (insns - start) * 4;
00387 #else
00388     // nothing to do
00389     return 0;
00390 #endif
00391 }
00392 
00394 static int
00395 postreentry (void *&from, void *to)
00396 {
00397 #if defined __i386__
00398     // Real jump
00399     return redirect (from, to);
00400 #elif defined __ppc__
00401     // ctr was set in prereentry(), jump into it
00402     assert (sizeof (unsigned int) == 4);
00403     unsigned int *start = (unsigned int *) from;
00404     unsigned int *insns = (unsigned int *) from;
00405     *insns++ = 0x4e800420; // bctr
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     // First part: unconditional jump to replacement
00423     prereentry (address, replacement);
00424     postreentry (address, replacement);
00425 
00426     // Second part: old function prologue + jump to post-prolugue code
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     // FIXME: Not atomic, freeze all other threads!
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; // nop
00449 #else
00450         // can't happen!
00451         abort ();
00452 #endif
00453     }
00454 }
00455 
00456 //<<<<<< PUBLIC FUNCTION DEFINITIONS                                    >>>>>>
00457 //<<<<<< MEMBER FUNCTION DEFINITIONS                                    >>>>>>
00458 
00459 IgHook::Status
00460 IgHook::hook (const char *function,
00461               const char *version,
00462               const char *library,
00463               void *replacement,
00464               int options /* = 0 */,
00465               void **chain /* = 0 */,
00466               void **original /* = 0 */,
00467               void **trampoline)
00468 {
00469     // For future compatibility -- call vs. jump, counting etc.
00470     if (options != 0)
00471         return ErrBadOptions;
00472 
00473     // Zero out variables
00474     if (chain) *chain = 0;
00475     if (original) *original = 0;
00476     if (trampoline) *trampoline = 0;
00477 
00478     // Lookup function
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     // See if we understand it
00487     int prologue = parse (function, sym);
00488     if (prologue < 0)
00489         return ErrPrologueNotRecognised;
00490     else if (prologue > TRAMPOLINE_SAVED)
00491         return ErrPrologueTooLarge;
00492 
00493     // Prepare trampoline
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     // Attach trampoline
00511     if ((s = protect (sym, true)) != Success)
00512     {
00513         release (tramp);
00514         return s;
00515     }
00516 
00517     patch (sym, tramp, prologue);
00518 
00519     // Restore privileges and flush caches
00520     // No: protect (tramp, false); -- segvs on linux, full page might not been allocated?
00521     protect (sym, false);
00522     flush (tramp);
00523     flush (sym);
00524 
00525     return Success;
00526 }

Generated on Tue Jun 9 17:38:08 2009 for CMSSW by  doxygen 1.5.4