1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "misc.h" 30 #include <ucontext.h> 31 #include <sys/frame.h> 32 #include <sys/stack.h> 33 #include <stdio.h> 34 35 #if defined(__sparc) || defined(__sparcv9) 36 extern void flush_windows(void); 37 #define UMEM_FRAMESIZE MINFRAME 38 39 #elif defined(__i386) || defined(__amd64) 40 /* 41 * On x86, MINFRAME is defined to be 0, but we want to be sure we can 42 * dereference the entire frame structure. 43 */ 44 #define UMEM_FRAMESIZE (sizeof (struct frame)) 45 46 #else 47 #error needs update for new architecture 48 #endif 49 50 /* 51 * Get a pc-only stacktrace. Used for kmem_alloc() buffer ownership tracking. 52 * Returns MIN(current stack depth, pcstack_limit). 53 */ 54 /*ARGSUSED*/ 55 int 56 getpcstack(uintptr_t *pcstack, int pcstack_limit, int check_signal) 57 { 58 struct frame *fp; 59 struct frame *nextfp, *minfp; 60 int depth = 0; 61 uintptr_t base = 0; 62 size_t size = 0; 63 #ifndef UMEM_STANDALONE 64 int on_altstack = 0; 65 uintptr_t sigbase = 0; 66 size_t sigsize = 0; 67 68 stack_t st; 69 70 if (stack_getbounds(&st) != 0) { 71 if (thr_stksegment(&st) != 0 || 72 (uintptr_t)st.ss_sp < st.ss_size) { 73 return (0); /* unable to get stack bounds */ 74 } 75 /* 76 * thr_stksegment(3C) has a slightly different interface than 77 * stack_getbounds(3C) -- correct it 78 */ 79 st.ss_sp = (void *)(((uintptr_t)st.ss_sp) - st.ss_size); 80 st.ss_flags = 0; /* can't be on-stack */ 81 } 82 on_altstack = (st.ss_flags & SS_ONSTACK); 83 84 if (st.ss_size != 0) { 85 base = (uintptr_t)st.ss_sp; 86 size = st.ss_size; 87 } else { 88 /* 89 * If size == 0, then ss_sp is the *top* of the stack. 90 * 91 * Since we only allow increasing frame pointers, and we 92 * know our caller set his up correctly, we can treat ss_sp 93 * as an upper bound safely. 94 */ 95 base = 0; 96 size = (uintptr_t)st.ss_sp; 97 } 98 99 if (check_signal != 0) { 100 void (*sigfunc)() = NULL; 101 int sigfuncsize = 0; 102 extern void thr_sighndlrinfo(void (**)(), int *); 103 104 thr_sighndlrinfo(&sigfunc, &sigfuncsize); 105 sigbase = (uintptr_t)sigfunc; 106 sigsize = sigfuncsize; 107 } 108 #else /* UMEM_STANDALONE */ 109 base = (uintptr_t)umem_min_stack; 110 size = umem_max_stack - umem_min_stack; 111 #endif 112 113 /* 114 * shorten size so that fr_savfp and fr_savpc will be within the stack 115 * bounds. 116 */ 117 if (size >= UMEM_FRAMESIZE - 1) 118 size -= (UMEM_FRAMESIZE - 1); 119 else 120 size = 0; 121 122 #if defined(__sparc) || defined(__sparcv9) 123 flush_windows(); 124 #endif 125 126 /* LINTED alignment */ 127 fp = (struct frame *)((caddr_t)getfp() + STACK_BIAS); 128 129 minfp = fp; 130 131 if (((uintptr_t)fp - base) >= size) 132 return (0); /* the frame pointer isn't in our stack */ 133 134 while (depth < pcstack_limit) { 135 uintptr_t tmp; 136 137 /* LINTED alignment */ 138 nextfp = (struct frame *)((caddr_t)fp->fr_savfp + STACK_BIAS); 139 tmp = (uintptr_t)nextfp; 140 141 /* 142 * Check nextfp for validity. It must be properly aligned, 143 * increasing compared to the last %fp (or the top of the 144 * stack we just switched to), and it must be inside 145 * [base, base + size). 146 */ 147 if (tmp != SA(tmp)) 148 break; 149 else if (nextfp <= minfp || (tmp - base) >= size) { 150 #ifndef UMEM_STANDALONE 151 if (tmp == NULL || !on_altstack) 152 break; 153 /* 154 * If we're on an alternate signal stack, try jumping 155 * to the main thread stack. 156 * 157 * If the main thread stack has an unlimited size, we 158 * punt, since we don't know where the frame pointer's 159 * been. 160 * 161 * (thr_stksegment() returns the *top of stack* 162 * in ss_sp, not the bottom) 163 */ 164 if (thr_stksegment(&st) == 0) { 165 if (st.ss_size >= (uintptr_t)st.ss_sp || 166 st.ss_size < UMEM_FRAMESIZE - 1) 167 break; 168 169 on_altstack = 0; 170 base = (uintptr_t)st.ss_sp - st.ss_size; 171 size = st.ss_size - (UMEM_FRAMESIZE - 1); 172 minfp = (struct frame *)base; 173 continue; /* try again */ 174 } 175 #endif 176 break; 177 } 178 179 #ifndef UMEM_STANDALONE 180 if (check_signal && (fp->fr_savpc - sigbase) <= sigsize) 181 umem_panic("called from signal handler"); 182 #endif 183 pcstack[depth++] = fp->fr_savpc; 184 fp = nextfp; 185 minfp = fp; 186 } 187 return (depth); 188 } 189