With --enable-bfd-backtraces, use binutils libbfd to resolve backtraces
[strongswan.git] / src / libstrongswan / utils / backtrace.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17
18 #ifdef HAVE_DLADDR
19 # include <dlfcn.h>
20 #endif /* HAVE_DLADDR */
21
22 #ifdef HAVE_BACKTRACE
23 # include <execinfo.h>
24 #endif /* HAVE_BACKTRACE */
25
26 #include <string.h>
27
28 #include "backtrace.h"
29
30 typedef struct private_backtrace_t private_backtrace_t;
31
32 /**
33 * Private data of an backtrace_t object.
34 */
35 struct private_backtrace_t {
36
37 /**
38 * Public backtrace_t interface.
39 */
40 backtrace_t public;
41
42 /**
43 * Number of stacks frames obtained in stack_frames
44 */
45 int frame_count;
46
47 /**
48 * Recorded stack frames.
49 */
50 void *frames[];
51 };
52
53 #ifdef HAVE_DLADDR
54 #ifdef HAVE_BFD_H
55
56 #include <bfd.h>
57 #include <utils/hashtable.h>
58 #include <threading/mutex.h>
59
60 /**
61 * Hashtable-cached bfd handle
62 */
63 typedef struct {
64 /** binary file name on disk */
65 char *filename;
66 /** bfd handle */
67 bfd *abfd;
68 /** loaded symbols */
69 asymbol **syms;
70 } bfd_entry_t;
71
72 /**
73 * Destroy a bfd_entry
74 */
75 static void bfd_entry_destroy(bfd_entry_t *this)
76 {
77 free(this->filename);
78 free(this->syms);
79 bfd_close(this->abfd);
80 free(this);
81 }
82
83 /**
84 * Data to pass to find_addr()
85 */
86 typedef struct {
87 /** used bfd entry */
88 bfd_entry_t *entry;
89 /** backtrace address */
90 bfd_vma vma;
91 /** stream to log to */
92 FILE *file;
93 /** TRUE if complete */
94 bool found;
95 } bfd_find_data_t;
96
97 /**
98 * bfd entry cache
99 */
100 static hashtable_t *bfds;
101
102 static mutex_t *bfd_mutex;
103
104 /**
105 * Hashtable hash function
106 */
107 static u_int bfd_hash(char *key)
108 {
109 return chunk_hash(chunk_create(key, strlen(key)));
110 }
111
112 /**
113 * Hashtable equals function
114 */
115 static bool bfd_equals(char *a, char *b)
116 {
117 return streq(a, b);
118 }
119
120 /**
121 * See header.
122 */
123 void backtrace_init()
124 {
125 bfd_init();
126 bfds = hashtable_create((hashtable_hash_t)bfd_hash,
127 (hashtable_equals_t)bfd_equals, 8);
128 bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
129 }
130
131 /**
132 * See header.
133 */
134 void backtrace_deinit()
135 {
136 enumerator_t *enumerator;
137 bfd_entry_t *entry;
138 char *key;
139
140 enumerator = bfds->create_enumerator(bfds);
141 while (enumerator->enumerate(enumerator, &key, &entry))
142 {
143 bfds->remove_at(bfds, enumerator);
144 bfd_entry_destroy(entry);
145 }
146 enumerator->destroy(enumerator);
147
148 bfds->destroy(bfds);
149 bfd_mutex->destroy(bfd_mutex);
150 }
151
152 /**
153 * Find and print information to an address
154 */
155 static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data)
156 {
157 bfd_size_type size;
158 bfd_vma vma;
159 const char *source;
160 const char *function;
161 u_int line;
162
163 if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0)
164 {
165 vma = bfd_get_section_vma(abfd, section);
166 if (data->vma >= vma)
167 {
168 size = bfd_get_section_size(section);
169 if (data->vma < vma + size)
170 {
171 data->found = bfd_find_nearest_line(abfd, section,
172 data->entry->syms, data->vma - vma,
173 &source, &function, &line);
174 if (data->found)
175 {
176 if (source || function)
177 {
178 fprintf(data->file, " -> ");
179 if (function)
180 {
181 fprintf(data->file, "\e[34m%s() ", function);
182 }
183 if (source)
184 {
185 fprintf(data->file, "\e[32m@ %s:%d", source, line);
186 }
187 fprintf(data->file, "\e[0m\n");
188 }
189 }
190 }
191 }
192 }
193 }
194
195 /**
196 * Find a cached bfd entry, create'n'cache if not found
197 */
198 static bfd_entry_t *get_bfd_entry(char *filename)
199 {
200 bool dynamic = FALSE, ok = FALSE;
201 bfd_entry_t *entry;
202 long size;
203
204 /* check cache */
205 entry = bfds->get(bfds, filename);
206 if (entry)
207 {
208 return entry;
209 }
210
211 INIT(entry,
212 .abfd = bfd_openr(filename, NULL),
213 );
214
215 if (!entry->abfd)
216 {
217 free(entry);
218 return NULL;
219 }
220 entry->abfd->flags |= BFD_DECOMPRESS;
221 if (bfd_check_format(entry->abfd, bfd_archive) == 0 &&
222 bfd_check_format_matches(entry->abfd, bfd_object, NULL))
223 {
224 if (bfd_get_file_flags(entry->abfd) & HAS_SYMS)
225 {
226 size = bfd_get_symtab_upper_bound(entry->abfd);
227 if (size == 0)
228 {
229 size = bfd_get_dynamic_symtab_upper_bound(entry->abfd);
230 }
231 if (size >= 0)
232 {
233 entry->syms = malloc(size);
234 if (dynamic)
235 {
236 ok = bfd_canonicalize_dynamic_symtab(entry->abfd,
237 entry->syms) >= 0;
238 }
239 else
240 {
241 ok = bfd_canonicalize_symtab(entry->abfd,
242 entry->syms) >= 0;
243 }
244 }
245 }
246 }
247 if (ok)
248 {
249 entry->filename = strdup(filename);
250 bfds->put(bfds, entry->filename, entry);
251 return entry;
252 }
253 bfd_entry_destroy(entry);
254 return NULL;
255 }
256
257 /**
258 * Print the source file with line number to file, libbfd variant
259 */
260 static void print_sourceline(FILE *file, char *filename, void *ptr)
261 {
262 bfd_entry_t *entry;
263 bfd_find_data_t data = {
264 .file = file,
265 .vma = (bfd_vma)ptr,
266 };
267 bool old = FALSE;
268
269 bfd_mutex->lock(bfd_mutex);
270 if (lib->leak_detective)
271 {
272 old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
273 }
274 entry = get_bfd_entry(filename);
275 if (entry)
276 {
277 data.entry = entry;
278 bfd_map_over_sections(entry->abfd, (void*)find_addr, &data);
279 }
280 if (lib->leak_detective)
281 {
282 lib->leak_detective->set_state(lib->leak_detective, old);
283 }
284 bfd_mutex->unlock(bfd_mutex);
285 }
286
287 #else /* !HAVE_BFD_H */
288
289 void backtrace_init() {}
290 void backtrace_deinit() {}
291
292 /**
293 * Print the source file with line number to file, slow addr2line variant
294 */
295 static void print_sourceline(FILE *file, char *filename, void *ptr)
296 {
297 char cmd[1024];
298 FILE *output;
299 int c;
300
301 snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", filename, ptr);
302 output = popen(cmd, "r");
303 if (output)
304 {
305 fprintf(file, " -> \e[32m");
306 while (TRUE)
307 {
308 c = getc(output);
309 if (c == '\n' || c == EOF)
310 {
311 break;
312 }
313 fputc(c, file);
314 }
315 pclose(output);
316 fprintf(file, "\e[0m\n");
317 }
318 }
319
320 #endif /* HAVE_BFD_H */
321 #endif /* HAVE_DLADDR */
322
323 METHOD(backtrace_t, log_, void,
324 private_backtrace_t *this, FILE *file, bool detailed)
325 {
326 #ifdef HAVE_BACKTRACE
327 size_t i;
328 char **strings;
329
330 strings = backtrace_symbols(this->frames, this->frame_count);
331
332 fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count);
333 for (i = 0; i < this->frame_count; i++)
334 {
335 #ifdef HAVE_DLADDR
336 Dl_info info;
337
338 if (dladdr(this->frames[i], &info))
339 {
340 void *ptr = this->frames[i];
341
342 if (strstr(info.dli_fname, ".so"))
343 {
344 ptr = (void*)(this->frames[i] - info.dli_fbase);
345 }
346 if (info.dli_sname)
347 {
348 fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%tx) [%p]\n",
349 info.dli_fname, info.dli_fbase, info.dli_sname,
350 this->frames[i] - info.dli_saddr, this->frames[i]);
351 }
352 else
353 {
354 fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
355 info.dli_fbase, this->frames[i]);
356 }
357 if (detailed)
358 {
359 print_sourceline(file, (char*)info.dli_fname, ptr);
360 }
361 }
362 else
363 #endif /* HAVE_DLADDR */
364 {
365 fprintf(file, " %s\n", strings[i]);
366 }
367 }
368 free (strings);
369 #else /* !HAVE_BACKTRACE */
370 fprintf(file, "C library does not support backtrace().\n");
371 #endif /* HAVE_BACKTRACE */
372 }
373
374 METHOD(backtrace_t, contains_function, bool,
375 private_backtrace_t *this, char *function[], int count)
376 {
377 #ifdef HAVE_DLADDR
378 int i, j;
379
380 for (i = 0; i< this->frame_count; i++)
381 {
382 Dl_info info;
383
384 if (dladdr(this->frames[i], &info) && info.dli_sname)
385 {
386 for (j = 0; j < count; j++)
387 {
388 if (streq(info.dli_sname, function[j]))
389 {
390 return TRUE;
391 }
392 }
393 }
394 }
395 #endif /* HAVE_DLADDR */
396 return FALSE;
397 }
398
399 METHOD(backtrace_t, equals, bool,
400 private_backtrace_t *this, backtrace_t *other_public)
401 {
402 private_backtrace_t *other = (private_backtrace_t*)other_public;
403 int i;
404
405 if (this == other)
406 {
407 return TRUE;
408 }
409 if (this->frame_count != other->frame_count)
410 {
411 return FALSE;
412 }
413 for (i = 0; i < this->frame_count; i++)
414 {
415 if (this->frames[i] != other->frames[i])
416 {
417 return FALSE;
418 }
419 }
420 return TRUE;
421 }
422
423 /**
424 * Frame enumerator
425 */
426 typedef struct {
427 /** implements enumerator_t */
428 enumerator_t public;
429 /** reference to backtrace */
430 private_backtrace_t *bt;
431 /** current position */
432 int i;
433 } frame_enumerator_t;
434
435 METHOD(enumerator_t, frame_enumerate, bool,
436 frame_enumerator_t *this, void **addr)
437 {
438 if (this->i < this->bt->frame_count)
439 {
440 *addr = this->bt->frames[this->i++];
441 return TRUE;
442 }
443 return FALSE;
444 }
445
446 METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
447 private_backtrace_t *this)
448 {
449 frame_enumerator_t *enumerator;
450
451 INIT(enumerator,
452 .public = {
453 .enumerate = (void*)_frame_enumerate,
454 .destroy = (void*)free,
455 },
456 .bt = this,
457 );
458 return &enumerator->public;
459 }
460
461 METHOD(backtrace_t, destroy, void,
462 private_backtrace_t *this)
463 {
464 free(this);
465 }
466
467 /**
468 * See header
469 */
470 backtrace_t *backtrace_create(int skip)
471 {
472 private_backtrace_t *this;
473 void *frames[50];
474 int frame_count = 0;
475
476 #ifdef HAVE_BACKTRACE
477 frame_count = backtrace(frames, countof(frames));
478 #endif /* HAVE_BACKTRACE */
479 frame_count = max(frame_count - skip, 0);
480 this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
481 memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
482 this->frame_count = frame_count;
483
484 this->public = (backtrace_t) {
485 .log = _log_,
486 .contains_function = _contains_function,
487 .equals = _equals,
488 .create_frame_enumerator = _create_frame_enumerator,
489 .destroy = _destroy,
490 };
491
492 return &this->public;
493 }
494
495 /**
496 * See header
497 */
498 void backtrace_dump(char *label, FILE *file, bool detailed)
499 {
500 backtrace_t *backtrace;
501
502 backtrace = backtrace_create(2);
503
504 if (label)
505 {
506 fprintf(file, "Debug backtrace: %s\n", label);
507 }
508 backtrace->log(backtrace, file, detailed);
509 backtrace->destroy(backtrace);
510 }
511