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