Patches for mpatrol to support uClibc and MIPS full call stack tracing by Dan Howell 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 #include -#if MP_SIGINFO_SUPPORT +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX #include #endif /* MP_SIGINFO_SUPPORT */ #include 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 #include #if TARGET == TARGET_UNIX -#if MP_SIGINFO_SUPPORT +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX #include #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 -#if MP_SIGINFO_SUPPORT +#if MP_SIGINFO_SUPPORT && SYSTEM != SYSTEM_LINUX #include #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 +/* 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 +#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.