diff options
-rw-r--r-- | configure.in | 9 | ||||
-rw-r--r-- | src/libstrongswan/Makefile.am | 2 | ||||
-rw-r--r-- | src/libstrongswan/library.c | 4 | ||||
-rw-r--r-- | src/libstrongswan/utils/backtrace.c | 300 | ||||
-rw-r--r-- | src/libstrongswan/utils/backtrace.h | 10 |
5 files changed, 296 insertions, 29 deletions
diff --git a/configure.in b/configure.in index 90683c895..c5fd1d3c6 100644 --- a/configure.in +++ b/configure.in @@ -194,6 +194,7 @@ ARG_ENABL_SET([coupling], [enable IKEv2 plugin to couple peer certificates ARG_ENABL_SET([radattr], [enable plugin to inject and process custom RADIUS attributes as IKEv2 client.]) ARG_ENABL_SET([vstr], [enforce using the Vstr string library to replace glibc-like printf hooks.]) ARG_ENABL_SET([monolithic], [build monolithic version of libstrongswan that includes all enabled plugins. Similarly, the plugins of charon are assembled in libcharon.]) +ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.]) dnl ========================= dnl set up compiler and flags @@ -785,6 +786,14 @@ if test x$integrity_test = xtrue; then ) fi +if test x$bfd_backtraces = xtrue; then + AC_HAVE_LIBRARY([bfd],[LIBS="$LIBS"],[AC_MSG_ERROR([binutils libbfd not found!])]) + AC_CHECK_HEADER([bfd.h],[AC_DEFINE([HAVE_BFD_H],,[have binutils bfd.h])], + [AC_MSG_ERROR([binutils bfd.h header not found!])]) + BFDLIB="-lbfd" + AC_SUBST(BFDLIB) +fi + AM_CONDITIONAL(USE_DEV_HEADERS, [test "x$dev_headers" != xno]) if test x$dev_headers = xyes; then dev_headers="$includedir/strongswan" diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 4046f65a1..dc849d6f2 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -63,7 +63,7 @@ endif library.lo : $(top_builddir)/config.status -libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) +libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) INCLUDES = -I$(top_srcdir)/src/libstrongswan AM_CFLAGS = \ diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index cd6a41f44..ed3f52027 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -23,6 +23,7 @@ #include <utils/identification.h> #include <utils/host.h> #include <utils/hashtable.h> +#include <utils/backtrace.h> #include <selectors/traffic_selector.h> #define CHECKSUM_LIBRARY IPSEC_LIB_DIR"/libchecksum.so" @@ -88,6 +89,7 @@ void library_deinit() } threads_deinit(); + backtrace_deinit(); free(this); lib = NULL; @@ -146,6 +148,7 @@ bool library_init(char *settings) ); lib = &this->public; + backtrace_init(); threads_init(); #ifdef LEAK_DETECTIVE @@ -204,6 +207,7 @@ bool library_init(char *settings) return FALSE; #endif /* INTEGRITY_TEST */ } + return TRUE; } diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c index db10c7844..b41712926 100644 --- a/src/libstrongswan/utils/backtrace.c +++ b/src/libstrongswan/utils/backtrace.c @@ -50,6 +50,276 @@ struct private_backtrace_t { void *frames[]; }; +#ifdef HAVE_DLADDR +#ifdef HAVE_BFD_H + +#include <bfd.h> +#include <utils/hashtable.h> +#include <threading/mutex.h> + +/** + * Hashtable-cached bfd handle + */ +typedef struct { + /** binary file name on disk */ + char *filename; + /** bfd handle */ + bfd *abfd; + /** loaded symbols */ + asymbol **syms; +} bfd_entry_t; + +/** + * Destroy a bfd_entry + */ +static void bfd_entry_destroy(bfd_entry_t *this) +{ + free(this->filename); + free(this->syms); + bfd_close(this->abfd); + free(this); +} + +/** + * Data to pass to find_addr() + */ +typedef struct { + /** used bfd entry */ + bfd_entry_t *entry; + /** backtrace address */ + bfd_vma vma; + /** stream to log to */ + FILE *file; + /** TRUE if complete */ + bool found; +} bfd_find_data_t; + +/** + * bfd entry cache + */ +static hashtable_t *bfds; + +static mutex_t *bfd_mutex; + +/** + * Hashtable hash function + */ +static u_int bfd_hash(char *key) +{ + return chunk_hash(chunk_create(key, strlen(key))); +} + +/** + * Hashtable equals function + */ +static bool bfd_equals(char *a, char *b) +{ + return streq(a, b); +} + +/** + * See header. + */ +void backtrace_init() +{ + bfd_init(); + bfds = hashtable_create((hashtable_hash_t)bfd_hash, + (hashtable_equals_t)bfd_equals, 8); + bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT); +} + +/** + * See header. + */ +void backtrace_deinit() +{ + enumerator_t *enumerator; + bfd_entry_t *entry; + char *key; + + enumerator = bfds->create_enumerator(bfds); + while (enumerator->enumerate(enumerator, &key, &entry)) + { + bfds->remove_at(bfds, enumerator); + bfd_entry_destroy(entry); + } + enumerator->destroy(enumerator); + + bfds->destroy(bfds); + bfd_mutex->destroy(bfd_mutex); +} + +/** + * Find and print information to an address + */ +static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data) +{ + bfd_size_type size; + bfd_vma vma; + const char *source; + const char *function; + u_int line; + + if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0) + { + vma = bfd_get_section_vma(abfd, section); + if (data->vma >= vma) + { + size = bfd_get_section_size(section); + if (data->vma < vma + size) + { + data->found = bfd_find_nearest_line(abfd, section, + data->entry->syms, data->vma - vma, + &source, &function, &line); + if (data->found) + { + if (source || function) + { + fprintf(data->file, " -> "); + if (function) + { + fprintf(data->file, "\e[34m%s() ", function); + } + if (source) + { + fprintf(data->file, "\e[32m@ %s:%d", source, line); + } + fprintf(data->file, "\e[0m\n"); + } + } + } + } + } +} + +/** + * Find a cached bfd entry, create'n'cache if not found + */ +static bfd_entry_t *get_bfd_entry(char *filename) +{ + bool dynamic = FALSE, ok = FALSE; + bfd_entry_t *entry; + long size; + + /* check cache */ + entry = bfds->get(bfds, filename); + if (entry) + { + return entry; + } + + INIT(entry, + .abfd = bfd_openr(filename, NULL), + ); + + if (!entry->abfd) + { + free(entry); + return NULL; + } + entry->abfd->flags |= BFD_DECOMPRESS; + if (bfd_check_format(entry->abfd, bfd_archive) == 0 && + bfd_check_format_matches(entry->abfd, bfd_object, NULL)) + { + if (bfd_get_file_flags(entry->abfd) & HAS_SYMS) + { + size = bfd_get_symtab_upper_bound(entry->abfd); + if (size == 0) + { + size = bfd_get_dynamic_symtab_upper_bound(entry->abfd); + } + if (size >= 0) + { + entry->syms = malloc(size); + if (dynamic) + { + ok = bfd_canonicalize_dynamic_symtab(entry->abfd, + entry->syms) >= 0; + } + else + { + ok = bfd_canonicalize_symtab(entry->abfd, + entry->syms) >= 0; + } + } + } + } + if (ok) + { + entry->filename = strdup(filename); + bfds->put(bfds, entry->filename, entry); + return entry; + } + bfd_entry_destroy(entry); + return NULL; +} + +/** + * Print the source file with line number to file, libbfd variant + */ +static void print_sourceline(FILE *file, char *filename, void *ptr) +{ + bfd_entry_t *entry; + bfd_find_data_t data = { + .file = file, + .vma = (bfd_vma)ptr, + }; + bool old = FALSE; + + bfd_mutex->lock(bfd_mutex); + if (lib->leak_detective) + { + old = lib->leak_detective->set_state(lib->leak_detective, FALSE); + } + entry = get_bfd_entry(filename); + if (entry) + { + data.entry = entry; + bfd_map_over_sections(entry->abfd, (void*)find_addr, &data); + } + if (lib->leak_detective) + { + lib->leak_detective->set_state(lib->leak_detective, old); + } + bfd_mutex->unlock(bfd_mutex); +} + +#else /* !HAVE_BFD_H */ + +void backtrace_init() {} +void backtrace_deinit() {} + +/** + * Print the source file with line number to file, slow addr2line variant + */ +static void print_sourceline(FILE *file, char *filename, void *ptr) +{ + char cmd[1024]; + FILE *output; + int c; + + snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", filename, ptr); + output = popen(cmd, "r"); + if (output) + { + fprintf(file, " -> \e[32m"); + while (TRUE) + { + c = getc(output); + if (c == '\n' || c == EOF) + { + break; + } + fputc(c, file); + } + pclose(output); + fprintf(file, "\e[0m\n"); + } +} + +#endif /* HAVE_BFD_H */ +#endif /* HAVE_DLADDR */ + METHOD(backtrace_t, log_, void, private_backtrace_t *this, FILE *file, bool detailed) { @@ -67,9 +337,6 @@ METHOD(backtrace_t, log_, void, if (dladdr(this->frames[i], &info)) { - char cmd[1024]; - FILE *output; - int c; void *ptr = this->frames[i]; if (strstr(info.dli_fname, ".so")) @@ -89,37 +356,14 @@ METHOD(backtrace_t, log_, void, } if (detailed) { - fprintf(file, " -> \e[32m"); - snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", - info.dli_fname, ptr); - output = popen(cmd, "r"); - if (output) - { - while (TRUE) - { - c = getc(output); - if (c == '\n' || c == EOF) - { - break; - } - fputc(c, file); - } - pclose(output); - } - else - { - #endif /* HAVE_DLADDR */ - fprintf(file, " %s\n", strings[i]); - #ifdef HAVE_DLADDR - } - fprintf(file, "\n\e[0m"); + print_sourceline(file, (char*)info.dli_fname, ptr); } } else +#endif /* HAVE_DLADDR */ { fprintf(file, " %s\n", strings[i]); } -#endif /* HAVE_DLADDR */ } free (strings); #else /* !HAVE_BACKTRACE */ diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h index 32d09043f..aeeba4dd6 100644 --- a/src/libstrongswan/utils/backtrace.h +++ b/src/libstrongswan/utils/backtrace.h @@ -86,4 +86,14 @@ backtrace_t *backtrace_create(int skip); */ void backtrace_dump(char *label, FILE *file, bool detailed); +/** + * Initialize backtracing framework. + */ +void backtrace_init(); + +/** + * Deinitialize backtracing framework. + */ +void backtrace_deinit(); + #endif /** BACKTRACE_H_ @}*/ |