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