backtrace: Add DbgHelp based Windows support for creating/printing backtraces
[strongswan.git] / src / libstrongswan / utils / backtrace.c
1 /*
2 * Copyright (C) 2006-2013 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2013 revosec AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18
19 #ifdef HAVE_BACKTRACE
20 # include <execinfo.h>
21 #endif /* HAVE_BACKTRACE */
22 #ifdef HAVE_DBGHELP
23 # include <winsock2.h>
24 # include <windows.h>
25 # include <dbghelp.h>
26 #endif /* HAVE_DBGHELP */
27 #include <string.h>
28
29 #include "backtrace.h"
30
31 #include <utils/debug.h>
32
33 typedef struct private_backtrace_t private_backtrace_t;
34
35 /**
36 * Private data of an backtrace_t object.
37 */
38 struct private_backtrace_t {
39
40 /**
41 * Public backtrace_t interface.
42 */
43 backtrace_t public;
44
45 /**
46 * Number of stacks frames obtained in stack_frames
47 */
48 int frame_count;
49
50 /**
51 * Recorded stack frames.
52 */
53 void *frames[];
54 };
55
56 /**
57 * Forward declaration of method getter
58 */
59 static backtrace_t get_methods();
60
61 /**
62 * Write a format string with arguments to a FILE line, if it is NULL to DBG
63 */
64 static void println(FILE *file, char *format, ...)
65 {
66 char buf[512];
67 va_list args;
68
69 va_start(args, format);
70 if (file)
71 {
72 vfprintf(file, format, args);
73 fputs("\n", file);
74 }
75 else
76 {
77 vsnprintf(buf, sizeof(buf), format, args);
78 DBG1(DBG_LIB, "%s", buf);
79 }
80 va_end(args);
81 }
82
83 #ifdef HAVE_DLADDR
84
85 #include <dlfcn.h>
86
87 /**
88 * Same as tty_escape_get(), but for a potentially NULL FILE*
89 */
90 static char* esc(FILE *file, tty_escape_t escape)
91 {
92 if (file)
93 {
94 return tty_escape_get(fileno(file), escape);
95 }
96 return "";
97 }
98
99 #ifdef HAVE_BFD_H
100
101 #include <bfd.h>
102 #include <collections/hashtable.h>
103 #include <threading/mutex.h>
104
105 /**
106 * Hashtable-cached bfd handle
107 */
108 typedef struct {
109 /** binary file name on disk */
110 char *filename;
111 /** bfd handle */
112 bfd *abfd;
113 /** loaded symbols */
114 asymbol **syms;
115 } bfd_entry_t;
116
117 /**
118 * Destroy a bfd_entry
119 */
120 static void bfd_entry_destroy(bfd_entry_t *this)
121 {
122 free(this->filename);
123 free(this->syms);
124 bfd_close(this->abfd);
125 free(this);
126 }
127
128 /**
129 * Data to pass to find_addr()
130 */
131 typedef struct {
132 /** used bfd entry */
133 bfd_entry_t *entry;
134 /** backtrace address */
135 bfd_vma vma;
136 /** stream to log to */
137 FILE *file;
138 /** TRUE if complete */
139 bool found;
140 } bfd_find_data_t;
141
142 /**
143 * bfd entry cache
144 */
145 static hashtable_t *bfds;
146
147 static mutex_t *bfd_mutex;
148
149 /**
150 * Hashtable hash function
151 */
152 static u_int bfd_hash(char *key)
153 {
154 return chunk_hash(chunk_create(key, strlen(key)));
155 }
156
157 /**
158 * Hashtable equals function
159 */
160 static bool bfd_equals(char *a, char *b)
161 {
162 return streq(a, b);
163 }
164
165 /**
166 * See header.
167 */
168 void backtrace_init()
169 {
170 bfd_init();
171 bfds = hashtable_create((hashtable_hash_t)bfd_hash,
172 (hashtable_equals_t)bfd_equals, 8);
173 bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
174 }
175
176 /**
177 * See header.
178 */
179 void backtrace_deinit()
180 {
181 enumerator_t *enumerator;
182 bfd_entry_t *entry;
183 char *key;
184
185 enumerator = bfds->create_enumerator(bfds);
186 while (enumerator->enumerate(enumerator, &key, &entry))
187 {
188 bfds->remove_at(bfds, enumerator);
189 bfd_entry_destroy(entry);
190 }
191 enumerator->destroy(enumerator);
192
193 bfds->destroy(bfds);
194 bfd_mutex->destroy(bfd_mutex);
195 }
196
197 /**
198 * Find and print information to an address
199 */
200 static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data)
201 {
202 bfd_size_type size;
203 bfd_vma vma;
204 const char *source;
205 const char *function;
206 char fbuf[512] = "", sbuf[512] = "";
207 u_int line;
208
209 if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0)
210 {
211 vma = bfd_get_section_vma(abfd, section);
212 if (data->vma >= vma)
213 {
214 size = bfd_get_section_size(section);
215 if (data->vma < vma + size)
216 {
217 data->found = bfd_find_nearest_line(abfd, section,
218 data->entry->syms, data->vma - vma,
219 &source, &function, &line);
220 if (data->found)
221 {
222 if (source || function)
223 {
224 if (function)
225 {
226 snprintf(fbuf, sizeof(fbuf), "%s%s() ",
227 esc(data->file, TTY_FG_BLUE), function);
228 }
229 if (source)
230 {
231 snprintf(sbuf, sizeof(sbuf), "%s@ %s:%d",
232 esc(data->file, TTY_FG_GREEN), source, line);
233 }
234 println(data->file, " -> %s%s%s", fbuf, sbuf,
235 esc(data->file, TTY_FG_DEF));
236 }
237 }
238 }
239 }
240 }
241 }
242
243 /**
244 * Find a cached bfd entry, create'n'cache if not found
245 */
246 static bfd_entry_t *get_bfd_entry(char *filename)
247 {
248 bool dynamic = FALSE, ok = FALSE;
249 bfd_entry_t *entry;
250 long size;
251
252 /* check cache */
253 entry = bfds->get(bfds, filename);
254 if (entry)
255 {
256 return entry;
257 }
258
259 INIT(entry,
260 .abfd = bfd_openr(filename, NULL),
261 );
262
263 if (!entry->abfd)
264 {
265 free(entry);
266 return NULL;
267 }
268 #ifdef BFD_DECOMPRESS
269 entry->abfd->flags |= BFD_DECOMPRESS;
270 #endif
271 if (bfd_check_format(entry->abfd, bfd_archive) == 0 &&
272 bfd_check_format_matches(entry->abfd, bfd_object, NULL))
273 {
274 if (bfd_get_file_flags(entry->abfd) & HAS_SYMS)
275 {
276 size = bfd_get_symtab_upper_bound(entry->abfd);
277 if (size == 0)
278 {
279 size = bfd_get_dynamic_symtab_upper_bound(entry->abfd);
280 }
281 if (size >= 0)
282 {
283 entry->syms = malloc(size);
284 if (dynamic)
285 {
286 ok = bfd_canonicalize_dynamic_symtab(entry->abfd,
287 entry->syms) >= 0;
288 }
289 else
290 {
291 ok = bfd_canonicalize_symtab(entry->abfd,
292 entry->syms) >= 0;
293 }
294 }
295 }
296 }
297 if (ok)
298 {
299 entry->filename = strdup(filename);
300 bfds->put(bfds, entry->filename, entry);
301 return entry;
302 }
303 bfd_entry_destroy(entry);
304 return NULL;
305 }
306
307 /**
308 * Print the source file with line number to file, libbfd variant
309 */
310 static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
311 {
312 bfd_entry_t *entry;
313 bfd_find_data_t data = {
314 .file = file,
315 .vma = (uintptr_t)ptr,
316 };
317 bool old = FALSE;
318
319 bfd_mutex->lock(bfd_mutex);
320 if (lib && lib->leak_detective)
321 {
322 old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
323 }
324 entry = get_bfd_entry(filename);
325 if (entry)
326 {
327 data.entry = entry;
328 bfd_map_over_sections(entry->abfd, (void*)find_addr, &data);
329 }
330 if (lib && lib->leak_detective)
331 {
332 lib->leak_detective->set_state(lib->leak_detective, old);
333 }
334 bfd_mutex->unlock(bfd_mutex);
335 }
336
337 #else /* !HAVE_BFD_H */
338
339 void backtrace_init() {}
340 void backtrace_deinit() {}
341
342 /**
343 * Print the source file with line number to file, slow addr2line variant
344 */
345 static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
346 {
347 char buf[1024];
348 FILE *output;
349 int c, i = 0;
350
351 #ifdef __APPLE__
352 snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1",
353 filename, base, ptr);
354 #else /* !__APPLE__ */
355 snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
356 #endif /* __APPLE__ */
357
358
359 output = popen(buf, "r");
360 if (output)
361 {
362 while (i < sizeof(buf))
363 {
364 c = getc(output);
365 if (c == '\n' || c == EOF)
366 {
367 buf[i++] = 0;
368 break;
369 }
370 buf[i++] = c;
371 }
372 pclose(output);
373
374 println(file, " -> %s%s%s", esc(file, TTY_FG_GREEN), buf,
375 esc(file, TTY_FG_DEF));
376 }
377 }
378
379 #endif /* HAVE_BFD_H */
380
381 #elif defined(HAVE_DBGHELP) /* && !HAVE_DLADDR */
382
383 #include <dbghelp.h>
384 #include <threading/mutex.h>
385
386 /**
387 * Mutex to access non-thread-safe dbghelp functions
388 */
389 static mutex_t *dbghelp_mutex;
390
391 void backtrace_init()
392 {
393 SymSetOptions(SYMOPT_LOAD_LINES);
394 SymInitialize(GetCurrentProcess(), NULL, TRUE);
395 dbghelp_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
396 }
397
398 void backtrace_deinit()
399 {
400 dbghelp_mutex->destroy(dbghelp_mutex);
401 SymCleanup(GetCurrentProcess());
402 }
403
404 #else /* !HAVE_DLADDR && !HAVE_DBGHELP */
405
406 void backtrace_init() {}
407 void backtrace_deinit() {}
408
409 #endif /* HAVE_DLADDR */
410
411 METHOD(backtrace_t, log_, void,
412 private_backtrace_t *this, FILE *file, bool detailed)
413 {
414 #if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(HAVE_DBGHELP)
415 size_t i;
416 char **strings = NULL;
417
418 println(file, " dumping %d stack frame addresses:", this->frame_count);
419 for (i = 0; i < this->frame_count; i++)
420 {
421 #ifdef HAVE_DLADDR
422 Dl_info info;
423
424 if (dladdr(this->frames[i], &info))
425 {
426 void *ptr = this->frames[i];
427
428 if (strstr(info.dli_fname, ".so"))
429 {
430 ptr = (void*)(this->frames[i] - info.dli_fbase);
431 }
432 if (info.dli_sname)
433 {
434 println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
435 esc(file, TTY_FG_YELLOW), info.dli_fname,
436 esc(file, TTY_FG_DEF), info.dli_fbase,
437 esc(file, TTY_FG_RED), info.dli_sname,
438 esc(file, TTY_FG_DEF), this->frames[i] - info.dli_saddr,
439 this->frames[i]);
440 }
441 else
442 {
443 println(file, " %s%s%s @ %p [%p]",
444 esc(file, TTY_FG_YELLOW), info.dli_fname,
445 esc(file, TTY_FG_DEF), info.dli_fbase, this->frames[i]);
446 }
447 if (detailed && info.dli_fname[0])
448 {
449 print_sourceline(file, (char*)info.dli_fname,
450 ptr, info.dli_fbase);
451 }
452 }
453 else
454 #elif defined(HAVE_DBGHELP)
455 struct {
456 SYMBOL_INFO hdr;
457 char buf[128];
458 } symbol;
459 char filename[MAX_PATH];
460 HINSTANCE module;
461 HANDLE process;
462 DWORD64 displace, frame;
463
464 process = GetCurrentProcess();
465 frame = (uintptr_t)this->frames[i];
466
467 memset(&symbol, 0, sizeof(symbol));
468 symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
469 symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
470
471 dbghelp_mutex->lock(dbghelp_mutex);
472
473 module = (HINSTANCE)SymGetModuleBase64(process, frame);
474
475 if (module && GetModuleFileName(module, filename, sizeof(filename)))
476 {
477 if (SymFromAddr(process, frame, &displace, &symbol.hdr) &&
478 symbol.hdr.Name)
479 {
480 println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
481 esc(file, TTY_FG_YELLOW), filename,
482 esc(file, TTY_FG_DEF), (void*)module,
483 esc(file, TTY_FG_RED), symbol.hdr.Name,
484 esc(file, TTY_FG_DEF), displace,
485 this->frames[i]);
486 }
487 else
488 {
489 println(file, " %s%s%s @ %p [%p]",
490 esc(file, TTY_FG_YELLOW), filename,
491 esc(file, TTY_FG_DEF), (void*)module, this->frames[i]);
492 }
493 if (detailed)
494 {
495 IMAGEHLP_LINE64 line;
496 DWORD off;
497
498 memset(&line, 0, sizeof(line));
499 line.SizeOfStruct = sizeof(line);
500
501 if (SymGetLineFromAddr64(process, frame, &off, &line))
502 {
503
504 println(file, " -> %s%s:%u%s", esc(file, TTY_FG_GREEN),
505 line.FileName, line.LineNumber,
506 esc(file, TTY_FG_DEF));
507 }
508 }
509 }
510 else
511 #endif /* HAVE_DLADDR/HAVE_DBGHELP */
512 {
513 #ifdef HAVE_BACKTRACE
514 if (!strings)
515 {
516 strings = backtrace_symbols(this->frames, this->frame_count);
517 }
518 if (strings)
519 {
520 println(file, " %s", strings[i]);
521 }
522 else
523 #endif /* HAVE_BACKTRACE */
524 {
525 println(file, " %p", this->frames[i]);
526 }
527 }
528 #ifdef HAVE_DBGHELP
529 dbghelp_mutex->unlock(dbghelp_mutex);
530 #endif
531 }
532 free(strings);
533 #else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
534 println(file, "no support for capturing backtraces");
535 #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
536 }
537
538 METHOD(backtrace_t, contains_function, bool,
539 private_backtrace_t *this, char *function[], int count)
540 {
541 #ifdef HAVE_DLADDR
542 int i, j;
543
544 for (i = 0; i< this->frame_count; i++)
545 {
546 Dl_info info;
547
548 if (dladdr(this->frames[i], &info) && info.dli_sname)
549 {
550 for (j = 0; j < count; j++)
551 {
552 if (streq(info.dli_sname, function[j]))
553 {
554 return TRUE;
555 }
556 }
557 }
558 }
559 #elif defined(HAVE_DBGHELP)
560 int i, j;
561 HANDLE process;
562
563 process = GetCurrentProcess();
564
565 dbghelp_mutex->lock(dbghelp_mutex);
566
567 for (i = 0; i < this->frame_count; i++)
568 {
569 struct {
570 SYMBOL_INFO hdr;
571 char buf[128];
572 } symbol;
573
574 memset(&symbol, 0, sizeof(symbol));
575 symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
576 symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
577
578 if (SymFromAddr(process, (DWORD64)this->frames[i], NULL, &symbol.hdr))
579 {
580 if (symbol.hdr.Name)
581 {
582 for (j = 0; j < count; j++)
583 {
584 if (streq(symbol.hdr.Name, function[j]))
585 {
586 dbghelp_mutex->unlock(dbghelp_mutex);
587 return TRUE;
588 }
589 }
590 }
591 }
592 }
593
594 dbghelp_mutex->unlock(dbghelp_mutex);
595 #endif /* HAVE_DLADDR/HAVE_DBGHELP */
596 return FALSE;
597 }
598
599 METHOD(backtrace_t, equals, bool,
600 private_backtrace_t *this, backtrace_t *other_public)
601 {
602 private_backtrace_t *other = (private_backtrace_t*)other_public;
603 int i;
604
605 if (this == other)
606 {
607 return TRUE;
608 }
609 if (this->frame_count != other->frame_count)
610 {
611 return FALSE;
612 }
613 for (i = 0; i < this->frame_count; i++)
614 {
615 if (this->frames[i] != other->frames[i])
616 {
617 return FALSE;
618 }
619 }
620 return TRUE;
621 }
622
623 /**
624 * Frame enumerator
625 */
626 typedef struct {
627 /** implements enumerator_t */
628 enumerator_t public;
629 /** reference to backtrace */
630 private_backtrace_t *bt;
631 /** current position */
632 int i;
633 } frame_enumerator_t;
634
635 METHOD(enumerator_t, frame_enumerate, bool,
636 frame_enumerator_t *this, void **addr)
637 {
638 if (this->i < this->bt->frame_count)
639 {
640 *addr = this->bt->frames[this->i++];
641 return TRUE;
642 }
643 return FALSE;
644 }
645
646 METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
647 private_backtrace_t *this)
648 {
649 frame_enumerator_t *enumerator;
650
651 INIT(enumerator,
652 .public = {
653 .enumerate = (void*)_frame_enumerate,
654 .destroy = (void*)free,
655 },
656 .bt = this,
657 );
658 return &enumerator->public;
659 }
660
661 METHOD(backtrace_t, clone_, backtrace_t*,
662 private_backtrace_t *this)
663 {
664 private_backtrace_t *clone;
665
666 clone = malloc(sizeof(private_backtrace_t) +
667 this->frame_count * sizeof(void*));
668 memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*));
669 clone->frame_count = this->frame_count;
670
671 clone->public = get_methods();
672
673 return &clone->public;
674 }
675
676 METHOD(backtrace_t, destroy, void,
677 private_backtrace_t *this)
678 {
679 free(this);
680 }
681
682 #ifdef HAVE_LIBUNWIND_H
683 #define UNW_LOCAL_ONLY
684 #include <libunwind.h>
685
686 /**
687 * libunwind variant for glibc backtrace()
688 */
689 static inline int backtrace_unwind(void **frames, int count)
690 {
691 unw_context_t context;
692 unw_cursor_t cursor;
693 unw_word_t ip;
694 int depth = 0;
695
696 unw_getcontext(&context);
697 unw_init_local(&cursor, &context);
698 do
699 {
700 unw_get_reg(&cursor, UNW_REG_IP, &ip);
701 frames[depth++] = (void*)ip;
702 }
703 while (depth < count && unw_step(&cursor) > 0);
704
705 return depth;
706 }
707 #endif /* HAVE_UNWIND */
708
709 #ifdef HAVE_DBGHELP
710
711 /**
712 * Windows variant for glibc backtrace()
713 */
714 static inline int backtrace_win(void **frames, int count)
715 {
716 STACKFRAME frame;
717 HANDLE process, thread;
718 DWORD machine;
719 CONTEXT context;
720 int got = 0;
721
722 memset(&frame, 0, sizeof(frame));
723 memset(&context, 0, sizeof(context));
724
725 process = GetCurrentProcess();
726 thread = GetCurrentThread();
727
728 #ifdef __x86_64
729 machine = IMAGE_FILE_MACHINE_AMD64;
730
731 frame.AddrPC.Offset = context.Rip;
732 frame.AddrPC.Mode = AddrModeFlat;
733 frame.AddrStack.Offset = context.Rsp;
734 frame.AddrStack.Mode = AddrModeFlat;
735 frame.AddrFrame.Offset = context.Rbp;
736 frame.AddrFrame.Mode = AddrModeFlat;
737 #else /* x86 */
738 machine = IMAGE_FILE_MACHINE_I386;
739
740 frame.AddrPC.Offset = context.Eip;
741 frame.AddrPC.Mode = AddrModeFlat;
742 frame.AddrStack.Offset = context.Esp;
743 frame.AddrStack.Mode = AddrModeFlat;
744 frame.AddrFrame.Offset = context.Ebp;
745 frame.AddrFrame.Mode = AddrModeFlat;
746 #endif /* x86_64/x86 */
747
748 dbghelp_mutex->lock(dbghelp_mutex);
749
750 RtlCaptureContext(&context);
751
752 while (got < count)
753 {
754 if (!StackWalk64(machine, process, thread, &frame, &context,
755 NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
756 {
757 break;
758 }
759 frames[got++] = (void*)frame.AddrPC.Offset;
760 }
761
762 dbghelp_mutex->unlock(dbghelp_mutex);
763
764 return got;
765 }
766 #endif /* HAVE_DBGHELP */
767
768 /**
769 * Get implementation methods of backtrace_t
770 */
771 static backtrace_t get_methods()
772 {
773 return (backtrace_t) {
774 .log = _log_,
775 .contains_function = _contains_function,
776 .equals = _equals,
777 .clone = _clone_,
778 .create_frame_enumerator = _create_frame_enumerator,
779 .destroy = _destroy,
780 };
781 }
782
783 /**
784 * See header
785 */
786 backtrace_t *backtrace_create(int skip)
787 {
788 private_backtrace_t *this;
789 void *frames[50];
790 int frame_count = 0;
791
792 #ifdef HAVE_LIBUNWIND_H
793 frame_count = backtrace_unwind(frames, countof(frames));
794 #elif defined(HAVE_BACKTRACE)
795 frame_count = backtrace(frames, countof(frames));
796 #elif defined(HAVE_DBGHELP)
797 frame_count = backtrace_win(frames, countof(frames));
798 #endif
799 frame_count = max(frame_count - skip, 0);
800 this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
801 memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
802 this->frame_count = frame_count;
803
804 this->public = get_methods();
805
806 return &this->public;
807 }
808
809 /**
810 * See header
811 */
812 void backtrace_dump(char *label, FILE *file, bool detailed)
813 {
814 backtrace_t *backtrace;
815
816 backtrace = backtrace_create(2);
817
818 if (label)
819 {
820 println(file, "Debug backtrace: %s", label);
821 }
822 backtrace->log(backtrace, file, detailed);
823 backtrace->destroy(backtrace);
824 }