| Patches for mpatrol to support uClibc and MIPS full call stack tracing |
| by Dan Howell <dahowell@directv.com> |
| |
| diff -urN mpatrol/src/config.h mpatrol-uclibc/src/config.h |
| --- mpatrol/src/config.h 2006-04-27 15:58:21.000000000 -0700 |
| +++ mpatrol-uclibc/src/config.h 2006-05-05 20:32:58.000000000 -0700 |
| @@ -795,6 +795,10 @@ |
| */ |
| |
| #ifndef MP_INIT_SUPPORT |
| +/* Note that machine.c currently only implements MP_INIT_SUPPORT for |
| + * x86, 68k, 88k, and Sparc architechtures. */ |
| +#if ARCH == ARCH_IX86 || ARCH == ARCH_M68K || \ |
| + ARCH == ARCH_M88K || ARCH == ARCH_SPARC |
| #if SYSTEM == SYSTEM_DGUX || SYSTEM == SYSTEM_DRSNX || \ |
| SYSTEM == SYSTEM_DYNIX || SYSTEM == SYSTEM_LINUX || \ |
| SYSTEM == SYSTEM_SOLARIS || SYSTEM == SYSTEM_UNIXWARE |
| @@ -809,6 +813,9 @@ |
| #else /* SYSTEM */ |
| #define MP_INIT_SUPPORT 0 |
| #endif /* SYSTEM */ |
| +#else /* ARCH */ |
| +#define MP_INIT_SUPPORT 0 |
| +#endif |
| #endif /* MP_INIT_SUPPORT */ |
| |
| |
| diff -urN mpatrol/src/inter.c mpatrol-uclibc/src/inter.c |
| --- mpatrol/src/inter.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/inter.c 2006-05-17 18:02:04.000000000 -0700 |
| @@ -79,12 +79,24 @@ |
| |
| #if TARGET == TARGET_UNIX |
| #if SYSTEM == SYSTEM_LINUX |
| +#ifndef __UCLIBC__ |
| /* This contains a pointer to the environment variables for a process. If |
| * it is not set up yet then we must use sbrk() to allocate all memory since |
| * we can't initialise mpatrol until the environment variable can be read. |
| */ |
| |
| extern char **__environ; |
| +#else /* __UCLIBC__ */ |
| +/* In uClibc, the dynamic loader calls malloc() and related functions, |
| + * and sets __environ before these calls, so we can't use it to determine |
| + * if we can initialize mpatrol. Instead, we use __progname, which is set |
| + * in __uClibc_main just before before uClibc transfers control to the |
| + * application's main() function (and static constructors, if any). Before |
| + * this, we must use sbrk() to allocate memory. |
| + */ |
| + |
| +extern const char *__progname; |
| +#endif /* __UCLIBC__ */ |
| #elif SYSTEM == SYSTEM_TRU64 |
| /* The exception support library on Tru64 always allocates some memory from |
| * the heap in order to initialise the code address range tables. We need |
| @@ -118,7 +130,11 @@ |
| |
| #if TARGET == TARGET_UNIX |
| #if SYSTEM == SYSTEM_LINUX |
| +#ifndef __UCLIBC__ |
| #define crt_initialised() (__environ) |
| +#else /* __UCLIBC__ */ |
| +#define crt_initialised() (__progname) |
| +#endif /* __UCLIBC__ */ |
| #elif SYSTEM == SYSTEM_TRU64 |
| #define crt_initialised() (__exc_crd_list_head && init_flag) |
| #else /* SYSTEM */ |
| @@ -306,7 +322,7 @@ |
| alloctype t; |
| int c; |
| |
| - if (memhead.fini || (memhead.astack.size == 0)) |
| + if (memhead.fini || (memhead.astack.size == 0) || memhead.recur != 1) |
| return; |
| #if MP_FULLSTACK |
| /* Create the address nodes for the current call. This is not necessarily |
| @@ -1307,7 +1323,7 @@ |
| loginfo v; |
| int j; |
| |
| - if (!memhead.init || memhead.fini) |
| + if (!memhead.init || memhead.fini || memhead.recur != 0) |
| { |
| __mp_memset(p, c, l); |
| return p; |
| @@ -1371,7 +1387,7 @@ |
| loginfo v; |
| int j; |
| |
| - if (!memhead.init || memhead.fini) |
| + if (!memhead.init || memhead.fini || memhead.recur != 0) |
| if (f == AT_MEMCCPY) |
| { |
| if (r = __mp_memfind(p, l, &c, 1)) |
| diff -ur mpatrol/src/machine.c mpatrol-uclibc/src/machine.c |
| --- mpatrol/src/machine.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/machine.c 2006-06-07 15:11:20.000000000 -0700 |
| @@ -217,6 +217,19 @@ |
| .end __mp_stackpointer |
| |
| |
| +/* Obtain the frame pointer (s8) for the current function. |
| + */ |
| + |
| + .text |
| + .globl __mp_framepointer |
| + .ent __mp_framepointer |
| +__mp_framepointer: |
| + .frame $29,0,$31 |
| + move $2,$30 |
| + j $31 |
| + .end __mp_framepointer |
| + |
| + |
| /* Obtain the return address for the current function. |
| */ |
| |
| diff -urN mpatrol/src/memory.c mpatrol-uclibc/src/memory.c |
| --- mpatrol/src/memory.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/memory.c 2006-05-12 18:12:39.000000000 -0700 |
| @@ -47,7 +47,7 @@ |
| #endif /* SYSTEM */ |
| #include <setjmp.h> |
| #include <signal.h> |
| -#if MP_SIGINFO_SUPPORT |
| +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX |
| #include <siginfo.h> |
| #endif /* MP_SIGINFO_SUPPORT */ |
| #include <fcntl.h> |
| diff -urN mpatrol/src/signals.c mpatrol-uclibc/src/signals.c |
| --- mpatrol/src/signals.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/signals.c 2006-05-12 18:12:19.000000000 -0700 |
| @@ -36,7 +36,7 @@ |
| #include <stdlib.h> |
| #include <signal.h> |
| #if TARGET == TARGET_UNIX |
| -#if MP_SIGINFO_SUPPORT |
| +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX |
| #include <siginfo.h> |
| #endif /* MP_SIGINFO_SUPPORT */ |
| #elif TARGET == TARGET_WINDOWS |
| diff -urN mpatrol/src/stack.c mpatrol-uclibc/src/stack.c |
| --- mpatrol/src/stack.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/stack.c 2006-06-22 15:39:04.000000000 -0700 |
| @@ -48,7 +48,7 @@ |
| #else /* MP_LIBRARYSTACK_SUPPORT */ |
| #if TARGET == TARGET_UNIX |
| #include <setjmp.h> |
| -#if MP_SIGINFO_SUPPORT |
| +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX |
| #include <siginfo.h> |
| #endif /* MP_SIGINFO_SUPPORT */ |
| #if SYSTEM == SYSTEM_DRSNX || SYSTEM == SYSTEM_SOLARIS |
| @@ -58,6 +58,17 @@ |
| #define R_SP REG_SP |
| #endif /* R_SP */ |
| #endif /* ARCH */ |
| +#elif SYSTEM == SYSTEM_LINUX |
| +#if ARCH == ARCH_MIPS |
| +#include <linux/unistd.h> |
| +/* We need the ucontext defined in asm/ucontext.h, but sys/ucontext.h |
| + * has a conflicting definition of ucontext. So we'll trick the |
| + * preprocessor into letting the include file define a non-conflicting |
| + * name. */ |
| +#define ucontext asm_ucontext |
| +#include <asm/ucontext.h> |
| +#undef ucontext |
| +#endif /* ARCH */ |
| #endif /* SYSTEM */ |
| #endif /* TARGET */ |
| #endif /* MP_LIBRARYSTACK_SUPPORT */ |
| @@ -122,6 +133,15 @@ |
| #define SP_OFFSET 2 /* stack pointer offset has been set */ |
| #define SP_LOWER 4 /* lower part of stack pointer offset has been set */ |
| #define SP_UPPER 8 /* upper part of stack pointer offset has been set */ |
| +#define BR_UNCOND 16 /* unconditional branch needs to be taken */ |
| +#define BR_COND 32 /* conditional branch encountered */ |
| +#define RA_NOFRAME 64 /* no frame - return address is in ra register */ |
| +#define SP_IN_FP 128 /* stack pointer stored in frame pointer (s8) register */ |
| + |
| +#if SYSTEM == SYSTEM_LINUX |
| +#define RA_SIGTRAMP 1 /* return address is a signal trampoline */ |
| +#define RA_SIGRETURN 2 /* return address is in the signalled function */ |
| +#endif /* SYSTEM */ |
| #endif /* TARGET && ARCH */ |
| #endif /* MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT */ |
| |
| @@ -152,6 +172,13 @@ |
| #endif /* SYSTEM */ |
| #endif /* SYSTEM */ |
| #else /* MP_LIBRARYSTACK_SUPPORT */ |
| +/* On some systems, such as those using uClibc, the signal() function may |
| + * call memcpy() or other memory related functions, so we need to guard |
| + * against recursion. |
| + */ |
| + |
| +static unsigned char recursive; |
| + |
| static jmp_buf environment; |
| #if MP_SIGINFO_SUPPORT |
| static struct sigaction bushandler; |
| @@ -261,23 +288,41 @@ |
| int |
| unwind(frameinfo *f) |
| { |
| - long p, s; |
| - unsigned long a, i, q; |
| + long p, m, s; |
| + unsigned long a, i, q, t, b, r; |
| unsigned short l, u; |
| |
| s = -1; |
| - p = 0; |
| + p = m = 0; |
| q = 0xFFFFFFFF; |
| l = u = 0; |
| a = 0; |
| + t = b = 0; |
| /* Determine the current stack pointer and return address if we are |
| * initiating call stack traversal. |
| */ |
| if (f->ra == 0) |
| { |
| f->sp = __mp_stackpointer(); |
| + f->fp = __mp_framepointer(); |
| f->ra = __mp_returnaddress(); |
| } |
| +#if SYSTEM == SYSTEM_LINUX |
| + /* Handle signal frames. |
| + */ |
| + if (f->ra & RA_SIGRETURN) |
| + { |
| + /* in case of frameless function, get ra and sp from sigcontext */ |
| + p = ((struct sigcontext *) f->sp)->sc_regs[31]; |
| + f->fp = ((struct sigcontext *) f->sp)->sc_regs[30]; |
| + f->sp = ((struct sigcontext *) f->sp)->sc_regs[29]; |
| + a |= RA_NOFRAME; |
| + } |
| + f->ra &= ~3; |
| +#endif |
| + /* Save initial code-reading starting point. |
| + */ |
| + r = f->ra; |
| /* Search for the return address offset in the stack frame. |
| */ |
| while (!((a & RA_OFFSET) && (a & SP_OFFSET)) && (f->ra < q)) |
| @@ -294,6 +339,67 @@ |
| s = 0; |
| a |= SP_OFFSET; |
| } |
| + else if (i == 0x03C0E821) |
| + { |
| + /* move sp,s8 */ |
| + a |= SP_IN_FP; |
| + } |
| + else if ((i >> 28 == 0x1) || (i >> 26 == 0x01)) |
| + { |
| + /* branch */ |
| + t = f->ra + ((signed short)(i & 0xFFFF) * 4) + 4; |
| + if ((i >> 16 == 0x1000) && !(a & BR_COND)) |
| + { |
| + /* unconditional branch, if no conditional branch could |
| + branch past this code */ |
| + b = t; |
| + a |= BR_UNCOND; |
| + } |
| + else |
| + { |
| + /* conditional branch, ignore if previous conditional branch |
| + is further forwards */ |
| + if ((t > b) && (t > f->ra)) |
| + { |
| + b = t; |
| + a |= BR_COND; |
| + /* can branch past an unconditional branch */ |
| + if (b > q) |
| + q = 0xFFFFFFFF; |
| + } |
| + else if (t < r) |
| + { |
| + /* but if branching backwards, set reverse branch target to |
| + lowest address target encountered so far */ |
| + r = t; |
| + /* ensure a loop back */ |
| + q = 0xFFFFFFFF; |
| + } |
| + } |
| + } |
| +#if SYSTEM == SYSTEM_LINUX |
| + else if (i == 0x0000000c) |
| + { |
| + /* syscall - check for signal handler trampolines */ |
| + if (*((unsigned long *) (f->ra - 4)) == 0x24020000 + __NR_sigreturn) |
| + { |
| + /* li v0,__NR_sigreturn */ |
| + /* get pointer to sigcontext */ |
| + f->sp = f->ra + 4; |
| + f->ra = ((struct sigcontext *) f->sp)->sc_pc | RA_SIGRETURN; |
| + return 1; |
| + } |
| + else if (*((unsigned long *) (f->ra - 4)) == 0x24020000 + __NR_rt_sigreturn) |
| + { |
| + /* li v0,__NR_rt_sigreturn */ |
| + /* get pointer to sigcontext */ |
| + f->sp = f->ra + 4 + |
| + sizeof(struct siginfo) + offsetof(struct asm_ucontext, uc_mcontext); |
| + f->ra = ((struct sigcontext *) f->sp)->sc_pc | RA_SIGRETURN; |
| + return 1; |
| + } |
| + } |
| +#endif |
| else |
| switch (i >> 16) |
| { |
| @@ -319,6 +425,10 @@ |
| u = i & 0xFFFF; |
| a |= SP_UPPER; |
| break; |
| + case 0x8FBE: |
| + /* lw s8,##(sp) */ |
| + m = i & 0xFFFF; |
| + break; |
| case 0x8FBF: |
| /* lw ra,##(sp) */ |
| p = i & 0xFFFF; |
| @@ -326,9 +436,52 @@ |
| break; |
| } |
| f->ra += 4; |
| + /* Process branch instructions. |
| + */ |
| + if (a & BR_COND) |
| + { |
| + if (f->ra >= b) |
| + { |
| + /* reached target of previous conditional branch */ |
| + a &= ~BR_COND; |
| + b = 0; |
| + } |
| + } |
| + else if (a & BR_UNCOND) |
| + /* clear branch flag and process instruction in delay slot */ |
| + a &= ~BR_UNCOND; |
| + else if (b != 0) |
| + { |
| + /* now follow the unconditional branch */ |
| + if (b < f->ra) |
| + { |
| + /* avoid infinite loops */ |
| + q = f->ra - 8; |
| + /* go back as far as possible */ |
| + if (r < b) |
| + b = r; |
| + } |
| + f->ra = b; |
| + b = 0; |
| + } |
| } |
| if ((s == 0) && ((a & SP_LOWER) || (a & SP_UPPER))) |
| s = (u << 16) | l; |
| +#if SYSTEM == SYSTEM_LINUX |
| + if ((a & RA_NOFRAME) && !(a & RA_OFFSET) && |
| + ((*((unsigned long *) (p - 8)) == 0x0320F809) || |
| + (*((unsigned long *) (p - 8)) >> 16 == 0x0C10))) |
| + { |
| + /* jalr ra,t9 or jal ## */ |
| + /* f->sp already set */ |
| + f->ra = p; |
| + return 1; |
| + } |
| +#endif |
| + if (a & SP_IN_FP) |
| + f->sp = f->fp; |
| + if (m > 0) |
| + f->fp = ((unsigned long *) f->sp)[m >> 2]; |
| if ((s > 0) && (i = ((unsigned long *) f->sp)[p >> 2]) && |
| ((*((unsigned long *) (i - 8)) == 0x0320F809) || |
| (*((unsigned long *) (i - 8)) >> 16 == 0x0C10))) |
| @@ -338,6 +491,19 @@ |
| f->ra = i; |
| return 1; |
| } |
| +#if SYSTEM == SYSTEM_LINUX |
| + else if ((s > 0) && (i != 0) && |
| + (*((unsigned long *) (i + 4)) == 0x0000000c) && |
| + ((*((unsigned long *) i) == 0x24020000 + __NR_sigreturn) || |
| + (*((unsigned long *) i) == 0x24020000 + __NR_rt_sigreturn))) |
| + { |
| + /* li v0,__NR_sigreturn or __NR_rt_sigreturn ; syscall */ |
| + /* signal trampoline */ |
| + f->sp += s; |
| + f->ra = i | RA_SIGTRAMP; |
| + return 1; |
| + } |
| +#endif |
| f->sp = f->ra = 0; |
| return 0; |
| } |
| @@ -573,16 +739,14 @@ |
| } |
| #endif /* TARGET */ |
| #else /* MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT */ |
| -#if (TARGET == TARGET_UNIX && (ARCH == ARCH_IX86 || ARCH == ARCH_M68K || \ |
| - ARCH == ARCH_M88K || ARCH == ARCH_POWER || ARCH == ARCH_POWERPC || \ |
| - ARCH == ARCH_SPARC)) || ((TARGET == TARGET_WINDOWS || \ |
| - TARGET == TARGET_NETWARE) && ARCH == ARCH_IX86) |
| - /* This section is not complete in any way for the OS / processor |
| - * combinations it supports, as it is intended to be as portable as possible |
| - * without writing in assembler. In particular, optimised code is likely |
| - * to cause major problems for stack traversal on some platforms. |
| - */ |
| #if TARGET == TARGET_UNIX |
| + /* On some systems, such as those using uClibc, the signal() function may |
| + * call memcpy() or other memory related functions, so we need to guard |
| + * against recursion here. |
| + */ |
| + if (!recursive) |
| + { |
| + recursive = 1; |
| #if MP_SIGINFO_SUPPORT |
| i.sa_flags = 0; |
| (void *) i.sa_handler = (void *) stackhandler; |
| @@ -597,6 +761,15 @@ |
| __mp_newframe(p, p->first); |
| else |
| #endif /* TARGET */ |
| +#if (TARGET == TARGET_UNIX && (ARCH == ARCH_IX86 || ARCH == ARCH_M68K || \ |
| + ARCH == ARCH_M88K || ARCH == ARCH_POWER || ARCH == ARCH_POWERPC || \ |
| + ARCH == ARCH_SPARC)) || ((TARGET == TARGET_WINDOWS || \ |
| + TARGET == TARGET_NETWARE) && ARCH == ARCH_IX86) |
| + /* This section is not complete in any way for the OS / processor |
| + * combinations it supports, as it is intended to be as portable as possible |
| + * without writing in assembler. In particular, optimised code is likely |
| + * to cause major problems for stack traversal on some platforms. |
| + */ |
| { |
| if (p->frame == NULL) |
| if (p->first == NULL) |
| @@ -640,32 +813,10 @@ |
| r = 1; |
| } |
| } |
| -#if TARGET == TARGET_UNIX |
| -#if MP_SIGINFO_SUPPORT |
| - sigaction(SIGBUS, &bushandler, NULL); |
| - sigaction(SIGSEGV, &segvhandler, NULL); |
| -#else /* MP_SIGINFO_SUPPORT */ |
| - signal(SIGBUS, bushandler); |
| - signal(SIGSEGV, segvhandler); |
| -#endif /* MP_SIGINFO_SUPPORT */ |
| -#endif /* TARGET */ |
| #elif TARGET == TARGET_UNIX && ARCH == ARCH_MIPS |
| /* For the MIPS architecture we perform code reading to determine the |
| * frame pointers and the return addresses. |
| */ |
| -#if MP_SIGINFO_SUPPORT |
| - i.sa_flags = 0; |
| - (void *) i.sa_handler = (void *) stackhandler; |
| - sigfillset(&i.sa_mask); |
| - sigaction(SIGBUS, &i, &bushandler); |
| - sigaction(SIGSEGV, &i, &segvhandler); |
| -#else /* MP_SIGINFO_SUPPORT */ |
| - bushandler = signal(SIGBUS, stackhandler); |
| - segvhandler = signal(SIGSEGV, stackhandler); |
| -#endif /* MP_SIGINFO_SUPPORT */ |
| - if (setjmp(environment)) |
| - __mp_newframe(p, p->first); |
| - else |
| { |
| if (p->frame == NULL) |
| unwind(&p->next); |
| @@ -673,6 +824,10 @@ |
| { |
| p->frame = (void *) p->next.sp; |
| p->addr = (void *) (p->next.ra - 8); |
| +#if SYSTEM == SYSTEM_LINUX |
| + if (p->next.ra & (RA_SIGTRAMP|RA_SIGRETURN)) |
| + p->addr = (void *) (p->next.ra & ~3); |
| +#endif /* SYSTEM */ |
| r = 1; |
| } |
| else |
| @@ -681,6 +836,8 @@ |
| p->addr = NULL; |
| } |
| } |
| +#endif /* TARGET && ARCH */ |
| +#if TARGET == TARGET_UNIX |
| #if MP_SIGINFO_SUPPORT |
| sigaction(SIGBUS, &bushandler, NULL); |
| sigaction(SIGSEGV, &segvhandler, NULL); |
| @@ -688,7 +845,9 @@ |
| signal(SIGBUS, bushandler); |
| signal(SIGSEGV, segvhandler); |
| #endif /* MP_SIGINFO_SUPPORT */ |
| -#endif /* TARGET && ARCH */ |
| + recursive = 0; |
| + } /* if (!bushandler) */ |
| +#endif /* TARGET */ |
| #endif /* MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT */ |
| return r; |
| } |
| diff -ur mpatrol/src/stack.h mpatrol-uclibc/src/stack.h |
| --- mpatrol/src/stack.h 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/stack.h 2006-06-07 15:12:58.000000000 -0700 |
| @@ -75,6 +75,7 @@ |
| typedef struct frameinfo |
| { |
| unsigned int sp; /* stack pointer */ |
| + unsigned int fp; /* frame pointer (s8) */ |
| unsigned int ra; /* return address */ |
| } |
| frameinfo; |
| diff -urN mpatrol/src/symbol.c mpatrol-uclibc/src/symbol.c |
| --- mpatrol/src/symbol.c 2002-01-08 12:13:59.000000000 -0800 |
| +++ mpatrol-uclibc/src/symbol.c 2006-05-24 15:43:04.000000000 -0700 |
| @@ -1157,7 +1157,7 @@ |
| __mp_error(ET_MAX, AT_MAX, NULL, 0, "%s: %s\n", f, m); |
| return 0; |
| } |
| - if (n == 0) |
| + if (n <= sizeof(asymbol *)) |
| { |
| /* If we couldn't find the symbol table then it is likely that the file |
| * has been stripped. However, if the file was dynamically linked then |
| @@ -1172,7 +1172,7 @@ |
| __mp_error(ET_MAX, AT_MAX, NULL, 0, "%s: %s\n", f, m); |
| return 0; |
| } |
| - if (n == 0) |
| + if (n <= sizeof(asymbol *)) |
| { |
| m = "missing symbol table"; |
| if (a != NULL) |
| @@ -1893,6 +1893,17 @@ |
| l = (dynamiclink *) *((unsigned long *) d->d_un.d_ptr + 1); |
| break; |
| } |
| +#if ARCH == ARCH_MIPS |
| + else if (d->d_tag == DT_MIPS_RLD_MAP) |
| + { |
| + /* MIPS elf has DT_MIPS_RLD_MAP instead of DT_DEBUG. */ |
| + if (!d->d_un.d_ptr || !(*(unsigned long **) d->d_un.d_ptr)) |
| + l = NULL; |
| + else |
| + l = (dynamiclink *) *((*(unsigned long **) d->d_un.d_ptr) + 1); |
| + break; |
| + } |
| +#endif /* ARCH */ |
| /* We skip past the first item on the list since it represents the |
| * executable file, but we may wish to record the name of the file |
| * if we haven't already determined it. |