BPatch functions that block are now locked (on a finer grain than the rest of the...
[dyninst.git] / dyninstAPI_RT / src / RTlinux.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: RTlinux.c,v 1.28 2005/02/25 07:04:47 jaw Exp $
44  * RTlinux.c: mutatee-side library function specific to Linux
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 #include <signal.h>
54 #include <assert.h>
55 #include <stdio.h>
56 #include <dlfcn.h>
57 #include <link.h>
58 #include <errno.h>
59 #include <unistd.h>
60
61 #include <sys/ptrace.h>
62
63
64 #ifndef ia64_unknown_linux2_4
65 extern struct sigaction DYNINSTactTrap;
66 extern struct sigaction DYNINSTactTrapApp;
67 #else
68 #include <errno.h>
69 #include <sys/mman.h>
70
71 extern double DYNINSTstaticHeap_32K_lowmemHeap_1[];
72 extern double DYNINSTstaticHeap_4M_anyHeap_1[];
73
74 void _start( void ) {
75         /* fprintf( stderr, "*** Initializing dyninstAPI runtime.\n" ); */
76
77         /* Grab the page size, to align the heap pointer. */
78         long int pageSize = sysconf( _SC_PAGESIZE );
79         if( pageSize == 0 || pageSize == - 1 ) {
80                 fprintf( stderr, "*** Failed to obtain page size, guessing 16K.\n" );
81                 perror( "_start" );
82                 pageSize = 1024 * 1024 * 16;
83                 } /* end pageSize initialization */
84
85         /* Align the heap pointer. */
86         unsigned long int alignedHeapPointer = (unsigned long int)DYNINSTstaticHeap_4M_anyHeap_1;
87         unsigned long long adjustedSize = alignedHeapPointer + 4 * 1024 * 1024;
88         alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
89         adjustedSize -= alignedHeapPointer;
90
91         /* Make the heap's page executable. */
92         if( mprotect( (void *)alignedHeapPointer, adjustedSize, PROT_READ | PROT_WRITE | PROT_EXEC ) != 0 ) {
93                 fprintf( stderr, "*** Unable to mark DYNINSTstaticHeap_4M_anyHeap_1 executable!\n" );
94                 perror( "_start" );
95                 }
96         /* fprintf( stderr, "*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer, alignedHeapPointer + adjustedSize ); */
97
98         /* Mark _both_ heaps executable. */
99         alignedHeapPointer = (unsigned long int)DYNINSTstaticHeap_32K_lowmemHeap_1;
100         adjustedSize = alignedHeapPointer + 32 * 1024;
101         alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
102         adjustedSize -= alignedHeapPointer;
103
104         /* Make the heap's page executable. */
105         if( mprotect( (void *)alignedHeapPointer, adjustedSize, PROT_READ | PROT_WRITE | PROT_EXEC ) != 0 ) {
106                 fprintf( stderr, "*** Unable to mark DYNINSTstaticHeap_4M_anyHeap_1 executable!\n" );
107                 perror( "_start" );
108                 }
109         /* fprintf( stderr, "*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer, alignedHeapPointer + adjustedSize ); */
110         }
111
112 /* Ensure we an executable block of memory. */
113 void R_BRK_TARGET() {
114         /* Make sure we've got room for two bundles. */
115         asm( "nop 0" ); asm( "nop 0" ); asm( "nop 0" );
116         asm( "nop 0" ); asm( "nop 0" ); asm( "nop 0" );
117         }
118 #endif
119
120 /************************************************************************
121  * void DYNINSTos_init(void)
122  *
123  * OS initialization function
124 ************************************************************************/
125
126 /* this handler's sigcontext arg is an undocumented feature of Linux    */
127 /* (requires the non-siginfo handler to be installed by sigaction.)     */
128 void DYNINSTtrapHandler(int sig, struct sigcontext uap);
129
130 void
131 DYNINSTos_init(int calledByFork, int calledByAttach)
132 {
133     RTprintf("DYNINSTos_init(%d,%d)\n", calledByFork, calledByAttach);
134
135     /*
136        Install trap handler.  Currently being used only on x86 platforms.
137     */
138     
139 #ifndef ia64_unknown_linux2_4
140     DYNINSTactTrap.sa_handler = (void(*)(int))DYNINSTtrapHandler;
141     DYNINSTactTrap.sa_flags = 0;
142     sigfillset(&DYNINSTactTrap.sa_mask);
143     if (sigaction(SIGTRAP, &DYNINSTactTrap, &DYNINSTactTrapApp) != 0) {
144         perror("sigaction(SIGTRAP) install");
145         assert(0);
146         abort();
147     }
148     
149     RTprintf("DYNINSTtrapHandler installed @ 0x%08X\n", DYNINSTactTrap.sa_handler);
150
151     if (DYNINSTactTrapApp.sa_flags&SA_SIGINFO) {
152         if (DYNINSTactTrapApp.sa_sigaction != NULL) {
153             RTprintf("App's TRAP sigaction @ 0x%08X displaced!\n",
154                    DYNINSTactTrapApp.sa_sigaction);
155         }
156     } else {
157         if (DYNINSTactTrapApp.sa_handler != NULL) {
158             RTprintf("App's TRAP handler @ 0x%08X displaced!\n",
159                    DYNINSTactTrapApp.sa_handler);
160         }
161     }
162 #endif /* !ia64_unknown_linux2_4 */
163
164     ptrace(PTRACE_TRACEME, 0, 0, 0);
165 }
166
167 /****************************************************************************
168    The trap handler. Currently being used only on x86 platform.
169
170    Traps are used when we can't insert a jump at a point. The trap
171    handler looks up the address of the base tramp for the point that
172    uses the trap, and set the pc to this base tramp.
173    The paradynd is responsible for updating the tramp table when it
174    inserts instrumentation.
175 *****************************************************************************/
176
177 #ifndef ia64_unknown_linux2_4
178
179 #ifndef IP_REG
180 #if defined(__x86_64__) && __WORDSIZE == 64
181 #define IP_REG rip      // 64-bit x86 ip
182 #else
183 #define IP_REG eip
184 #endif
185 #endif
186
187 trampTableEntry DYNINSTtrampTable[TRAMPTABLESZ];
188 unsigned DYNINSTtotalTraps = 0;
189
190 static unsigned lookup(unsigned key) {
191     unsigned u;
192     unsigned k;
193     for (u = HASH1(key); 1; u = (u + HASH2(key)) % TRAMPTABLESZ) {
194       k = DYNINSTtrampTable[u].key;
195       if (k == 0)
196         return 0;
197       else if (k == key)
198         return DYNINSTtrampTable[u].val;
199     }
200     /* not reached */
201     assert(0);
202     abort();
203 }
204
205 void DYNINSTtrapHandler(int sig, struct sigcontext uap) {
206     unsigned pc = uap.IP_REG;
207     unsigned nextpc;
208
209     /* If we're in the process of running an inferior RPC, we'll
210        ignore the trap here and have the daemon rerun the trap
211        instruction when the inferior rpc is done.  Because the default
212        behavior is for the daemon to reset the PC to it's previous
213        value and the PC is still at the trap instruction, we don't
214        need to make any additional adjustments to the PC in the
215        daemon.
216
217        This is used only on x86 platforms, so if multithreading is
218        ever extended to x86 platforms, then perhaps this would need to
219        be modified for that.
220
221        I haven't seen the irpc trap bug with linux version.  This is
222        probably because on linux we have the application's traps sent
223        to the daemon and forwarded back to the application.  However,
224        if trap signals are ever changed to be handled locally by the
225        application, we'll be ready for it.  */
226
227     if(curRPC.runningInferiorRPC == 1) {
228       /* If the current PC is somewhere in the RPC then it's a trap that
229          occurred just before the RPC and is just now getting delivered.
230          That is we want to ignore it here and regenerate it later. */
231       if(curRPC.begRPCAddr <= pc && pc <= curRPC.endRPCAddr) {
232       /* If a previous trap didn't get handled on this next irpc (assumes one 
233          trap per irpc) then we have a bug, a trap didn't get regenerated */
234         /* printf("trapHandler, begRPCAddr: %x, pc: %x, endRPCAddr: %x\n",
235            curRPC.begRPCAddr, pc, curRPC.endRPCAddr);
236         */
237         assert(trapNotHandled==0);
238         trapNotHandled = 1; 
239         return;
240       }
241       else  ;   /* a trap occurred as a result of a function call within the */ 
242                 /* irpc, these traps we want to handle */
243     }
244     else { /* not in an irpc */
245       if(trapNotHandled == 1) {
246         /* Ok good, the trap got regenerated.
247            Check to make sure that this trap is the one corresponding to the 
248            one that needs to get regenerated.
249         */
250         assert(pcAtLastIRPC == pc);
251         trapNotHandled = 0;
252         /* we'll then continue to process the trap */
253       }
254     }
255     nextpc = lookup(--pc);
256
257     if (!nextpc) {
258       /* kludge: the PC may have been at or right after the trap */
259       pc++;
260       nextpc = lookup(pc);
261     }
262
263     if (nextpc) {
264       RTprintf("DYNINST trap [%d] 0x%08X -> 0x%08X\n",
265                DYNINSTtotalTraps, pc, nextpc);
266       uap.IP_REG = nextpc;
267     } else {
268       if ((DYNINSTactTrapApp.sa_flags&SA_SIGINFO)) {
269         if (DYNINSTactTrapApp.sa_sigaction != NULL) {
270           siginfo_t info; /* dummy */
271           void (*handler)(int,siginfo_t*,void*) =
272                 (void(*)(int,siginfo_t*,void*))DYNINSTactTrapApp.sa_sigaction;
273           RTprintf("DYNINST trap [%d] 0x%08X DEFERED to A0x%08X!\n",
274                 DYNINSTtotalTraps, pc, DYNINSTactTrapApp.sa_sigaction);
275           memset(&info,0,sizeof(info));
276           sigprocmask(SIG_SETMASK, &DYNINSTactTrapApp.sa_mask, NULL);
277           (*handler)(sig,&info,NULL);
278           sigprocmask(SIG_SETMASK, &DYNINSTactTrap.sa_mask, NULL);
279         } else {
280           printf("DYNINST trap [%d] 0x%08X missing SA_SIGACTION!\n",
281                   DYNINSTtotalTraps, pc);
282           abort();
283         }
284       } else {
285         if (DYNINSTactTrapApp.sa_handler != NULL) {
286           void (*handler)(int,struct sigcontext) =
287               (void(*)(int,struct sigcontext))DYNINSTactTrapApp.sa_handler;
288           RTprintf("DYNINST trap [%d] 0x%08X DEFERED to H0x%08X!\n",
289                 DYNINSTtotalTraps, pc, DYNINSTactTrapApp.sa_handler);
290           sigprocmask(SIG_SETMASK, &DYNINSTactTrapApp.sa_mask, NULL);
291           (*handler)(sig,uap);
292           sigprocmask(SIG_SETMASK, &DYNINSTactTrap.sa_mask, NULL);
293         } else {
294           printf("DYNINST trap [%d] 0x%08X missing SA_HANDLER!\n",
295                   DYNINSTtotalTraps, pc);
296           abort();
297         }
298       }
299     }
300     DYNINSTtotalTraps++;
301 }
302 #endif /* !ia64_unknown_linux2_4 */
303
304 void *DYNINSTdlopen_fake_ret(const char *filename, int flag,
305                              const char *fake_ret);
306
307 char gLoadLibraryErrorString[ERROR_STRING_LENGTH];
308 int DYNINSTloadLibrary(char *libname)
309 {
310   void *res;
311   char *err_str;
312   gLoadLibraryErrorString[0]='\0';
313
314   if (NULL == (res = dlopen(libname, RTLD_NOW | RTLD_GLOBAL))) {
315     // An error has occurred
316     if (NULL != (err_str = dlerror()))
317       strncpy(gLoadLibraryErrorString, err_str, ERROR_STRING_LENGTH);
318     else 
319       sprintf(gLoadLibraryErrorString,"unknown error with dlopen");
320 #ifdef i386_unknown_linux2_0
321     if (strstr(gLoadLibraryErrorString, "invalid caller") != NULL) {
322         /* dlopen on Suse 9.1 has a "security" check in it so that
323            only registered modules can call it. We fool this check
324            around by calling _dl_open and pretending it was called
325            from libc. */
326         unsigned char *fake_ret;
327         int i, scan_bytes = (intptr_t)&getuid - (intptr_t)&getpid;
328
329         /* Scan from getpid to getuid (hopefully in libc) looking for
330            a ret instruction (or a byte in another instruction that
331            just looks like a ret). We will pretend that _dl_open was
332            called from that address. */
333         if (scan_bytes >= 0) {
334             fake_ret = (unsigned char *)&getpid;
335         }
336         else {
337             fake_ret = (unsigned char *)&getuid;
338             scan_bytes = -scan_bytes;
339         }
340         for (i=0; i<scan_bytes; i++) {
341             if (fake_ret[i] == 0xC3) { /* ret instruction */
342                 DYNINSTdlopen_fake_ret(libname, RTLD_NOW | RTLD_GLOBAL,
343                                        fake_ret);
344                 /* If an error happens in _dl_open, we would never
345                    return here -- _dl_open would abort the process. It
346                    is unfortunate, but creating a proper context so
347                    that _dl_open can tolerate errors is a harder problem */
348                 return 1;
349             }
350         }
351         strcpy(gLoadLibraryErrorString, "No suitable ret instruction found");
352         return 0;
353     }
354 #endif
355     perror( "DYNINSTloadLibrary -- dlopen" );
356     //fprintf(stderr, "%s[%d]: %s\n",__FILE__,__LINE__,gLoadLibraryErrorString);
357     return 0;  
358   } else
359     return 1;
360   
361   /*
362    * All of this is necessary because on linux, dlopen is not in libc, but
363    * in a separate library libdl.  Not all programs are linked with libdl,
364    * but libc does contain the underlying functions.  This is gross and
365    * may break with new versions of glibc.  It is based on glibc 2.0.6
366    */
367   /*
368     struct link_map *new;
369     char *errstr;
370     int err;
371     
372     void doit (void) {
373     new = _dl_open( libname ?: "", RTLD_NOW | RTLD_GLOBAL );
374     }
375     
376     err = _dl_catch_error( &errstr, doit );
377     
378     if( errstr == NULL )
379     return 1;
380     else {
381     fprintf( stderr, errstr );
382     free( errstr );
383     return 0;
384     }
385   */
386 }
387 #endif /* EXPORT SPINLOCKS */
388
389 void DYNINSTlock_spinlock(dyninst_spinlock *mut)
390 {
391 #if defined (arch_x86) || (defined(arch_x86_64) && __WORDSIZE == 32)
392   /*  same assembly as for x86 windows, just different format for asm stmt */
393   /*  so if you change one, make the same changes in the other, please */
394
395  asm (
396          "  .Loop: \n"
397          "  movl        8(%ebp), %ecx  # &mut in ecx \n"
398          "  movl        $0, %eax       # 0 (unlocked) in eax\n"
399          "  movl        $1, %edx       # 1 (locked) in edx \n"
400          "  lock  \n"
401          "  cmpxchgl    %edx, (%ecx)   # try to atomically store edx (1 = locked) \n"
402          "                             # only if we are unlocked (ecx == eax) \n"
403          "  jnz         .Loop          # if failure, zero flag set, spin again. \n"
404      );
405
406 #elif defined(arch_x86_64) && __WORDSIZE == 64
407   /*  same assembly as for x86 windows, just different format for asm stmt */
408   /*  so if you change one, make the same changes in the other, please */
409
410  asm (
411          "  .Loop: \n"
412          "  movq        8(%rbp), %rcx  # &mut in rcx \n"
413          "  movq        $0, %rax       # 0 (unlocked) in rax\n"
414          "  movq        $1, %rdx       # 1 (locked) in rdx \n"
415          "  lock  \n"
416          "  cmpxchgq    %rdx, (%rcx)   # try to atomically store rdx (1 = locked) \n"
417          "                             # only if we are unlocked (rcx == rax) \n"
418          "  jnz         .Loop          # if failure, zero flag set, spin again. \n"
419      );
420
421 #elif defined (arch_ia64)
422
423  asm (
424          "  1:                                      \n"
425          "  ld4                 r31=[%0]            \n" /* r31 <- lock value*/
426          " ;;                                       \n"
427          "  cmp.ne              p15,p0=r31,r0       \n" /* test lock set */
428          "  (p15) br.cond.sptk  1b                  \n" /* yes:  spin */
429          " ;;                                       \n" /* no: try to obtain lock */
430          "  mov                 r31=1               \n" /* r31 <- 1, desired lock val */
431          "  mov                 ar.ccv=0            \n" /* set value to compare to */
432          "                                          \n" /* unlocked lock ( = 0) */
433          " ;;                                       \n"
434          "  cmpxchg4.acq        r31=[%0],r31,ar.ccv \n" /* if (lock == ar.ccv) lock = r31 */
435          " ;;                                       \n"
436          "  cmp.ne              p15,p0=r31,r0       \n" /* test r31 to see if xchg worked */
437          "  (p15) br.cond.sptk  1b                  \n" /* if r31 != 0, lock failed, spin*/
438          " ;;                                       \n"
439          "  end:                                    \n" /* else, we got the lock, done */
440  ::"r"(mut):"ar.ccv", "p15", "p0", "r31","memory");
441
442
443 #else
444 #error
445 #endif
446 }