Home | History | Annotate | Line # | Download | only in isc
backtrace.c revision 1.1.1.3
      1 /*	$NetBSD: backtrace.c,v 1.1.1.3 2020/05/24 19:36:45 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 /*! \file */
     15 
     16 #include <stdlib.h>
     17 #include <string.h>
     18 #ifdef HAVE_LIBCTRACE
     19 #include <execinfo.h>
     20 #endif /* ifdef HAVE_LIBCTRACE */
     21 
     22 #include <isc/backtrace.h>
     23 #include <isc/result.h>
     24 #include <isc/util.h>
     25 
     26 #ifdef USE_BACKTRACE
     27 /*
     28  * Getting a back trace of a running process is tricky and highly platform
     29  * dependent.  Our current approach is as follows:
     30  * 1. If the system library supports the "backtrace()" function, use it.
     31  * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64,
     32  *    then use gcc's (hidden) Unwind_Backtrace() function.  Note that this
     33  *    function doesn't work for C programs on many other architectures.
     34  * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack
     35  *    frame following frame pointers.  This assumes the executable binary
     36  *    compiled with frame pointers; this is not always true for x86_64 (rather,
     37  *    compiler optimizations often disable frame pointers).  The validation
     38  *    checks in getnextframeptr() hopefully rejects bogus values stored in
     39  *    the RBP register in such a case.  If the backtrace function itself crashes
     40  *    due to this problem, the whole package should be rebuilt with
     41  *    --disable-backtrace.
     42  */
     43 #ifdef HAVE_LIBCTRACE
     44 #define BACKTRACE_LIBC
     45 #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__))
     46 #define BACKTRACE_GCC
     47 #elif defined(WIN32)
     48 #define BACKTRACE_WIN32
     49 #elif defined(__x86_64__) || defined(__i386__)
     50 #define BACKTRACE_X86STACK
     51 #else /* ifdef HAVE_LIBCTRACE */
     52 #define BACKTRACE_DISABLED
     53 #endif /* HAVE_LIBCTRACE */
     54 #else  /* USE_BACKTRACE */
     55 #define BACKTRACE_DISABLED
     56 #endif /* USE_BACKTRACE */
     57 
     58 #ifdef BACKTRACE_LIBC
     59 isc_result_t
     60 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
     61 	int n;
     62 
     63 	/*
     64 	 * Validate the arguments: intentionally avoid using REQUIRE().
     65 	 * See notes in backtrace.h.
     66 	 */
     67 	if (addrs == NULL || nframes == NULL) {
     68 		return (ISC_R_FAILURE);
     69 	}
     70 
     71 	/*
     72 	 * backtrace(3) includes this function itself in the address array,
     73 	 * which should be eliminated from the returned sequence.
     74 	 */
     75 	n = backtrace(addrs, maxaddrs);
     76 	if (n < 2) {
     77 		return (ISC_R_NOTFOUND);
     78 	}
     79 	n--;
     80 	memmove(addrs, &addrs[1], sizeof(void *) * n);
     81 	*nframes = n;
     82 	return (ISC_R_SUCCESS);
     83 }
     84 #elif defined(BACKTRACE_GCC)
     85 extern int
     86 _Unwind_Backtrace(void *fn, void *a);
     87 extern void *
     88 _Unwind_GetIP(void *ctx);
     89 
     90 typedef struct {
     91 	void **result;
     92 	int max_depth;
     93 	int skip_count;
     94 	int count;
     95 } trace_arg_t;
     96 
     97 static int
     98 btcallback(void *uc, void *opq) {
     99 	trace_arg_t *arg = (trace_arg_t *)opq;
    100 
    101 	if (arg->skip_count > 0) {
    102 		arg->skip_count--;
    103 	} else {
    104 		arg->result[arg->count++] = (void *)_Unwind_GetIP(uc);
    105 	}
    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 
    121 	arg.skip_count = 1;
    122 	arg.result = addrs;
    123 	arg.max_depth = maxaddrs;
    124 	arg.count = 0;
    125 	_Unwind_Backtrace(btcallback, &arg);
    126 
    127 	*nframes = arg.count;
    128 
    129 	return (ISC_R_SUCCESS);
    130 }
    131 #elif defined(BACKTRACE_WIN32)
    132 isc_result_t
    133 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
    134 	unsigned long ftc = (unsigned long)maxaddrs;
    135 
    136 	*nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL);
    137 	return (ISC_R_SUCCESS);
    138 }
    139 #elif defined(BACKTRACE_X86STACK)
    140 #ifdef __x86_64__
    141 static unsigned long
    142 getrbp(void) {
    143 	__asm("movq %rbp, %rax\n");
    144 }
    145 #endif /* ifdef __x86_64__ */
    146 
    147 static void **
    148 getnextframeptr(void **sp) {
    149 	void **newsp = (void **)*sp;
    150 
    151 	/*
    152 	 * Perform sanity check for the new frame pointer, derived from
    153 	 * google glog.  This can actually be bogus depending on compiler.
    154 	 */
    155 
    156 	/* prohibit the stack frames from growing downwards */
    157 	if (newsp <= sp) {
    158 		return (NULL);
    159 	}
    160 
    161 	/* A heuristics to reject "too large" frame: this actually happened. */
    162 	if ((char *)newsp - (char *)sp > 100000) {
    163 		return (NULL);
    164 	}
    165 
    166 	/*
    167 	 * Not sure if other checks used in glog are needed at this moment.
    168 	 * For our purposes we don't have to consider non-contiguous frames,
    169 	 * for example.
    170 	 */
    171 
    172 	return (newsp);
    173 }
    174 
    175 isc_result_t
    176 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
    177 	int i = 0;
    178 	void **sp;
    179 
    180 	/* Argument validation: see above. */
    181 	if (addrs == NULL || nframes == NULL) {
    182 		return (ISC_R_FAILURE);
    183 	}
    184 
    185 #ifdef __x86_64__
    186 	sp = (void **)getrbp();
    187 	if (sp == NULL) {
    188 		return (ISC_R_NOTFOUND);
    189 	}
    190 	/*
    191 	 * sp is the frame ptr of this function itself due to the call to
    192 	 * getrbp(), so need to unwind one frame for consistency.
    193 	 */
    194 	sp = getnextframeptr(sp);
    195 #else  /* ifdef __x86_64__ */
    196 	/*
    197 	 * i386: the frame pointer is stored 2 words below the address for the
    198 	 * first argument.  Note that the body of this function cannot be
    199 	 * inlined since it depends on the address of the function argument.
    200 	 */
    201 	sp = (void **)&addrs - 2;
    202 #endif /* ifdef __x86_64__ */
    203 
    204 	while (sp != NULL && i < maxaddrs) {
    205 		addrs[i++] = *(sp + 1);
    206 		sp = getnextframeptr(sp);
    207 	}
    208 
    209 	*nframes = i;
    210 
    211 	return (ISC_R_SUCCESS);
    212 }
    213 #elif defined(BACKTRACE_DISABLED)
    214 isc_result_t
    215 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
    216 	/* Argument validation: see above. */
    217 	if (addrs == NULL || nframes == NULL) {
    218 		return (ISC_R_FAILURE);
    219 	}
    220 
    221 	UNUSED(maxaddrs);
    222 
    223 	return (ISC_R_NOTIMPLEMENTED);
    224 }
    225 #endif /* ifdef BACKTRACE_LIBC */
    226 
    227 isc_result_t
    228 isc_backtrace_getsymbolfromindex(int idx, const void **addrp,
    229 				 const char **symbolp) {
    230 	REQUIRE(addrp != NULL && *addrp == NULL);
    231 	REQUIRE(symbolp != NULL && *symbolp == NULL);
    232 
    233 	if (idx < 0 || idx >= isc__backtrace_nsymbols) {
    234 		return (ISC_R_RANGE);
    235 	}
    236 
    237 	*addrp = isc__backtrace_symtable[idx].addr;
    238 	*symbolp = isc__backtrace_symtable[idx].symbol;
    239 	return (ISC_R_SUCCESS);
    240 }
    241 
    242 static int
    243 symtbl_compare(const void *addr, const void *entryarg) {
    244 	const isc_backtrace_symmap_t *entry = entryarg;
    245 	const isc_backtrace_symmap_t *end =
    246 		&isc__backtrace_symtable[isc__backtrace_nsymbols - 1];
    247 
    248 	if (isc__backtrace_nsymbols == 1 || entry == end) {
    249 		if (addr >= entry->addr) {
    250 			/*
    251 			 * If addr is equal to or larger than that of the last
    252 			 * entry of the table, we cannot be sure if this is
    253 			 * within a valid range so we consider it valid.
    254 			 */
    255 			return (0);
    256 		}
    257 		return (-1);
    258 	}
    259 
    260 	/* entry + 1 is a valid entry from now on. */
    261 	if (addr < entry->addr) {
    262 		return (-1);
    263 	} else if (addr >= (entry + 1)->addr) {
    264 		return (1);
    265 	}
    266 	return (0);
    267 }
    268 
    269 isc_result_t
    270 isc_backtrace_getsymbol(const void *addr, const char **symbolp,
    271 			unsigned long *offsetp) {
    272 	isc_result_t result = ISC_R_SUCCESS;
    273 	isc_backtrace_symmap_t *found;
    274 
    275 	/*
    276 	 * Validate the arguments: intentionally avoid using REQUIRE().
    277 	 * See notes in backtrace.h.
    278 	 */
    279 	if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) {
    280 		return (ISC_R_FAILURE);
    281 	}
    282 
    283 	if (isc__backtrace_nsymbols < 1) {
    284 		return (ISC_R_NOTFOUND);
    285 	}
    286 
    287 	/*
    288 	 * Search the table for the entry that meets:
    289 	 * entry.addr <= addr < next_entry.addr.
    290 	 */
    291 	found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols,
    292 			sizeof(isc__backtrace_symtable[0]), symtbl_compare);
    293 	if (found == NULL) {
    294 		result = ISC_R_NOTFOUND;
    295 	} else {
    296 		*symbolp = found->symbol;
    297 		*offsetp = (unsigned long)((const char *)addr -
    298 					   (char *)found->addr);
    299 	}
    300 
    301 	return (result);
    302 }
    303