--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libc/port/gen/walkstack.c Tue Jun 14 00:00:00 2005 -0700
@@ -0,0 +1,532 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file provides a general purpose mechanism
+ * for a user thread to walk its own call stack,
+ * calling a user-specified iterator function for each
+ * stack frame. Special handling is provided to indicate
+ * kernel-constructed signal handler frames.
+ *
+ * Adapted from usr/src/lib/libproc/common/Pstack.c:
+ *
+ * A signal handler frame is essentially a set of data pushed on to the user
+ * stack by the kernel prior to returning to the user program in one of the
+ * pre-defined signal handlers. The signal handler itself receives the signal
+ * number, an optional pointer to a siginfo_t, and a pointer to the interrupted
+ * ucontext as arguments.
+ *
+ * When performing a stack backtrace, we would like to
+ * detect these frames so that we can correctly return the interrupted program
+ * counter and frame pointer as a separate frame.
+ *
+ * The stack layout for a signal handler frame is as follows:
+ *
+ * SPARC v7/v9: Intel ia32:
+ * +--------------+ - high +--------------+ -
+ * | struct fq | ^ addrs | siginfo_t | optional
+ * +--------------+ | ^ +--------------+ -
+ * | gwindows_t | | | ucontext_t | ^
+ * +--------------+ optional +--------------+ |
+ * | siginfo_t | | ucontext_t * | |
+ * +--------------+ | | +--------------+
+ * | xregs data | v v | siginfo_t * | mandatory
+ * +--------------+ - low +--------------+
+ * | ucontext_t | ^ addrs | int (signo) | |
+ * +--------------+ mandatory +--------------+ |
+ * | struct frame | v | struct frame | v
+ * +--------------+ - <- %sp on resume +--------------+ - <- %esp on resume
+ *
+ * amd64 (64-bit)
+ * +--------------+ -
+ * | siginfo_t | optional
+ * +--------------+ -
+ * | ucontext_t | ^
+ * +--------------+ |
+ * | siginfo_t * |
+ * +--------------+ mandatory
+ * | int (signo) |
+ * +--------------+ |
+ * | struct frame | v
+ * +--------------+ - <- %rsp on resume
+ *
+ * The bottom-most struct frame is actually constructed by the kernel by
+ * copying the previous stack frame, allowing naive backtrace code to simply
+ * skip over the interrupted frame. The copied frame is never really used,
+ * since it is presumed the libc or libthread signal handler wrapper function
+ * will explicitly setcontext(2) to the interrupted context if the user
+ * program's handler returns. If we detect a signal handler frame, we simply
+ * read the interrupted context structure from the stack, use its embedded
+ * gregs to construct the register set for the interrupted frame, and then
+ * continue our backtrace. Detecting the frame itself is easy according to
+ * the diagram ("oldcontext" represents any element in the uc_link chain):
+ *
+ * On SPARC v7 or v9:
+ * %fp + sizeof (struct frame) == oldcontext
+ *
+ * On i386:
+ * %ebp + sizeof (struct frame) + (3 words) == oldcontext
+ *
+ * On amd64:
+ * %rbp + sizeof (struct frame) + (2 words) == oldcontext
+ *
+ * Since we want to provide the signal number that generated a signal stack
+ * frame and on sparc this information isn't written to the stack by the kernel
+ * the way it's done on i386, we're forced to read the signo from the stack as
+ * one of the arguments to the signal handler. What we hope is that no one has
+ * used __sigaction directly; if we're not linked with libthread
+ * (_thr_sighndlrinfo is NULL) then we attempt to read the signo directly from
+ * the register window. Otherwise we use the _thr_sighndlrinfo interface to find
+ * the correct frame.
+ *
+ */
+
+#pragma weak walkcontext = _walkcontext
+#pragma weak printstack = _printstack
+
+#include "synonyms.h"
+#include <assert.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <link.h>
+#include <procfs.h>
+#include <strings.h>
+#include <signal.h>
+#include <sys/frame.h>
+#include <sys/regset.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <thread.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/stack.h>
+#include <errno.h>
+#include <stdio.h>
+#include <alloca.h>
+#include <limits.h>
+
+#ifdef _LP64
+#define _ELF64
+#endif
+
+#include <sys/machelf.h>
+
+
+#if defined(__sparc)
+#define FRAME_PTR_REGISTER REG_SP
+#define PC_REGISTER REG_PC
+#define CHECK_FOR_SIGFRAME(fp, oldctx) ((fp) + SA(sizeof (struct frame)) \
+ == (oldctx))
+
+#elif defined(__amd64)
+#define FRAME_PTR_REGISTER REG_RBP
+#define PC_REGISTER REG_RIP
+#define CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \
+ 2 * sizeof (long) == (oldctx)) && \
+ (((struct frame *)fp)->fr_savpc == (greg_t)-1))
+
+#elif defined(__i386)
+#define FRAME_PTR_REGISTER EBP
+#define PC_REGISTER EIP
+#define CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \
+ 3 * sizeof (int) == (oldctx)) && \
+ (((struct frame *)fp)->fr_savpc == (greg_t)-1))
+#else
+#error no arch defined
+#endif
+
+
+/*
+ * use /proc/self/as to safely dereference pointers so we don't
+ * die in the case of a stack smash
+ */
+
+static int
+read_safe(int fd, struct frame *fp, struct frame **savefp, uintptr_t *savepc)
+{
+
+ uintptr_t newfp;
+
+ if ((uintptr_t)fp & (sizeof (void *) - 1))
+ return (-1); /* misaligned */
+
+ if ((pread(fd, (void *)&newfp, sizeof (fp->fr_savfp),
+ (off_t)&fp->fr_savfp) != sizeof (fp->fr_savfp)) ||
+ pread(fd, (void *)savepc, sizeof (fp->fr_savpc),
+ (off_t)&fp->fr_savpc) != sizeof (fp->fr_savpc))
+ return (-1);
+
+ /*
+ * handle stack bias on sparcv9
+ */
+
+ if (newfp != 0)
+ newfp += STACK_BIAS;
+
+ *savefp = (struct frame *)newfp;
+
+ return (0);
+}
+
+int
+walkcontext(const ucontext_t *uptr, int (*operate_func)(uintptr_t, int, void *),
+ void *usrarg)
+{
+ ucontext_t *oldctx = uptr->uc_link;
+
+ int fd;
+ int sig;
+#if defined(__sparc)
+ int signo = 0;
+#endif
+
+ struct frame *savefp;
+ uintptr_t savepc;
+
+ /*
+ * snag frame point from ucontext... we'll see caller of
+ * getucontext since we'll start by working up the call
+ * stack by one
+ */
+
+ struct frame *fp = (struct frame *)
+ ((uintptr_t)uptr->uc_mcontext.gregs[FRAME_PTR_REGISTER] +
+ STACK_BIAS);
+
+ /*
+ * Since we don't write signo to the stack on sparc, we need
+ * to extract signo from the stack frames. This is problematic
+ * in the case of libthread (libc has deterministic behavior)
+ * since we're not sure where we can do that safely. An awkward
+ * interface was provided for this purpose in libthread:
+ * _thr_sighndlrinfo; this is documented in
+ * /shared/sac/PSARC/1999/024. When called, this function
+ * returns the PC of a special function (and its size) that
+ * will be present in the stack frame if a signal was
+ * delivered and will have the following signature
+ * __sighndlr(int sig, siginfo_t *si, ucontex_t *uc,
+ * void (*hndlr)())
+ * Since this function is written in assembler and doesn't
+ * perturb its registers, we can then read sig out of arg0
+ * when the saved pc is inside this function.
+ *
+ */
+#if defined(__sparc)
+
+ uintptr_t special_pc = NULL;
+ int special_size = 0;
+
+ extern void _thr_sighndlrinfo(void (**func)(), int *funcsize);
+
+#pragma weak _thr_sighndlrinfo
+
+ if (_thr_sighndlrinfo != NULL) {
+ _thr_sighndlrinfo((void (**)())&special_pc, &special_size);
+ }
+#endif /* sparc */
+
+
+ if ((fd = open("/proc/self/as", O_RDONLY)) < 0)
+ return (-1);
+
+ while (fp != NULL) {
+
+ sig = 0;
+
+ /*
+ * get value of saved fp and pc w/o crashing
+ */
+
+ if (read_safe(fd, fp, &savefp, &savepc) != 0) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (savefp == NULL)
+ break;
+
+ /*
+ * note that the following checks to see if we've got a
+ * special signal stack frame present; this allows us to
+ * detect signals and pass that info to the user stack walker
+ */
+
+ if (oldctx != NULL &&
+ CHECK_FOR_SIGFRAME((uintptr_t)savefp, (uintptr_t)oldctx)) {
+
+#if defined(__i386) || defined(__amd64)
+ /*
+ * i386 and amd64 store signo on stack;
+ * simple to detect and use
+ */
+ sig = *((int *)(savefp + 1));
+#endif
+
+#if defined(__sparc)
+ /*
+ * with sparc we need to handle
+ * single and multi-threaded cases
+ * separately
+ * If we're single threaded, the trampoline
+ * in libc will have the signo as the first
+ * argumment; we can snag that directly.
+ * In the case of threads, since there are multiple
+ * complex routines between kernel and user handler,
+ * we need to figure out where we can read signal from
+ * using _thr_sighndlrinfo - which we've already done
+ * for this signal, since it appeared on the stack
+ * before the signal frame.... sigh.
+ */
+
+ if (_thr_sighndlrinfo == NULL) /* single threaded */
+ sig = fp->fr_arg[0];
+ else
+ sig = signo; /* already read - see below */
+#endif
+ /*
+ * this is the special signal frame, so cons up
+ * the saved fp & pc to pass to user's function
+ */
+
+ savefp = (struct frame *)
+ ((uintptr_t)oldctx->
+ uc_mcontext.gregs[FRAME_PTR_REGISTER] +
+ STACK_BIAS);
+ savepc = oldctx->uc_mcontext.gregs[PC_REGISTER];
+
+ oldctx = oldctx->uc_link; /* handle nested signals */
+ }
+#if defined(__sparc)
+
+ /*
+ * lookahead code to find right spot to read signo from...
+ */
+
+ if (_thr_sighndlrinfo &&
+ savepc >= special_pc && savepc <
+ (special_pc + special_size))
+ signo = fp->fr_arg[0];
+#endif
+
+ /*
+ * call user-supplied function and quit if non-zero return.
+ */
+
+ if (operate_func((uintptr_t)savepc, sig, usrarg) != 0)
+ break;
+
+ fp = savefp; /* up one in the call stack */
+ }
+
+ (void) close(fd);
+ return (0);
+}
+
+static size_t
+ulongtos(char *buffer, unsigned long x, int base)
+{
+ char local[80];
+ static const char digits[] = "0123456789abcdef";
+
+ unsigned int n = sizeof (local) - 1;
+ unsigned long rem;
+ unsigned int mod;
+
+ local[n] = 0;
+
+ rem = x;
+
+ do {
+ switch (base) {
+ case 10:
+ mod = rem % 10;
+ rem = rem / 10;
+ break;
+
+ case 16:
+ mod = rem & 15;
+ rem = rem >> 4;
+ break;
+ default:
+ return (0);
+ }
+ local[--n] = digits[mod];
+ } while (rem != 0);
+
+ (void) strcpy(buffer, local + n);
+
+ return (sizeof (local) - n - 1);
+}
+
+static void
+async_filenoprintf(int filenum, const char *format, ...)
+{
+ const char *src = format;
+ va_list ap;
+ long i;
+ struct iovec *iov;
+ int cnt;
+ int iter = 0;
+
+ /*
+ * count # of %'s.. max # of iovs is 2n + 1
+ */
+
+ for (cnt = i = 0; src[i] != '\0'; i++)
+ if (src[i] == '%')
+ cnt++;
+
+ iov = alloca((2 * cnt + 1) * sizeof (struct iovec));
+
+ va_start(ap, format);
+
+
+ while (*src) {
+
+ iov[iter].iov_base = (char *)src;
+ iov[iter].iov_len = 0;
+
+ while (*src && *src != '%') {
+ iov[iter].iov_len++;
+ src++;
+ }
+
+ if (iov[iter].iov_len != 0)
+ iter++;
+
+ if (*src == '%') {
+ switch (*++src) {
+ case 's':
+ iov[iter].iov_base = va_arg(ap, char *);
+ iov[iter].iov_len = strlen(iov[iter].iov_base);
+ iter++;
+ break;
+ case 'd':
+ iov[iter].iov_base = alloca(24);
+
+ i = va_arg(ap, long);
+ if (i < 0) {
+ *iov[iter].iov_base = '-';
+ iov[iter].iov_len =
+ ulongtos(iov[iter].iov_base + 1,
+ -i, 10) + 1;
+ } else
+ iov[iter].iov_len =
+ ulongtos(iov[iter].iov_base,
+ i, 10);
+ iter++;
+ break;
+ case 'x':
+ iov[iter].iov_base = alloca(24);
+ iov[iter].iov_len = ulongtos(iov[iter].iov_base,
+ va_arg(ap, unsigned long), 16);
+ iter++;
+ break;
+
+ case '%':
+ iov[iter].iov_base = (char *)src;
+ iov[iter].iov_len = 1;
+ iter++;
+ break;
+ }
+ src++;
+ }
+ }
+ va_end(ap);
+
+ (void) writev(filenum, iov, iter);
+
+}
+
+static int
+display_stack_info(uintptr_t pc, int signo, void *arg)
+{
+ Dl_info info;
+
+ char sigbuf[SIG2STR_MAX];
+
+ Sym *sym;
+
+ int filenum = (intptr_t)arg;
+
+ if (signo) {
+ if (sig2str(signo, sigbuf) != 0)
+ (void) strcpy(sigbuf, "?");
+ }
+
+ if (dladdr1((void *) pc, &info, (void**) &sym, RTLD_DL_SYMENT) == 0) {
+ /* no info at all */
+ if (signo == 0)
+ async_filenoprintf(filenum, "0x%x\n", pc);
+ else
+ async_filenoprintf(filenum,
+ "0x%x [ Signal %d (%s)]\n", pc,
+ (ulong_t)signo, sigbuf);
+
+ } else if ((pc - (unsigned long)info.dli_saddr) <
+ sym->st_size) {
+ /* found a global symbol */
+ if (signo == 0)
+ async_filenoprintf(filenum, "%s:%s+0x%x\n",
+ info.dli_fname,
+ info.dli_sname,
+ pc - (unsigned long)info.dli_saddr);
+ else
+ async_filenoprintf(filenum,
+ "%s:%s+0x%x [ Signal %d (%s)]\n",
+ info.dli_fname,
+ info.dli_sname,
+ pc - (unsigned long)info.dli_saddr,
+ (ulong_t)signo, sigbuf);
+ } else {
+ /* found a static symbol */
+ if (signo == 0)
+ async_filenoprintf(filenum, "%s:0x%x\n",
+ info.dli_fname,
+ pc - (unsigned long)info.dli_fbase);
+ else
+ async_filenoprintf(filenum,
+ "%s:0x%x [ Signal %d (%s)]\n",
+ info.dli_fname,
+ pc - (unsigned long)info.dli_fbase,
+ (ulong_t)signo, sigbuf);
+ }
+
+ return (0);
+}
+
+int
+printstack(int dofd)
+{
+ ucontext_t u;
+
+ if (getcontext(&u) < 0)
+ return (-1);
+
+ return (walkcontext(&u, display_stack_info, (void*)(intptr_t)dofd));
+}