1 /* 2 * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */ 18 19 /*! \file */ 20 21 #include "config.h" 22 23 #include <string.h> 24 #include <stdlib.h> 25 #ifdef HAVE_LIBCTRACE 26 #include <execinfo.h> 27 #endif 28 29 #include <isc/backtrace.h> 30 #include <isc/result.h> 31 #include <isc/util.h> 32 33 #ifdef ISC_PLATFORM_USEBACKTRACE 34 /* 35 * Getting a back trace of a running process is tricky and highly platform 36 * dependent. Our current approach is as follows: 37 * 1. If the system library supports the "backtrace()" function, use it. 38 * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, 39 * then use gcc's (hidden) Unwind_Backtrace() function. Note that this 40 * function doesn't work for C programs on many other architectures. 41 * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack 42 * frame following frame pointers. This assumes the executable binary 43 * compiled with frame pointers; this is not always true for x86_64 (rather, 44 * compiler optimizations often disable frame pointers). The validation 45 * checks in getnextframeptr() hopefully rejects bogus values stored in 46 * the RBP register in such a case. If the backtrace function itself crashes 47 * due to this problem, the whole package should be rebuilt with 48 * --disable-backtrace. 49 */ 50 #ifdef HAVE_LIBCTRACE 51 #define BACKTRACE_LIBC 52 #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) 53 #define BACKTRACE_GCC 54 #elif defined(__x86_64__) || defined(__i386__) 55 #define BACKTRACE_X86STACK 56 #else 57 #define BACKTRACE_DISABLED 58 #endif /* HAVE_LIBCTRACE */ 59 #else /* !ISC_PLATFORM_USEBACKTRACE */ 60 #define BACKTRACE_DISABLED 61 #endif /* ISC_PLATFORM_USEBACKTRACE */ 62 63 #ifdef BACKTRACE_LIBC 64 isc_result_t 65 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 66 int n; 67 68 /* 69 * Validate the arguments: intentionally avoid using REQUIRE(). 70 * See notes in backtrace.h. 71 */ 72 if (addrs == NULL || nframes == NULL) 73 return (ISC_R_FAILURE); 74 75 /* 76 * backtrace(3) includes this function itself in the address array, 77 * which should be eliminated from the returned sequence. 78 */ 79 n = backtrace(addrs, maxaddrs); 80 if (n < 2) 81 return (ISC_R_NOTFOUND); 82 n--; 83 memmove(addrs, &addrs[1], sizeof(void *) * n); 84 *nframes = n; 85 return (ISC_R_SUCCESS); 86 } 87 #elif defined(BACKTRACE_GCC) 88 extern int _Unwind_Backtrace(void* fn, void* a); 89 extern void* _Unwind_GetIP(void* ctx); 90 91 typedef struct { 92 void **result; 93 int max_depth; 94 int skip_count; 95 int count; 96 } trace_arg_t; 97 98 static int 99 btcallback(void *uc, void *opq) { 100 trace_arg_t *arg = (trace_arg_t *)opq; 101 102 if (arg->skip_count > 0) 103 arg->skip_count--; 104 else 105 arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); 106 if (arg->count == arg->max_depth) 107 return (5); /* _URC_END_OF_STACK */ 108 109 return (0); /* _URC_NO_REASON */ 110 } 111 112 isc_result_t 113 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 114 trace_arg_t arg; 115 116 /* Argument validation: see above. */ 117 if (addrs == NULL || nframes == NULL) 118 return (ISC_R_FAILURE); 119 120 arg.skip_count = 1; 121 arg.result = addrs; 122 arg.max_depth = maxaddrs; 123 arg.count = 0; 124 _Unwind_Backtrace(btcallback, &arg); 125 126 *nframes = arg.count; 127 128 return (ISC_R_SUCCESS); 129 } 130 #elif defined(BACKTRACE_X86STACK) 131 #ifdef __x86_64__ 132 static unsigned long 133 getrbp() { 134 __asm("movq %rbp, %rax\n"); 135 } 136 #endif 137 138 static void ** 139 getnextframeptr(void **sp) { 140 void **newsp = (void **)*sp; 141 142 /* 143 * Perform sanity check for the new frame pointer, derived from 144 * google glog. This can actually be bogus depending on compiler. 145 */ 146 147 /* prohibit the stack frames from growing downwards */ 148 if (newsp <= sp) 149 return (NULL); 150 151 /* A heuristics to reject "too large" frame: this actually happened. */ 152 if ((char *)newsp - (char *)sp > 100000) 153 return (NULL); 154 155 /* 156 * Not sure if other checks used in glog are needed at this moment. 157 * For our purposes we don't have to consider non-contiguous frames, 158 * for example. 159 */ 160 161 return (newsp); 162 } 163 164 isc_result_t 165 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 166 int i = 0; 167 void **sp; 168 169 /* Argument validation: see above. */ 170 if (addrs == NULL || nframes == NULL) 171 return (ISC_R_FAILURE); 172 173 #ifdef __x86_64__ 174 sp = (void **)getrbp(); 175 if (sp == NULL) 176 return (ISC_R_NOTFOUND); 177 /* 178 * sp is the frame ptr of this function itself due to the call to 179 * getrbp(), so need to unwind one frame for consistency. 180 */ 181 sp = getnextframeptr(sp); 182 #else 183 /* 184 * i386: the frame pointer is stored 2 words below the address for the 185 * first argument. Note that the body of this function cannot be 186 * inlined since it depends on the address of the function argument. 187 */ 188 sp = (void **)&addrs - 2; 189 #endif 190 191 while (sp != NULL && i < maxaddrs) { 192 addrs[i++] = *(sp + 1); 193 sp = getnextframeptr(sp); 194 } 195 196 *nframes = i; 197 198 return (ISC_R_SUCCESS); 199 } 200 #elif defined(BACKTRACE_DISABLED) 201 isc_result_t 202 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 203 /* Argument validation: see above. */ 204 if (addrs == NULL || nframes == NULL) 205 return (ISC_R_FAILURE); 206 207 UNUSED(maxaddrs); 208 209 return (ISC_R_NOTIMPLEMENTED); 210 } 211 #endif 212 213 isc_result_t 214 isc_backtrace_getsymbolfromindex(int idx, const void **addrp, 215 const char **symbolp) 216 { 217 REQUIRE(addrp != NULL && *addrp == NULL); 218 REQUIRE(symbolp != NULL && *symbolp == NULL); 219 220 if (idx < 0 || idx >= isc__backtrace_nsymbols) 221 return (ISC_R_RANGE); 222 223 *addrp = isc__backtrace_symtable[idx].addr; 224 *symbolp = isc__backtrace_symtable[idx].symbol; 225 return (ISC_R_SUCCESS); 226 } 227 228 static int 229 symtbl_compare(const void *addr, const void *entryarg) { 230 const isc_backtrace_symmap_t *entry = entryarg; 231 const isc_backtrace_symmap_t *end = 232 &isc__backtrace_symtable[isc__backtrace_nsymbols - 1]; 233 234 if (isc__backtrace_nsymbols == 1 || entry == end) { 235 if (addr >= entry->addr) { 236 /* 237 * If addr is equal to or larger than that of the last 238 * entry of the table, we cannot be sure if this is 239 * within a valid range so we consider it valid. 240 */ 241 return (0); 242 } 243 return (-1); 244 } 245 246 /* entry + 1 is a valid entry from now on. */ 247 if (addr < entry->addr) 248 return (-1); 249 else if (addr >= (entry + 1)->addr) 250 return (1); 251 return (0); 252 } 253 254 isc_result_t 255 isc_backtrace_getsymbol(const void *addr, const char **symbolp, 256 unsigned long *offsetp) 257 { 258 isc_result_t result = ISC_R_SUCCESS; 259 isc_backtrace_symmap_t *found; 260 261 /* 262 * Validate the arguments: intentionally avoid using REQUIRE(). 263 * See notes in backtrace.h. 264 */ 265 if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) 266 return (ISC_R_FAILURE); 267 268 if (isc__backtrace_nsymbols < 1) 269 return (ISC_R_NOTFOUND); 270 271 /* 272 * Search the table for the entry that meets: 273 * entry.addr <= addr < next_entry.addr. 274 */ 275 found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols, 276 sizeof(isc__backtrace_symtable[0]), symtbl_compare); 277 if (found == NULL) 278 result = ISC_R_NOTFOUND; 279 else { 280 *symbolp = found->symbol; 281 *offsetp = (u_long)((const char *)addr - (char *)found->addr); 282 } 283 284 return (result); 285 } 286