2 * Copyright (C) 2006-2013 Martin Willi
3 * HSR Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2013 revosec AG
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>.
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
20 # include <execinfo.h>
21 #endif /* HAVE_BACKTRACE */
23 # include <winsock2.h>
26 #endif /* HAVE_DBGHELP */
29 #include "backtrace.h"
31 #include <utils/debug.h>
35 /* missing in MinGW */
37 #ifndef GetModuleInformation
38 WINBOOL
K32GetModuleInformation(HANDLE hProcess
, HMODULE hModule
,
39 LPMODULEINFO lpmodinfo
, DWORD cb
);
40 #define GetModuleInformation K32GetModuleInformation
41 #endif /* !GetModuleInformation */
42 #ifndef GetModuleFileNameEx
43 DWORD
K32GetModuleFileNameExA(HANDLE hProcess
, HMODULE hModule
,
44 LPTSTR lpFilename
, DWORD nSize
);
45 #define GetModuleFileNameEx K32GetModuleFileNameExA
46 #endif /* !GetModuleFileNameEx */
50 typedef struct private_backtrace_t private_backtrace_t
;
53 * Private data of an backtrace_t object.
55 struct private_backtrace_t
{
58 * Public backtrace_t interface.
63 * Number of stacks frames obtained in stack_frames
68 * Recorded stack frames.
74 * Forward declaration of method getter
76 static backtrace_t
get_methods();
79 * Write a format string with arguments to a FILE line, if it is NULL to DBG
81 static void println(FILE *file
, char *format
, ...)
86 va_start(args
, format
);
89 vfprintf(file
, format
, args
);
94 vsnprintf(buf
, sizeof(buf
), format
, args
);
95 DBG1(DBG_LIB
, "%s", buf
);
101 * Same as tty_escape_get(), but for a potentially NULL FILE*
103 static inline char* esc(FILE *file
, tty_escape_t escape
)
107 return tty_escape_get(fileno(file
), escape
);
115 #include <threading/mutex.h>
118 * Mutex to access non-thread-safe dbghelp functions
120 static mutex_t
*dbghelp_mutex
;
122 void backtrace_init()
124 SymSetOptions(SYMOPT_LOAD_LINES
);
125 SymInitialize(GetCurrentProcess(), NULL
, TRUE
);
126 dbghelp_mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
129 void backtrace_deinit()
131 dbghelp_mutex
->destroy(dbghelp_mutex
);
132 SymCleanup(GetCurrentProcess());
135 #elif defined(HAVE_DLADDR) || defined(HAVE_BFD_H)
144 #include <collections/hashtable.h>
145 #include <threading/mutex.h>
147 /* interface changes for newer BFD versions, note that older versions declared
148 * some of the new functions as macros but with different arguments */
149 #if HAVE_DECL_BFD_GET_SECTION_FLAGS
150 #define get_section_flags(a, s) bfd_get_section_flags(a, s)
151 #elif HAVE_DECL_BFD_SECTION_FLAGS
152 #define get_section_flags(a, s) bfd_section_flags(s)
154 #error Unknown BFD API
157 #if HAVE_DECL_BFD_GET_SECTION_VMA
158 #define get_section_vma(a, s) bfd_get_section_vma(a, s)
159 #elif HAVE_DECL_BFD_SECTION_VMA
160 #define get_section_vma(a, s) bfd_section_vma(s)
162 #error Unknown BFD API
165 #if HAVE_DECL_BFD_GET_SECTION_SIZE
166 #define get_section_size bfd_get_section_size
167 #elif HAVE_DECL_BFD_SECTION_SIZE
168 #define get_section_size bfd_section_size
170 #error Unknown BFD API
174 * Hashtable-cached bfd handle
177 /** binary file name on disk */
181 /** loaded symbols */
186 * Destroy a bfd_entry
188 static void bfd_entry_destroy(bfd_entry_t
*this)
190 free(this->filename
);
192 bfd_close(this->abfd
);
197 * Data to pass to find_addr()
200 /** used bfd entry */
202 /** backtrace address */
204 /** stream to log to */
206 /** TRUE if complete */
213 static hashtable_t
*bfds
;
215 static mutex_t
*bfd_mutex
;
218 * Hashtable hash function
220 static u_int
bfd_hash(char *key
)
222 return chunk_hash(chunk_create(key
, strlen(key
)));
226 * Hashtable equals function
228 static bool bfd_equals(char *a
, char *b
)
236 void backtrace_init()
239 bfds
= hashtable_create((hashtable_hash_t
)bfd_hash
,
240 (hashtable_equals_t
)bfd_equals
, 8);
241 bfd_mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
247 void backtrace_deinit()
249 enumerator_t
*enumerator
;
253 enumerator
= bfds
->create_enumerator(bfds
);
254 while (enumerator
->enumerate(enumerator
, &key
, &entry
))
256 bfds
->remove_at(bfds
, enumerator
);
257 bfd_entry_destroy(entry
);
259 enumerator
->destroy(enumerator
);
262 bfd_mutex
->destroy(bfd_mutex
);
266 * Find and print information to an address
268 static void find_addr(bfd
*abfd
, asection
*section
, bfd_find_data_t
*data
)
273 const char *function
;
274 char fbuf
[512] = "", sbuf
[512] = "";
277 if (!data
->found
|| (get_section_flags(abfd
, section
) & SEC_ALLOC
) != 0)
279 vma
= get_section_vma(abfd
, section
);
280 if (data
->vma
>= vma
)
282 size
= get_section_size(section
);
283 if (data
->vma
< vma
+ size
)
285 data
->found
= bfd_find_nearest_line(abfd
, section
,
286 data
->entry
->syms
, data
->vma
- vma
,
287 &source
, &function
, &line
);
290 if (source
|| function
)
294 snprintf(fbuf
, sizeof(fbuf
), "%s%s() ",
295 esc(data
->file
, TTY_FG_BLUE
), function
);
299 snprintf(sbuf
, sizeof(sbuf
), "%s@ %s:%d",
300 esc(data
->file
, TTY_FG_GREEN
), source
, line
);
302 println(data
->file
, " -> %s%s%s", fbuf
, sbuf
,
303 esc(data
->file
, TTY_FG_DEF
));
312 * Find a cached bfd entry, create'n'cache if not found
314 static bfd_entry_t
*get_bfd_entry(char *filename
)
316 bool dynamic
= FALSE
, ok
= FALSE
;
321 entry
= bfds
->get(bfds
, filename
);
328 .abfd
= bfd_openr(filename
, NULL
),
336 #ifdef BFD_DECOMPRESS
337 entry
->abfd
->flags
|= BFD_DECOMPRESS
;
339 if (bfd_check_format(entry
->abfd
, bfd_archive
) == 0 &&
340 bfd_check_format_matches(entry
->abfd
, bfd_object
, NULL
))
342 if (bfd_get_file_flags(entry
->abfd
) & HAS_SYMS
)
344 size
= bfd_get_symtab_upper_bound(entry
->abfd
);
347 size
= bfd_get_dynamic_symtab_upper_bound(entry
->abfd
);
352 entry
->syms
= malloc(size
);
355 ok
= bfd_canonicalize_dynamic_symtab(entry
->abfd
,
360 ok
= bfd_canonicalize_symtab(entry
->abfd
,
368 entry
->filename
= strdup(filename
);
369 bfds
->put(bfds
, entry
->filename
, entry
);
372 bfd_entry_destroy(entry
);
377 * Print the source file with line number to file, libbfd variant
379 static void print_sourceline(FILE *file
, char *filename
, void *ptr
, void *base
)
382 bfd_find_data_t data
= {
384 .vma
= (uintptr_t)ptr
,
388 bfd_mutex
->lock(bfd_mutex
);
389 if (lib
&& lib
->leak_detective
)
391 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
393 entry
= get_bfd_entry(filename
);
397 bfd_map_over_sections(entry
->abfd
, (void*)find_addr
, &data
);
399 if (lib
&& lib
->leak_detective
)
401 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
403 bfd_mutex
->unlock(bfd_mutex
);
406 #else /* !HAVE_BFD_H */
408 void backtrace_init() {}
409 void backtrace_deinit() {}
412 * Print the source file with line number to file, slow addr2line variant
414 static void print_sourceline(FILE *file
, char *filename
, void *ptr
, void* base
)
421 snprintf(buf
, sizeof(buf
), "atos -o %s -l %p %p 2>&1 | tail -n1",
422 filename
, base
, ptr
);
423 #else /* !__APPLE__ */
424 snprintf(buf
, sizeof(buf
), "addr2line -e %s %p", filename
, ptr
);
425 #endif /* __APPLE__ */
427 output
= popen(buf
, "r");
430 while (i
< sizeof(buf
))
433 if (c
== '\n' || c
== EOF
)
442 println(file
, " -> %s%s%s", esc(file
, TTY_FG_GREEN
), buf
,
443 esc(file
, TTY_FG_DEF
));
447 #endif /* HAVE_BFD_H */
449 #else /* !HAVE_DLADDR && !HAVE_DBGHELP */
451 void backtrace_init() {}
452 void backtrace_deinit() {}
454 #endif /* HAVE_DLADDR */
456 METHOD(backtrace_t
, log_
, void,
457 private_backtrace_t
*this, FILE *file
, bool detailed
)
459 #if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32)
461 char **strings
= NULL
;
463 println(file
, " dumping %d stack frame addresses:", this->frame_count
);
464 for (i
= 0; i
< this->frame_count
; i
++)
469 if (dladdr(this->frames
[i
], &info
))
471 void *ptr
= this->frames
[i
];
473 if (strstr(info
.dli_fname
, ".so"))
475 ptr
= (void*)(this->frames
[i
] - info
.dli_fbase
);
479 println(file
, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
480 esc(file
, TTY_FG_YELLOW
), info
.dli_fname
,
481 esc(file
, TTY_FG_DEF
), info
.dli_fbase
,
482 esc(file
, TTY_FG_RED
), info
.dli_sname
,
483 esc(file
, TTY_FG_DEF
), this->frames
[i
] - info
.dli_saddr
,
488 println(file
, " %s%s%s @ %p [%p]",
489 esc(file
, TTY_FG_YELLOW
), info
.dli_fname
,
490 esc(file
, TTY_FG_DEF
), info
.dli_fbase
, this->frames
[i
]);
492 if (detailed
&& info
.dli_fname
[0])
494 print_sourceline(file
, (char*)info
.dli_fname
,
495 ptr
, info
.dli_fbase
);
499 #elif defined(HAVE_DBGHELP)
504 char filename
[MAX_PATH
];
507 DWORD64 displace
, frame
;
509 process
= GetCurrentProcess();
510 frame
= (uintptr_t)this->frames
[i
];
512 memset(&symbol
, 0, sizeof(symbol
));
513 symbol
.hdr
.SizeOfStruct
= sizeof(symbol
.hdr
);
514 symbol
.hdr
.MaxNameLen
= sizeof(symbol
.buf
) - 1;
516 dbghelp_mutex
->lock(dbghelp_mutex
);
518 module
= (HINSTANCE
)SymGetModuleBase64(process
, frame
);
520 if (module
&& GetModuleFileName(module
, filename
, sizeof(filename
)))
522 if (SymFromAddr(process
, frame
, &displace
, &symbol
.hdr
))
524 println(file
, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
525 esc(file
, TTY_FG_YELLOW
), filename
,
526 esc(file
, TTY_FG_DEF
), (void*)module
,
527 esc(file
, TTY_FG_RED
), symbol
.hdr
.Name
,
528 esc(file
, TTY_FG_DEF
), displace
,
533 println(file
, " %s%s%s @ %p [%p]",
534 esc(file
, TTY_FG_YELLOW
), filename
,
535 esc(file
, TTY_FG_DEF
), (void*)module
, this->frames
[i
]);
539 IMAGEHLP_LINE64 line
;
542 memset(&line
, 0, sizeof(line
));
543 line
.SizeOfStruct
= sizeof(line
);
545 if (SymGetLineFromAddr64(process
, frame
, &off
, &line
))
548 println(file
, " -> %s%s:%u%s", esc(file
, TTY_FG_GREEN
),
549 line
.FileName
, line
.LineNumber
,
550 esc(file
, TTY_FG_DEF
));
558 char filename
[MAX_PATH
];
560 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
561 this->frames
[i
], &module
) &&
562 GetModuleInformation(GetCurrentProcess(), module
,
563 &info
, sizeof(info
)) &&
564 GetModuleFileNameEx(GetCurrentProcess(), module
,
565 filename
, sizeof(filename
)))
567 println(file
, " %s%s%s @ %p [%p]",
568 esc(file
, TTY_FG_YELLOW
), filename
,
569 esc(file
, TTY_FG_DEF
), info
.lpBaseOfDll
, this->frames
[i
]);
571 print_sourceline(file
, filename
, this->frames
[i
], info
.lpBaseOfDll
);
572 #endif /* HAVE_BFD_H */
575 #endif /* HAVE_DLADDR/HAVE_DBGHELP */
577 #ifdef HAVE_BACKTRACE
580 strings
= backtrace_symbols(this->frames
, this->frame_count
);
584 println(file
, " %s", strings
[i
]);
587 #endif /* HAVE_BACKTRACE */
589 println(file
, " %p", this->frames
[i
]);
593 dbghelp_mutex
->unlock(dbghelp_mutex
);
597 #else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
598 println(file
, "no support for capturing backtraces");
599 #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
602 METHOD(backtrace_t
, contains_function
, bool,
603 private_backtrace_t
*this, char *function
[], int count
)
608 for (i
= 0; i
< this->frame_count
; i
++)
612 if (dladdr(this->frames
[i
], &info
) && info
.dli_sname
)
614 for (j
= 0; j
< count
; j
++)
616 if (streq(info
.dli_sname
, function
[j
]))
623 #elif defined(HAVE_DBGHELP)
627 process
= GetCurrentProcess();
629 dbghelp_mutex
->lock(dbghelp_mutex
);
631 for (i
= 0; i
< this->frame_count
; i
++)
638 memset(&symbol
, 0, sizeof(symbol
));
639 symbol
.hdr
.SizeOfStruct
= sizeof(symbol
.hdr
);
640 symbol
.hdr
.MaxNameLen
= sizeof(symbol
.buf
) - 1;
642 if (SymFromAddr(process
, (DWORD64
)this->frames
[i
], NULL
, &symbol
.hdr
))
644 for (j
= 0; j
< count
; j
++)
646 if (streq(symbol
.hdr
.Name
, function
[j
]))
648 dbghelp_mutex
->unlock(dbghelp_mutex
);
655 dbghelp_mutex
->unlock(dbghelp_mutex
);
656 #endif /* HAVE_DLADDR/HAVE_DBGHELP */
660 METHOD(backtrace_t
, equals
, bool,
661 private_backtrace_t
*this, backtrace_t
*other_public
)
663 private_backtrace_t
*other
= (private_backtrace_t
*)other_public
;
670 if (this->frame_count
!= other
->frame_count
)
674 for (i
= 0; i
< this->frame_count
; i
++)
676 if (this->frames
[i
] != other
->frames
[i
])
688 /** implements enumerator_t */
690 /** reference to backtrace */
691 private_backtrace_t
*bt
;
692 /** current position */
694 } frame_enumerator_t
;
696 METHOD(enumerator_t
, frame_enumerate
, bool,
697 frame_enumerator_t
*this, va_list args
)
701 VA_ARGS_VGET(args
, addr
);
703 if (this->i
< this->bt
->frame_count
)
705 *addr
= this->bt
->frames
[this->i
++];
711 METHOD(backtrace_t
, create_frame_enumerator
, enumerator_t
*,
712 private_backtrace_t
*this)
714 frame_enumerator_t
*enumerator
;
718 .enumerate
= enumerator_enumerate_default
,
719 .venumerate
= _frame_enumerate
,
720 .destroy
= (void*)free
,
724 return &enumerator
->public;
727 METHOD(backtrace_t
, clone_
, backtrace_t
*,
728 private_backtrace_t
*this)
730 private_backtrace_t
*clone
;
732 clone
= malloc(sizeof(private_backtrace_t
) +
733 this->frame_count
* sizeof(void*));
734 memcpy(clone
->frames
, this->frames
, this->frame_count
* sizeof(void*));
735 clone
->frame_count
= this->frame_count
;
737 clone
->public = get_methods();
739 return &clone
->public;
742 METHOD(backtrace_t
, destroy
, void,
743 private_backtrace_t
*this)
748 #ifdef HAVE_LIBUNWIND_H
749 #define UNW_LOCAL_ONLY
750 #include <libunwind.h>
753 * libunwind variant for glibc backtrace()
755 static inline int backtrace_unwind(void **frames
, int count
)
757 unw_context_t context
;
762 unw_getcontext(&context
);
763 unw_init_local(&cursor
, &context
);
766 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
767 frames
[depth
++] = (void*)ip
;
769 while (depth
< count
&& unw_step(&cursor
) > 0);
773 #endif /* HAVE_UNWIND */
778 * Windows dbghelp variant for glibc backtrace()
780 static inline int backtrace_win(void **frames
, int count
)
783 HANDLE process
, thread
;
788 memset(&frame
, 0, sizeof(frame
));
789 memset(&context
, 0, sizeof(context
));
791 process
= GetCurrentProcess();
792 thread
= GetCurrentThread();
795 machine
= IMAGE_FILE_MACHINE_AMD64
;
797 frame
.AddrPC
.Offset
= context
.Rip
;
798 frame
.AddrPC
.Mode
= AddrModeFlat
;
799 frame
.AddrStack
.Offset
= context
.Rsp
;
800 frame
.AddrStack
.Mode
= AddrModeFlat
;
801 frame
.AddrFrame
.Offset
= context
.Rbp
;
802 frame
.AddrFrame
.Mode
= AddrModeFlat
;
804 machine
= IMAGE_FILE_MACHINE_I386
;
806 frame
.AddrPC
.Offset
= context
.Eip
;
807 frame
.AddrPC
.Mode
= AddrModeFlat
;
808 frame
.AddrStack
.Offset
= context
.Esp
;
809 frame
.AddrStack
.Mode
= AddrModeFlat
;
810 frame
.AddrFrame
.Offset
= context
.Ebp
;
811 frame
.AddrFrame
.Mode
= AddrModeFlat
;
812 #endif /* x86_64/x86 */
814 dbghelp_mutex
->lock(dbghelp_mutex
);
816 RtlCaptureContext(&context
);
820 if (!StackWalk64(machine
, process
, thread
, &frame
, &context
,
821 NULL
, SymFunctionTableAccess
, SymGetModuleBase
, NULL
))
825 frames
[got
++] = (void*)frame
.AddrPC
.Offset
;
828 dbghelp_mutex
->unlock(dbghelp_mutex
);
833 #endif /* HAVE_DBGHELP */
836 * Get implementation methods of backtrace_t
838 static backtrace_t
get_methods()
840 return (backtrace_t
) {
842 .contains_function
= _contains_function
,
845 .create_frame_enumerator
= _create_frame_enumerator
,
853 backtrace_t
*backtrace_create(int skip
)
855 private_backtrace_t
*this;
859 #ifdef HAVE_LIBUNWIND_H
860 frame_count
= backtrace_unwind(frames
, countof(frames
));
861 #elif defined(HAVE_BACKTRACE)
862 frame_count
= backtrace(frames
, countof(frames
));
863 #elif defined(HAVE_DBGHELP)
864 frame_count
= backtrace_win(frames
, countof(frames
));
866 frame_count
= CaptureStackBackTrace(skip
, countof(frames
), frames
, NULL
);
869 frame_count
= max(frame_count
- skip
, 0);
870 this = malloc(sizeof(private_backtrace_t
) + frame_count
* sizeof(void*));
871 memcpy(this->frames
, frames
+ skip
, frame_count
* sizeof(void*));
872 this->frame_count
= frame_count
;
874 this->public = get_methods();
876 return &this->public;
882 void backtrace_dump(char *label
, FILE *file
, bool detailed
)
884 backtrace_t
*backtrace
;
886 backtrace
= backtrace_create(2);
890 println(file
, "Debug backtrace: %s", label
);
892 backtrace
->log(backtrace
, file
, detailed
);
893 backtrace
->destroy(backtrace
);