Initial import.
[libobjid.git] / hook.c
1 /* $Id$ */
2
3
4 #include <signal.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <string.h>
8 #include <dlfcn.h>
9 #include <asm/page.h>
10 #include <sys/mman.h>
11 #include <stdlib.h>
12
13 #include "hook.h"
14 #include "hook-arch-c.h"
15 #include "build.h"
16 #include "maps.h"
17 #include "common.h"
18
19
20 static sigaction_t sigaction_orig;
21 /* == sigaction_orig_libc + trampoline_size  */
22 INTERNAL sigaction_t sigaction_orig_libc_cont;
23
24
25 INTERNAL int debug;
26
27 typedef void (*sa_sigaction_t) (int, siginfo_t *, void *);
28
29 static const sa_sigaction_t *const_sigaction_pointer_get (const struct sigaction *act)
30 {
31   if (act->sa_flags & SA_SIGINFO)
32     return (sa_sigaction_t *) &act->sa_sigaction;
33   else
34     return (sa_sigaction_t *) &act->sa_handler;
35 }
36
37 static sa_sigaction_t *sigaction_pointer_get (struct sigaction *act)
38 {
39   return (sa_sigaction_t *) const_sigaction_pointer_get
40     ((const struct sigaction *) act);
41 }
42
43 /* `SA_RESETHAND' will reset `*sigaction_pointer_get ()' to `SIG_DFL'
44    even for `sigaction' parameter `oldact'.
45    `SA_RESETHAND' will stay set there.  */
46
47 static struct sigaction act_app[NSIG];
48
49 static void handler (int signum, siginfo_t *siginfo, void *ucontext_t_pointer)
50 {
51   struct sigaction *act = &act_app[signum];
52   sa_sigaction_t *act_sigaction_pointer;
53
54   act_sigaction_pointer = sigaction_pointer_get (act);
55   if (*act_sigaction_pointer != (sa_sigaction_t) SIG_DFL)
56     (*act_sigaction_pointer) (signum, siginfo, ucontext_t_pointer);
57   else
58     {
59       struct sigaction sig_dfl;
60
61       /* Reset the handler first to get caught SEGV in core_dump().  */
62       sig_dfl.sa_handler = SIG_DFL;
63       sigemptyset (&sig_dfl.sa_mask);
64       sig_dfl.sa_flags = 0;
65       (*sigaction_orig) (signum, act, NULL);
66
67       core_dump ();
68     }
69   if (act->sa_flags & SA_RESETHAND)
70     *act_sigaction_pointer = (sa_sigaction_t) SIG_DFL;
71 }
72
73 static int sigaction_subst (int signum, const struct sigaction *act,
74                             struct sigaction *oldact)
75 {
76   struct sigaction act_new, act_app_saved;
77   int retval;
78
79   switch (signum)
80     {
81       /* Core dumping signals:  */
82 #define SIGNAL_CORE(name) case name:
83 #include "signal-core.h"
84 #undef SIGNAL_CORE
85         break;
86
87       default:
88         return (*sigaction_orig) (signum, act, oldact);
89     }
90   if (oldact != NULL)
91     act_app_saved = act_app[signum];
92   if (act != NULL)
93     {
94       const sa_sigaction_t *const_act_sigaction_pointer;
95
96       const_act_sigaction_pointer = const_sigaction_pointer_get (act);
97       if (*const_act_sigaction_pointer == (sa_sigaction_t) SIG_DFL
98           || act->sa_flags & SA_RESETHAND)
99         {
100           sa_sigaction_t *act_sigaction_pointer;
101
102           act_app[signum] = *act;
103           act_new = *act;
104           act = &act_new;
105           act_sigaction_pointer = sigaction_pointer_get (&act_new);
106           *act_sigaction_pointer = handler;
107           act_new.sa_flags &= ~SA_RESETHAND;
108         }
109     }
110   retval = (*sigaction_orig) (signum, act, oldact);
111   if (oldact != NULL)
112     {
113       sa_sigaction_t *oldact_sigaction_pointer;
114
115       assert (memcmp (&act_app_saved.sa_mask, &oldact->sa_mask,
116               sizeof (&act_app_saved.sa_mask)) == 0);
117       assert ((act_app_saved.sa_flags & ~SA_RESETHAND)
118               == (oldact->sa_flags & ~SA_RESETHAND));
119       oldact_sigaction_pointer = sigaction_pointer_get (oldact);
120       if (*oldact_sigaction_pointer == handler)
121         *oldact_sigaction_pointer = *(sigaction_pointer_get (&act_app[signum]));
122       oldact->sa_flags = act_app_saved.sa_flags;
123     }
124   return retval;
125 }
126
127 static void sigaction_orig_patch (void)
128 {
129   unsigned long page_start, page_end;
130   sigaction_t sigaction_orig_libc;
131
132   sigaction_orig_libc = sigaction_orig;
133
134   page_start = (unsigned long) sigaction_orig_libc & PAGE_MASK;
135   page_end = ((unsigned long) sigaction_orig_libc + trampoline_size
136               + PAGE_SIZE - 1) & PAGE_MASK;
137   maps_read ();
138   if (maps_verify ((void *) page_start, (void *) page_end))
139     {
140       fprintf (stderr, "libobjid: Invalid \"%s\" (0x%lx, %u)\n",
141                MAPS_FILENAME, page_start, (unsigned) (page_end - page_start));
142       return;
143     }
144   maps_static_setup ((void *) page_start, (void *) page_end);
145   if (mprotect ((void *) page_start, page_end - page_start,
146                 PROT_READ | PROT_WRITE | PROT_EXEC))
147     {
148       fprintf (stderr, "libobjid: mprotect (0x%lx, %u): %m\n", page_start,
149                (unsigned) (page_end - page_start));
150       return;
151     }
152   if (memcmp (sigaction_orig_libc, sigaction_trampoline, trampoline_size) != 0)
153     {
154       fprintf (stderr, "libobjid: libc prologue (%p) is not matching\n",
155                sigaction_orig_libc);
156       return;
157     }
158   /* Target jump from our trampoline.  */
159   sigaction_orig_libc_cont = (sigaction_t) ((char *) sigaction_orig_libc
160                                             + trampoline_size);
161   /* Jump instruction to our `sigaction_subst'.  */
162   memcpy (sigaction_orig_libc, sigaction_trampoline_jmpdir,
163           trampoline_size);
164   /* Long direct jump operand is relative on i386 but absolute on x86_64.  */
165 #define REBASE(ptr) ((char *) (ptr) - (char *) sigaction_trampoline_jmpdir \
166                                     + (char *) sigaction_orig_libc)
167   *(void **) REBASE (sigaction_trampoline_jmpdir_vec) = (void *)
168     ((char *) sigaction_subst - ((void *) sigaction_trampoline_jmpdir_base
169                                  == (void *) sigaction_trampoline ? NULL
170                                  : REBASE (sigaction_trampoline_jmpdir_base)));
171 #undef REBASE
172
173   /* Returning through our trampoline code back to the original function.  */
174   sigaction_orig = sigaction_trampoline;
175 }
176
177 static void signal_init (int signum)
178 {
179   sighandler_t handler_orig;
180
181   handler_orig = signal (signum, SIG_DFL);
182   if (handler_orig != SIG_DFL)
183     fprintf (stderr, "libobjid: signal_init: %d: %p\n", signum, handler_orig);
184 }
185
186 static void *libc_handle;
187
188 static void libobjid_init (void) __attribute__((__constructor__));
189 static void libobjid_init (void)
190 {
191   char *debug_str;
192
193   debug_str = getenv ("LIBOBJID");
194   if (debug_str != NULL)
195     debug = atoi (debug_str);
196
197   if (libc_handle == NULL)
198     {
199       libc_handle = dlopen (NULL, RTLD_LAZY);   /* RTLD_LAZY is mandatory */
200       if (libc_handle == NULL)
201         fprintf (stderr, "libobjid: dlopen (NULL): %s\n", dlerror ());
202     }
203   if (libc_handle == NULL)
204     return;
205
206 #define SIGACTION_NAME "sigaction"
207   sigaction_orig = (sigaction_t) dlsym (libc_handle, SIGACTION_NAME);
208   if (sigaction_orig == NULL)
209     fprintf (stderr, "libobjid: dlsym (\"%s\"): %s\n", SIGACTION_NAME,
210              dlerror ());
211 #undef SIGACTION_NAME
212   if (sigaction_orig == NULL)
213     return;
214
215   sigaction_orig_patch ();
216
217 #define SIGNAL_CORE(name) signal_init (name);
218 #include "signal-core.h"
219 #undef SIGNAL_CORE
220 }
221
222 static void libobjid_fini (void) __attribute__((__constructor__));
223 static void libobjid_fini (void)
224 {
225   if (libc_handle != NULL && dlclose (libc_handle))
226     fprintf (stderr, "libobjid: dlclose (): %s\n", dlerror ());
227   libc_handle = NULL;
228 }