/* $Id$ */ #include #include #include #include #include #include #include #include #include "hook.h" #include "hook-arch-c.h" #include "build.h" #include "maps.h" #include "common.h" static sigaction_t sigaction_orig; /* == sigaction_orig_libc + trampoline_size */ INTERNAL sigaction_t sigaction_orig_libc_cont; INTERNAL int debug; typedef void (*sa_sigaction_t) (int, siginfo_t *, void *); static const sa_sigaction_t *const_sigaction_pointer_get (const struct sigaction *act) { if (act->sa_flags & SA_SIGINFO) return (sa_sigaction_t *) &act->sa_sigaction; else return (sa_sigaction_t *) &act->sa_handler; } static sa_sigaction_t *sigaction_pointer_get (struct sigaction *act) { return (sa_sigaction_t *) const_sigaction_pointer_get ((const struct sigaction *) act); } /* `SA_RESETHAND' will reset `*sigaction_pointer_get ()' to `SIG_DFL' even for `sigaction' parameter `oldact'. `SA_RESETHAND' will stay set there. */ static struct sigaction act_app[NSIG]; static void handler (int signum, siginfo_t *siginfo, void *ucontext_t_pointer) { struct sigaction *act = &act_app[signum]; sa_sigaction_t *act_sigaction_pointer; act_sigaction_pointer = sigaction_pointer_get (act); if (*act_sigaction_pointer != (sa_sigaction_t) SIG_DFL) (*act_sigaction_pointer) (signum, siginfo, ucontext_t_pointer); else { struct sigaction sig_dfl; /* Reset the handler first to get caught SEGV in core_dump(). */ sig_dfl.sa_handler = SIG_DFL; sigemptyset (&sig_dfl.sa_mask); sig_dfl.sa_flags = 0; (*sigaction_orig) (signum, act, NULL); core_dump (); } if (act->sa_flags & SA_RESETHAND) *act_sigaction_pointer = (sa_sigaction_t) SIG_DFL; } static int sigaction_subst (int signum, const struct sigaction *act, struct sigaction *oldact) { struct sigaction act_new, act_app_saved; int retval; switch (signum) { /* Core dumping signals: */ #define SIGNAL_CORE(name) case name: #include "signal-core.h" #undef SIGNAL_CORE break; default: return (*sigaction_orig) (signum, act, oldact); } if (oldact != NULL) act_app_saved = act_app[signum]; if (act != NULL) { const sa_sigaction_t *const_act_sigaction_pointer; const_act_sigaction_pointer = const_sigaction_pointer_get (act); if (*const_act_sigaction_pointer == (sa_sigaction_t) SIG_DFL || act->sa_flags & SA_RESETHAND) { sa_sigaction_t *act_sigaction_pointer; act_app[signum] = *act; act_new = *act; act = &act_new; act_sigaction_pointer = sigaction_pointer_get (&act_new); *act_sigaction_pointer = handler; act_new.sa_flags &= ~SA_RESETHAND; } } retval = (*sigaction_orig) (signum, act, oldact); if (oldact != NULL) { sa_sigaction_t *oldact_sigaction_pointer; assert (memcmp (&act_app_saved.sa_mask, &oldact->sa_mask, sizeof (&act_app_saved.sa_mask)) == 0); assert ((act_app_saved.sa_flags & ~SA_RESETHAND) == (oldact->sa_flags & ~SA_RESETHAND)); oldact_sigaction_pointer = sigaction_pointer_get (oldact); if (*oldact_sigaction_pointer == handler) *oldact_sigaction_pointer = *(sigaction_pointer_get (&act_app[signum])); oldact->sa_flags = act_app_saved.sa_flags; } return retval; } static void sigaction_orig_patch (void) { unsigned long page_start, page_end; sigaction_t sigaction_orig_libc; sigaction_orig_libc = sigaction_orig; page_start = (unsigned long) sigaction_orig_libc & PAGE_MASK; page_end = ((unsigned long) sigaction_orig_libc + trampoline_size + PAGE_SIZE - 1) & PAGE_MASK; maps_read (); if (maps_verify ((void *) page_start, (void *) page_end)) { fprintf (stderr, "libobjid: Invalid \"%s\" (0x%lx, %u)\n", MAPS_FILENAME, page_start, (unsigned) (page_end - page_start)); return; } maps_static_setup ((void *) page_start, (void *) page_end); if (mprotect ((void *) page_start, page_end - page_start, PROT_READ | PROT_WRITE | PROT_EXEC)) { fprintf (stderr, "libobjid: mprotect (0x%lx, %u): %m\n", page_start, (unsigned) (page_end - page_start)); return; } if (memcmp (sigaction_orig_libc, sigaction_trampoline, trampoline_size) != 0) { fprintf (stderr, "libobjid: libc prologue (%p) is not matching\n", sigaction_orig_libc); return; } /* Target jump from our trampoline. */ sigaction_orig_libc_cont = (sigaction_t) ((char *) sigaction_orig_libc + trampoline_size); /* Jump instruction to our `sigaction_subst'. */ memcpy (sigaction_orig_libc, sigaction_trampoline_jmpdir, trampoline_size); /* Long direct jump operand is relative on i386 but absolute on x86_64. */ #define REBASE(ptr) ((char *) (ptr) - (char *) sigaction_trampoline_jmpdir \ + (char *) sigaction_orig_libc) *(void **) REBASE (sigaction_trampoline_jmpdir_vec) = (void *) ((char *) sigaction_subst - ((void *) sigaction_trampoline_jmpdir_base == (void *) sigaction_trampoline ? NULL : REBASE (sigaction_trampoline_jmpdir_base))); #undef REBASE /* Returning through our trampoline code back to the original function. */ sigaction_orig = sigaction_trampoline; } static void signal_init (int signum) { sighandler_t handler_orig; handler_orig = signal (signum, SIG_DFL); if (handler_orig != SIG_DFL) fprintf (stderr, "libobjid: signal_init: %d: %p\n", signum, handler_orig); } static void *libc_handle; static void libobjid_init (void) __attribute__((__constructor__)); static void libobjid_init (void) { char *debug_str; debug_str = getenv ("LIBOBJID"); if (debug_str != NULL) debug = atoi (debug_str); if (libc_handle == NULL) { libc_handle = dlopen (NULL, RTLD_LAZY); /* RTLD_LAZY is mandatory */ if (libc_handle == NULL) fprintf (stderr, "libobjid: dlopen (NULL): %s\n", dlerror ()); } if (libc_handle == NULL) return; #define SIGACTION_NAME "sigaction" sigaction_orig = (sigaction_t) dlsym (libc_handle, SIGACTION_NAME); if (sigaction_orig == NULL) fprintf (stderr, "libobjid: dlsym (\"%s\"): %s\n", SIGACTION_NAME, dlerror ()); #undef SIGACTION_NAME if (sigaction_orig == NULL) return; sigaction_orig_patch (); #define SIGNAL_CORE(name) signal_init (name); #include "signal-core.h" #undef SIGNAL_CORE } static void libobjid_fini (void) __attribute__((__constructor__)); static void libobjid_fini (void) { if (libc_handle != NULL && dlclose (libc_handle)) fprintf (stderr, "libobjid: dlclose (): %s\n", dlerror ()); libc_handle = NULL; }