BPatch functions that block are now locked (on a finer grain than the rest of the...
[dyninst.git] / dyninstAPI_RT / src / RTsolaris.c
1 /*
2  * Copyright (c) 1996-2004 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 /************************************************************************
43  * $Id: RTsolaris.c,v 1.19 2005/02/25 07:04:48 jaw Exp $
44  * RTsolaris.c: mutatee-side library function specific to Solaris
45  ************************************************************************/
46
47 #include "dyninstAPI_RT/h/dyninstAPI_RT.h"
48 #if !defined (EXPORT_SPINLOCKS_AS_HEADER)
49 /* everything should be under this flag except for the assembly code
50    that handles the runtime spinlocks  -- this is imported into the
51    test suite for direct testing */
52
53
54 #include <signal.h>
55 #include <sys/ucontext.h>
56 #include <assert.h>
57 #include <stdio.h>
58 #include <dlfcn.h>
59
60 #include <sys/procfs.h> /* /proc PIOCUSAGE */
61 #include <fcntl.h> /* O_RDONLY */
62 #include <unistd.h> /* getpid() */
63
64
65 #ifdef i386_unknown_solaris2_5
66 void DYNINSTtrapHandler(int sig, siginfo_t *info, ucontext_t *uap);
67
68 extern struct sigaction DYNINSTactTrap;
69 extern struct sigaction DYNINSTactTrapApp;
70 #endif
71
72 /************************************************************************
73  * void DYNINSTos_init(void)
74  *
75  * OS initialization function
76 ************************************************************************/
77
78 extern void DYNINSTheap_setbounds();  /* RTheap-solaris.c */
79
80 void
81 DYNINSTos_init(int calledByFork, int calledByAttach)
82 {
83     RTprintf("DYNINSTos_init(%d,%d)\n", calledByFork, calledByAttach);
84 #ifdef i386_unknown_solaris2_5
85     /*
86        Install trap handler.
87        This is currently being used only on the x86 platform.
88     */
89     DYNINSTactTrap.sa_handler = DYNINSTtrapHandler;
90     DYNINSTactTrap.sa_flags = 0;
91     sigfillset(&DYNINSTactTrap.sa_mask);
92     if (sigaction(SIGTRAP, &DYNINSTactTrap, &DYNINSTactTrapApp) != 0) {
93         perror("sigaction(SIGTRAP) install");
94         assert(0);
95         abort();
96     }
97
98     RTprintf("DYNINSTtrapHandler installed @ 0x%08X\n", DYNINSTactTrap.sa_handler);
99
100     if (DYNINSTactTrapApp.sa_flags&SA_SIGINFO) {
101         if (DYNINSTactTrapApp.sa_sigaction != NULL) {
102             RTprintf("App's TRAP sigaction @ 0x%08X displaced!\n",
103                    DYNINSTactTrapApp.sa_sigaction);
104         }
105     } else {
106         if (DYNINSTactTrapApp.sa_handler != NULL) {
107             RTprintf("App's TRAP handler @ 0x%08X displaced!\n",
108                    DYNINSTactTrapApp.sa_handler);
109         }
110     }
111 #endif
112
113     DYNINSTheap_setbounds();
114     /* uncomment this if you want instrumentation written out in core files */
115     /* setmemwrite(); */
116 }
117
118
119 \f
120
121
122 /****************************************************************************
123    The trap handler. Currently being used only on x86 platform.
124
125    Traps are used when we can't insert a jump at a point. The trap
126    handler looks up the address of the base tramp for the point that
127    uses the trap, and set the pc to this base tramp.
128    The paradynd is responsible for updating the tramp table when it
129    inserts instrumentation.
130 *****************************************************************************/
131
132 #ifdef i386_unknown_solaris2_5
133 trampTableEntry DYNINSTtrampTable[TRAMPTABLESZ];
134 /*static unsigned*/ int DYNINSTtotalTraps = 0;
135 /* native CC type/name decorations aren't handled yet by dyninst */
136
137 static unsigned lookup(unsigned key) {
138     unsigned u;
139     unsigned k;
140     for (u = HASH1(key); 1; u = (u + HASH2(key)) % TRAMPTABLESZ) {
141       k = DYNINSTtrampTable[u].key;
142       if (k == 0)
143         return 0;
144       else if (k == key)
145         return DYNINSTtrampTable[u].val;
146     }
147     /* not reached */
148 }
149
150 void DYNINSTtrapHandler(int sig, siginfo_t *info, ucontext_t *uap) {
151     unsigned pc = uap->uc_mcontext.gregs[PC];
152     unsigned nextpc;
153
154     /* If we're in the process of running an inferior RPC, we'll
155        ignore the trap here and have the daemon rerun the trap
156        instruction when the inferior rpc is done.  Because the default
157        behavior is for the daemon to reset the PC to it's previous
158        value and the PC is still at the trap instruction, we don't
159        need to make any additional adjustments to the PC in the
160        daemon.
161
162        This is used only on x86 platforms, so if multithreading is
163        ever extended to x86 platforms, then perhaps this would need to
164        be modified for that.  */
165
166     if(curRPC.runningInferiorRPC == 1) {
167       /* If the current PC is somewhere in the RPC then it's a trap that
168          occurred just before the RPC and is just now getting delivered.
169          That is we want to ignore it here and regenerate it later. */
170       if(curRPC.begRPCAddr <= pc && pc <= curRPC.endRPCAddr) {
171       /* If a previous trap didn't get handled on this next irpc (assumes one 
172          trap per irpc) then we have a bug, a trap didn't get regenerated */
173         /* printf("trapHandler, begRPCAddr: %x, pc: %x, endRPCAddr: %x\n",
174            curRPC.begRPCAddr, pc, curRPC.endRPCAddr);
175         */
176         assert(trapNotHandled==0);
177         trapNotHandled = 1; 
178         return;
179       }
180       else  ;   /* a trap occurred as a result of a function call within the */ 
181                 /* irpc, these traps we want to handle */
182     }
183     else { /* not in an irpc */
184       if(trapNotHandled == 1) {
185         /* Ok good, the trap got regenerated.
186            Check to make sure that this trap is the one corresponding to the one
187            that needs to get regenerated.
188         */
189         assert(pcAtLastIRPC == pc);
190         trapNotHandled = 0;
191         /* we'll then continue to process the trap */
192       }
193     }
194     nextpc = lookup(pc);
195
196     if (!nextpc) {
197       /* kludge: maybe the PC was not automatically adjusted after the trap */
198       /* this happens for a forked process */
199       pc--;
200       nextpc = lookup(pc);
201     }
202
203     if (nextpc) {
204       RTprintf("DYNINST trap [%d] 0x%08X -> 0x%08X\n",
205                DYNINSTtotalTraps, pc, nextpc);
206       uap->uc_mcontext.gregs[PC] = nextpc;
207     } else {
208       if ((DYNINSTactTrapApp.sa_flags&SA_SIGINFO)) {
209           if (DYNINSTactTrapApp.sa_sigaction != NULL) {
210               void (*handler)(int,siginfo_t*,ucontext_t*) =
211                   (void(*)(int,siginfo_t*,ucontext_t*))DYNINSTactTrapApp.sa_sigaction;
212               RTprintf("DYNINST trap [%d] 0x%08X DEFERED to 0x%08X!\n",
213                        DYNINSTtotalTraps, pc, DYNINSTactTrapApp.sa_sigaction);
214               sigprocmask(SIG_SETMASK, &DYNINSTactTrapApp.sa_mask, NULL);
215               (*handler)(sig,info,uap);
216               sigprocmask(SIG_SETMASK, &DYNINSTactTrap.sa_mask, NULL);
217           } else {
218               printf("DYNINST trap [%d] 0x%08X missing SA_SIGACTION!\n",
219                     DYNINSTtotalTraps, pc);
220               abort();
221           }
222       } else {
223           if (DYNINSTactTrapApp.sa_handler != NULL) {
224               void (*handler)(int,siginfo_t*,ucontext_t*) =
225                   (void(*)(int,siginfo_t*,ucontext_t*))DYNINSTactTrapApp.sa_sigaction;
226               RTprintf("DYNINST trap [%d] 0x%08X DEFERED to 0x%08X!\n",
227                     DYNINSTtotalTraps, pc, DYNINSTactTrapApp.sa_sigaction);
228               sigprocmask(SIG_SETMASK, &DYNINSTactTrapApp.sa_mask, NULL);
229               (*handler)(sig,info,uap);
230               sigprocmask(SIG_SETMASK, &DYNINSTactTrap.sa_mask, NULL);
231           } else {
232               printf("DYNINST trap [%d] 0x%08X missing SA_HANDLER!\n",
233                     DYNINSTtotalTraps, pc);
234               abort();
235           }
236       }
237     }
238     DYNINSTtotalTraps++;
239 }
240
241 #endif
242
243 char gLoadLibraryErrorString[ERROR_STRING_LENGTH];
244 int DYNINSTloadLibrary(char *libname)
245 {
246   void *res;
247   char *err_str;
248   gLoadLibraryErrorString[0]='\0';
249   
250   if (NULL == (res = dlopen(libname, RTLD_NOW | RTLD_GLOBAL))) {
251     /* An error has occurred */
252     perror( "DYNINSTloadLibrary -- dlopen" );
253     
254     if (NULL != (err_str = dlerror()))
255       strncpy(gLoadLibraryErrorString, err_str, ERROR_STRING_LENGTH);
256     else 
257       sprintf(gLoadLibraryErrorString,"unknown error with dlopen");
258     
259     fprintf(stderr, "%s[%d]: %s\n",__FILE__,__LINE__,gLoadLibraryErrorString);
260     return 0;  
261   } else
262     return 1;
263 }
264
265 /*
266 int DYNINSTloadLibrary(char *libname)
267 {
268     if (dlopen(libname, RTLD_NOW | RTLD_GLOBAL) != NULL)
269         return 1;
270     else
271         return 0;
272 }
273 */
274
275
276 /*
277 We can get Solaris to put instrumented code in the core file of dumped
278 mutatees by setting setting WRITE protection on all pages in the
279 process (SHARED text pages cannot have WRITE protect set).
280
281 To use, compile and link this code with the runtime library, and call
282 setmemwrite from DYNINSTinit.
283 */
284
285 /* Set every page in this process to be writable to
286    cause pages with instrumented code to be saved in core dumps. */
287 #include <sys/types.h>
288 #include <sys/mman.h>
289 #include <sys/procfs.h>
290 #define maxpmap 512
291 static
292 int setmemwrite()
293 {
294     int pfd, numpmap, i;
295     prmap_t pmap[maxpmap];
296
297     char buf[32];
298     sprintf(buf, "/proc/%05d", getpid());
299     pfd = open(buf, O_RDONLY);
300     if (0 > pfd) {
301          perror("open (in setmemwrite)");
302          fprintf(stderr, "Can't open /proc on myself\n");
303          exit(1);
304     }
305     if (0 > ioctl(pfd, PIOCNMAP, &numpmap)) {
306          perror("PIOCNMAP (in setmemwrite)");
307          exit(1);
308     }
309     if (numpmap + 1 > maxpmap) {
310          fprintf(stderr, "Too many memory mappings\n");
311          exit(1);
312     }
313     if (0 > ioctl(pfd, PIOCMAP, pmap)) {
314          perror("PIOCMAP (in setmemwrite)");
315          exit(1);
316     }
317     for (i = 0; i < numpmap; i++) {
318          prmap_t *p = &pmap[i];
319          /* Enable WRITE if this region does not have it already and
320             we won't get in trouble for setting it (i.e., it is not
321             SHARED). */
322          if (~p->pr_mflags & MA_WRITE
323              && ~p->pr_mflags & MA_SHARED)
324               if (0 > mprotect(p->pr_vaddr, p->pr_size,
325                                PROT_WRITE
326                                | PROT_READ
327                                | (p->pr_mflags & MA_EXEC ? PROT_EXEC : 0))) {
328                    perror("mprotect (in setmemwrite)");
329                    fprintf(stderr, "mprotect (it %d) args: %#010x, %x, %x\n",
330                            i,
331                            p->pr_vaddr, p->pr_size, 
332                            PROT_WRITE
333                            | PROT_READ
334                            | (p->pr_mflags & MA_EXEC ? PROT_EXEC : 0));
335                    exit(1);
336               }
337     }
338     close(pfd);
339     return 0;
340 }
341 #endif /* EXPORT SPINLOCKS */
342 void DYNINSTlock_spinlock(dyninst_spinlock *mut)
343 {
344
345 #if (os_solaris == 9)
346   /*  This might only work for solaris 2.9 since it uses cas. */
347   /*  The command -Av9 needs to be sent to the solaris assembler */
348   /*  also works with -Av8plus, and analagous solaris cc command option */
349 #if defined __GNUC__
350  asm (
351      " 1:                       \n"
352      " mov      1, %%g2         \n" /* l0 <- 1 */
353      " mov      0, %%g0         \n" /* g0 <- 0 */
354      " cas      [%0],%%g0,%%g2  \n" /* if (lock == g0) lock = %l0, atomic compare/swap */
355      " brz,pn   %%g2,out        \n" /* if (l0 == 0) after the swap, it worked, goto done */
356
357      " 2:                       \n" /* if not, spin until lock is not set anymore */
358      " ld       [%0], %%g2      \n" /* l0 <- mut->lock */
359      " brnz,pt  %%g2,2b         \n" /* if (lock != 0) spin*/
360      " b,a      1b              \n" /* else try to obtain lock again (goto 1) */
361
362      " out:                     \n" /* last step, memory barrier */
363      " membar   #LoadLoad | #LoadStore | #StoreLoad | #StoreStore\n"
364      : :  "r" (mut) :  "g0", "g2", "memory");
365 #else
366  asm (
367      " 1:                       \n"
368      " mov      1, %l2          \n" /* l0 <- 1 */
369      " mov      0, %l0          \n" /* g0 <- 0 */
370      " cas      [%i0],%l0,%l2   \n" /* if (lock == g0) lock = %l0, atomic compare/swap */
371      " cmp      %l2,0           \n" /* */
372      " be       out             \n"  /* else, got lock, done*/
373
374      " 2:                       \n" /* if not, spin until lock is not set anymore */
375      " ld       [%i0], %l2      \n" /* l0 <- mut->lock */
376      " cmp      %l2,0           \n" /* */
377      " bne      2b              \n"  /* else, got lock, done*/
378      " b,a      1b              \n" /* else try to obtain lock again (goto 1) */
379
380      " out:                     \n" /* last step, memory barrier */
381      " membar   #LoadLoad | #LoadStore | #StoreLoad | #StoreStore\n"
382      );
383
384 #endif
385
386 #else /* solaris 8 */
387
388 #if defined __GNUC__
389
390  asm (
391      " 1:                       \n"
392      " ldstub   [%0],%%g3       \n" /* get old lock value, store lock val of all ones */
393      " cmp      %%g3,0          \n" /* if (old lock != 0), someone has lock, spin*/
394      " be       out             \n"  /* else, got lock, done*/
395      " nop                      \n"
396
397      " 2:                       \n"  /* if not, spin until lock is not set anymore */
398      " ld       [%0], %%g3      \n" /* l0 <- mut->lock */
399      " cmp      %%g3,0          \n"  /* if (old lock != 0), someone has lock, spin*/
400
401      " bne      2b              \n" /* if (lock != 0) spin*/
402      " nop                      \n"
403      " b,a      1b              \n"  /* else try to obtain lock again (goto 1) */
404      " nop                      \n"
405
406      " out:                     \n" /* last step, memory barrier */
407      : : "r" (mut) : "g3", "memory");
408
409 #else /* sun cc */
410
411  asm (
412      " 1:                       \n"
413      " ldstub   [%i0],%g3       \n" /* get old lock value, store lock val of all ones */
414      " cmp      %g3,0           \n" /* if (old lock != 0), someone has lock, spin*/
415      " be       out             \n"  /* else, got lock, done*/
416      " nop                      \n"
417
418      " 2:                       \n"  /* if not, spin until lock is not set anymore */
419      " ld       [%i0], %g3      \n" /* l0 <- mut->lock */
420      " cmp      %g3,0           \n"  /* if (old lock != 0), someone has lock, spin*/
421
422      " bne      2b              \n" /* if (lock != 0) spin*/
423      " nop                      \n"
424      " b,a      1b              \n"  /* else try to obtain lock again (goto 1) */
425      " nop                      \n"
426
427      " out:                     \n" /* last step, memory barrier */
428      );
429
430 #endif /* gnuc vs suncc switch */
431
432 #endif /* solaris 8-9 switch */
433
434 }
435