From 1bfb5d87e5e2589cb18ef7f35da012b308ff78bf Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 9 Oct 2012 18:20:45 +0000 Subject: [PATCH] Add support for tracing through shared libraries. * configure.ac: Check for link.h and dl_iterate_phdr. * elf.c: #include if system has dl_iterate_phdr. #undef ELF macros before #defining them. (dl_phdr_info, dl_iterate_phdr): Define if system does not have dl_iterate_phdr. (struct elf_syminfo_data): Add next field. (elf_initialize_syminfo): Initialize next field. (elf_add_syminfo_data): New static function. (elf_add): New static function, broken out of backtrace_initialize. Call backtrace_dwarf_add instead of backtrace_dwarf_initialize. (struct phdr_data): Define. (phdr_callback): New static function. (backtrace_initialize): Call elf_add. * dwarf.c (struct dwarf_data): Add next and base_address fields. (add_unit_addr): Add base_address parameter. Change all callers. (add_unit_ranges, build_address_map): Likewise. (add_line): Add ddata parameter. Change all callers. (read_line_program, add_function_range): Likewise. (dwarf_lookup_pc): New static function, broken out of dwarf_fileline. (dwarf_fileline): Call dwarf_lookup_pc. (build_dwarf_data): New static function. (backtrace_dwarf_add): New function. (backtrace_dwarf_initialize): Remove. * internal.h (backtrace_dwarf_initialize): Don't declare. (backtrace_dwarf_add): Declare. * configure, config.h.in: Rebuild. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@192267 138bc75d-0d04-0410-961f-82ee72b054a4 --- libbacktrace/ChangeLog | 34 +++++- libbacktrace/config.h.in | 6 + libbacktrace/configure | 47 ++++++++ libbacktrace/configure.ac | 18 +++ libbacktrace/dwarf.c | 275 ++++++++++++++++++++++++++++++++++++---------- libbacktrace/elf.c | 272 ++++++++++++++++++++++++++++++++++++++++----- libbacktrace/internal.h | 33 +++--- 7 files changed, 579 insertions(+), 106 deletions(-) diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 1641056..f9d6862 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,7 +1,39 @@ +2012-10-09 Ian Lance Taylor + + Add support for tracing through shared libraries. + * configure.ac: Check for link.h and dl_iterate_phdr. + * elf.c: #include if system has dl_iterate_phdr. #undef + ELF macros before #defining them. + (dl_phdr_info, dl_iterate_phdr): Define if system does not have + dl_iterate_phdr. + (struct elf_syminfo_data): Add next field. + (elf_initialize_syminfo): Initialize next field. + (elf_add_syminfo_data): New static function. + (elf_add): New static function, broken out of + backtrace_initialize. Call backtrace_dwarf_add instead of + backtrace_dwarf_initialize. + (struct phdr_data): Define. + (phdr_callback): New static function. + (backtrace_initialize): Call elf_add. + * dwarf.c (struct dwarf_data): Add next and base_address fields. + (add_unit_addr): Add base_address parameter. Change all callers. + (add_unit_ranges, build_address_map): Likewise. + (add_line): Add ddata parameter. Change all callers. + (read_line_program, add_function_range): Likewise. + (dwarf_lookup_pc): New static function, broken out of + dwarf_fileline. + (dwarf_fileline): Call dwarf_lookup_pc. + (build_dwarf_data): New static function. + (backtrace_dwarf_add): New function. + (backtrace_dwarf_initialize): Remove. + * internal.h (backtrace_dwarf_initialize): Don't declare. + (backtrace_dwarf_add): Declare. + * configure, config.h.in: Rebuild. + 2012-10-04 Gerald Pfeifer * btest.c (f23): Avoid uninitialized variable warning. - + 2012-10-04 Ian Lance Taylor * dwarf.c: If the system header files do not declare strnlen, diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in index 656c2ee..ba564a8 100644 --- a/libbacktrace/config.h.in +++ b/libbacktrace/config.h.in @@ -10,6 +10,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Define if dl_iterate_phdr is available. */ +#undef HAVE_DL_ITERATE_PHDR + /* Define to 1 if you have the fcntl function */ #undef HAVE_FCNTL @@ -19,6 +22,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINK_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H diff --git a/libbacktrace/configure b/libbacktrace/configure index 8e2ea41..8d34856 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -12182,6 +12182,53 @@ if test "$ALLOC_FILE" = "alloc.lo"; then fi +# Check for dl_iterate_phdr. +for ac_header in link.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "link.h" "ac_cv_header_link_h" "$ac_includes_default" +if test "x$ac_cv_header_link_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LINK_H 1 +_ACEOF + +fi + +done + +if test "$ac_cv_header_link_h" = "no"; then + have_dl_iterate_phdr=no +else + if test -n "${with_target_subdir}"; then + # When built as a GCC target library, we can't do a link test. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "dl_iterate_phdr" >/dev/null 2>&1; then : + have_dl_iterate_phdr=yes +else + have_dl_iterate_phdr=no +fi +rm -f conftest* + + else + ac_fn_c_check_func "$LINENO" "dl_iterate_phdr" "ac_cv_func_dl_iterate_phdr" +if test "x$ac_cv_func_dl_iterate_phdr" = x""yes; then : + have_dl_iterate_phdr=yes +else + have_dl_iterate_phdr=no +fi + + fi +fi +if test "$have_dl_iterate_phdr" = "yes"; then + +$as_echo "#define HAVE_DL_ITERATE_PHDR 1" >>confdefs.h + +fi + # Check for the fcntl function. if test -n "${with_target_subdir}"; then case "${host}" in diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index 1ea8860..083a086 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -226,6 +226,24 @@ if test "$ALLOC_FILE" = "alloc.lo"; then fi AC_SUBST(BACKTRACE_USES_MALLOC) +# Check for dl_iterate_phdr. +AC_CHECK_HEADERS(link.h) +if test "$ac_cv_header_link_h" = "no"; then + have_dl_iterate_phdr=no +else + if test -n "${with_target_subdir}"; then + # When built as a GCC target library, we can't do a link test. + AC_EGREP_HEADER([dl_iterate_phdr], [link.h], [have_dl_iterate_phdr=yes], + [have_dl_iterate_phdr=no]) + else + AC_CHECK_FUNC([dl_iterate_phdr], [have_dl_iterate_phdr=yes], + [have_dl_iterate_phdr=no]) + fi +fi +if test "$have_dl_iterate_phdr" = "yes"; then + AC_DEFINE(HAVE_DL_ITERATE_PHDR, 1, [Define if dl_iterate_phdr is available.]) +fi + # Check for the fcntl function. if test -n "${with_target_subdir}"; then case "${host}" in diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c index 4e13fc54..1b28a8f 100644 --- a/libbacktrace/dwarf.c +++ b/libbacktrace/dwarf.c @@ -333,6 +333,10 @@ struct unit_addrs_vector struct dwarf_data { + /* The data for the next file we know about. */ + struct dwarf_data *next; + /* The base address for this file. */ + uintptr_t base_address; /* A sorted list of address ranges. */ struct unit_addrs *addrs; /* Number of address ranges in list. */ @@ -831,12 +835,18 @@ function_addrs_search (const void *vkey, const void *ventry) success, 0 on failure. */ static int -add_unit_addr (struct backtrace_state *state, struct unit_addrs addrs, +add_unit_addr (struct backtrace_state *state, uintptr_t base_address, + struct unit_addrs addrs, backtrace_error_callback error_callback, void *data, struct unit_addrs_vector *vec) { struct unit_addrs *p; + /* Add in the base address of the module here, so that we can look + up the PC directly. */ + addrs.low += base_address; + addrs.high += base_address; + /* Try to merge with the last entry. */ if (vec->count > 0) { @@ -1156,9 +1166,10 @@ lookup_abbrev (struct abbrevs *abbrevs, uint64_t code, 1 on success, 0 on failure. */ static int -add_unit_ranges (struct backtrace_state *state, struct unit *u, - uint64_t ranges, uint64_t base, int is_bigendian, - const unsigned char *dwarf_ranges, size_t dwarf_ranges_size, +add_unit_ranges (struct backtrace_state *state, uintptr_t base_address, + struct unit *u, uint64_t ranges, uint64_t base, + int is_bigendian, const unsigned char *dwarf_ranges, + size_t dwarf_ranges_size, backtrace_error_callback error_callback, void *data, struct unit_addrs_vector *addrs) { @@ -1202,7 +1213,8 @@ add_unit_ranges (struct backtrace_state *state, struct unit *u, a.low = low + base; a.high = high + base; a.u = u; - if (!add_unit_addr (state, a, error_callback, data, addrs)) + if (!add_unit_addr (state, base_address, a, error_callback, data, + addrs)) return 0; } } @@ -1218,7 +1230,7 @@ add_unit_ranges (struct backtrace_state *state, struct unit *u, on success, 0 on failure. */ static int -build_address_map (struct backtrace_state *state, +build_address_map (struct backtrace_state *state, uintptr_t base_address, const unsigned char *dwarf_info, size_t dwarf_info_size, const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size, const unsigned char *dwarf_ranges, size_t dwarf_ranges_size, @@ -1417,9 +1429,10 @@ build_address_map (struct backtrace_state *state, if (have_ranges) { - if (!add_unit_ranges (state, u, ranges, lowpc, is_bigendian, - dwarf_ranges, dwarf_ranges_size, - error_callback, data, addrs)) + if (!add_unit_ranges (state, base_address, u, ranges, lowpc, + is_bigendian, dwarf_ranges, + dwarf_ranges_size, error_callback, data, + addrs)) { free_abbrevs (state, &u->abbrevs, error_callback, data); backtrace_free (state, u, sizeof *u, error_callback, data); @@ -1434,7 +1447,8 @@ build_address_map (struct backtrace_state *state, a.high = highpc; a.u = u; - if (!add_unit_addr (state, a, error_callback, data, addrs)) + if (!add_unit_addr (state, base_address, a, error_callback, data, + addrs)) { free_abbrevs (state, &u->abbrevs, error_callback, data); backtrace_free (state, u, sizeof *u, error_callback, data); @@ -1463,8 +1477,9 @@ build_address_map (struct backtrace_state *state, building. Returns 1 on success, 0 on failure. */ static int -add_line (struct backtrace_state *state, uintptr_t pc, const char *filename, - int lineno, backtrace_error_callback error_callback, void *data, +add_line (struct backtrace_state *state, struct dwarf_data *ddata, + uintptr_t pc, const char *filename, int lineno, + backtrace_error_callback error_callback, void *data, struct line_vector *vec) { struct line *ln; @@ -1484,7 +1499,10 @@ add_line (struct backtrace_state *state, uintptr_t pc, const char *filename, if (ln == NULL) return 0; - ln->pc = pc; + /* Add in the base address here, so that we can look up the PC + directly. */ + ln->pc = pc + ddata->base_address; + ln->filename = filename; ln->lineno = lineno; @@ -1672,9 +1690,9 @@ read_line_header (struct backtrace_state *state, struct unit *u, success, 0 on failure. */ static int -read_line_program (struct backtrace_state *state, struct unit *u, - const struct line_header *hdr, struct dwarf_buf *line_buf, - struct line_vector *vec) +read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, const struct line_header *hdr, + struct dwarf_buf *line_buf, struct line_vector *vec) { uint64_t address; unsigned int op_index; @@ -1706,8 +1724,8 @@ read_line_program (struct backtrace_state *state, struct unit *u, / hdr->max_ops_per_insn); op_index = (op_index + advance) % hdr->max_ops_per_insn; lineno += hdr->line_base + (int) (op % hdr->line_range); - add_line (state, address, filename, lineno, line_buf->error_callback, - line_buf->data, vec); + add_line (state, ddata, address, filename, lineno, + line_buf->error_callback, line_buf->data, vec); } else if (op == DW_LNS_extended_op) { @@ -1795,7 +1813,7 @@ read_line_program (struct backtrace_state *state, struct unit *u, switch (op) { case DW_LNS_copy: - add_line (state, address, filename, lineno, + add_line (state, ddata, address, filename, lineno, line_buf->error_callback, line_buf->data, vec); break; case DW_LNS_advance_pc: @@ -1923,7 +1941,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr)) goto fail; - if (!read_line_program (state, u, hdr, &line_buf, &vec)) + if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec)) goto fail; if (line_buf.reported_underflow) @@ -2076,13 +2094,18 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, success, 0 on error. */ static int -add_function_range (struct backtrace_state *state, struct function *function, - uint64_t lowpc, uint64_t highpc, +add_function_range (struct backtrace_state *state, struct dwarf_data *ddata, + struct function *function, uint64_t lowpc, uint64_t highpc, backtrace_error_callback error_callback, void *data, struct function_vector *vec) { struct function_addrs *p; + /* Add in the base address here, so that we can look up the PC + directly. */ + lowpc += ddata->base_address; + highpc += ddata->base_address; + if (vec->count > 0) { p = (struct function_addrs *) vec->vec.base + vec->count - 1; @@ -2153,8 +2176,8 @@ add_function_ranges (struct backtrace_state *state, struct dwarf_data *ddata, base = high; else { - if (!add_function_range (state, function, low + base, high + base, - error_callback, data, vec)) + if (!add_function_range (state, ddata, function, low + base, + high + base, error_callback, data, vec)) return 0; } } @@ -2364,7 +2387,7 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, { if (highpc_is_relative) highpc += lowpc; - if (!add_function_range (state, function, lowpc, highpc, + if (!add_function_range (state, ddata, function, lowpc, highpc, error_callback, data, vec)) return 0; } @@ -2522,15 +2545,17 @@ report_inlined_functions (uintptr_t pc, struct function *function, return 0; } -/* Return the file/line information for a PC using the DWARF mapping - we built earlier. */ +/* Look for a PC in the DWARF mapping for one module. On success, + call CALLBACK and return whatever it returns. On error, call + ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found, + 0 if not. */ static int -dwarf_fileline (struct backtrace_state *state, uintptr_t pc, - backtrace_full_callback callback, - backtrace_error_callback error_callback, void *data) +dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, + uintptr_t pc, backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data, + int *found) { - struct dwarf_data *ddata; struct unit_addrs *entry; struct unit *u; int new_data; @@ -2542,14 +2567,17 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, int lineno; int ret; - ddata = (struct dwarf_data *) state->fileline_data; + *found = 1; /* Find an address range that includes PC. */ entry = bsearch (&pc, ddata->addrs, ddata->addrs_count, sizeof (struct unit_addrs), unit_addrs_search); if (entry == NULL) - return callback (data, pc, NULL, 0, NULL); + { + *found = 0; + return 0; + } /* If there are multiple ranges that contain PC, use the last one, in order to produce predictable results. If we assume that all @@ -2656,7 +2684,8 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, try again to see if there is a better compilation unit for this PC. */ if (new_data) - dwarf_fileline (state, pc, callback, error_callback, data); + return dwarf_lookup_pc (state, ddata, pc, callback, error_callback, + data, found); return callback (data, pc, NULL, 0, NULL); } @@ -2705,39 +2734,93 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, return callback (data, pc, filename, lineno, function->name); } -/* Build our data structures from the .debug_info and .debug_line - sections. Set *FILELINE_FN and *FILELINE_DATA. Return 1 on - success, 0 on failure. */ -int -backtrace_dwarf_initialize (struct backtrace_state *state, - const unsigned char *dwarf_info, - size_t dwarf_info_size, - const unsigned char *dwarf_line, - size_t dwarf_line_size, - const unsigned char *dwarf_abbrev, - size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, - const unsigned char *dwarf_str, - size_t dwarf_str_size, - int is_bigendian, - backtrace_error_callback error_callback, - void *data, fileline *fileline_fn) +/* Return the file/line information for a PC using the DWARF mapping + we built earlier. */ + +static int +dwarf_fileline (struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) +{ + struct dwarf_data *ddata; + int found; + int ret; + + if (!state->threaded) + { + for (ddata = (struct dwarf_data *) state->fileline_data; + ddata != NULL; + ddata = ddata->next) + { + ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback, + data, &found); + if (ret != 0 || found) + return ret; + } + } + else + { + struct dwarf_data **pp; + + pp = (struct dwarf_data **) &state->fileline_data; + while (1) + { + ddata = *pp; + /* Atomic load. */ + while (!__sync_bool_compare_and_swap (pp, ddata, ddata)) + ddata = *pp; + + if (ddata == NULL) + break; + + ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback, + data, &found); + if (ret != 0 || found) + return ret; + + pp = &ddata->next; + } + } + + /* FIXME: See if any libraries have been dlopen'ed. */ + + return callback (data, pc, NULL, 0, NULL); +} + +/* Initialize our data structures from the DWARF debug info for a + file. Return NULL on failure. */ + +static struct dwarf_data * +build_dwarf_data (struct backtrace_state *state, + uintptr_t base_address, + const unsigned char *dwarf_info, + size_t dwarf_info_size, + const unsigned char *dwarf_line, + size_t dwarf_line_size, + const unsigned char *dwarf_abbrev, + size_t dwarf_abbrev_size, + const unsigned char *dwarf_ranges, + size_t dwarf_ranges_size, + const unsigned char *dwarf_str, + size_t dwarf_str_size, + int is_bigendian, + backtrace_error_callback error_callback, + void *data) { struct unit_addrs_vector addrs_vec; struct unit_addrs *addrs; size_t addrs_count; struct dwarf_data *fdata; - if (!build_address_map (state, dwarf_info, dwarf_info_size, dwarf_abbrev, - dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size, - dwarf_str, dwarf_str_size, is_bigendian, - error_callback, data, &addrs_vec)) - return 0; + if (!build_address_map (state, base_address, dwarf_info, dwarf_info_size, + dwarf_abbrev, dwarf_abbrev_size, dwarf_ranges, + dwarf_ranges_size, dwarf_str, dwarf_str_size, + is_bigendian, error_callback, data, &addrs_vec)) + return NULL; if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data)) - return 0; + return NULL; addrs = (struct unit_addrs *) addrs_vec.vec.base; addrs_count = addrs_vec.count; qsort (addrs, addrs_count, sizeof (struct unit_addrs), unit_addrs_compare); @@ -2746,8 +2829,10 @@ backtrace_dwarf_initialize (struct backtrace_state *state, backtrace_alloc (state, sizeof (struct dwarf_data), error_callback, data)); if (fdata == NULL) - return 0; + return NULL; + fdata->next = NULL; + fdata->base_address = base_address; fdata->addrs = addrs; fdata->addrs_count = addrs_count; fdata->dwarf_info = dwarf_info; @@ -2761,7 +2846,77 @@ backtrace_dwarf_initialize (struct backtrace_state *state, fdata->is_bigendian = is_bigendian; memset (&fdata->fvec, 0, sizeof fdata->fvec); - state->fileline_data = fdata; + return fdata; +} + +/* Build our data structures from the DWARF sections for a module. + Set FILELINE_FN and STATE->FILELINE_DATA. Return 1 on success, 0 + on failure. */ + +int +backtrace_dwarf_add (struct backtrace_state *state, + uintptr_t base_address, + const unsigned char *dwarf_info, + size_t dwarf_info_size, + const unsigned char *dwarf_line, + size_t dwarf_line_size, + const unsigned char *dwarf_abbrev, + size_t dwarf_abbrev_size, + const unsigned char *dwarf_ranges, + size_t dwarf_ranges_size, + const unsigned char *dwarf_str, + size_t dwarf_str_size, + int is_bigendian, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + struct dwarf_data *fdata; + + fdata = build_dwarf_data (state, base_address, dwarf_info, dwarf_info_size, + dwarf_line, dwarf_line_size, dwarf_abbrev, + dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size, + dwarf_str, dwarf_str_size, is_bigendian, + error_callback, data); + if (fdata == NULL) + return 0; + + if (!state->threaded) + { + struct dwarf_data **pp; + + for (pp = (struct dwarf_data **) &state->fileline_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = fdata; + } + else + { + while (1) + { + struct dwarf_data **pp; + + pp = (struct dwarf_data **) &state->fileline_data; + + while (1) + { + struct dwarf_data *p; + + /* Atomic load. */ + p = *pp; + while (!__sync_bool_compare_and_swap (pp, p, p)) + p = *pp; + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, fdata)) + break; + } + } *fileline_fn = dwarf_fileline; diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index fd0ecd7..48e8884 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -36,9 +36,36 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include +#ifdef HAVE_DL_ITERATE_PHDR +#include +#endif + #include "backtrace.h" #include "internal.h" +#ifndef HAVE_DL_ITERATE_PHDR + +/* Dummy version of dl_iterate_phdr for systems that don't have it. */ + +#define dl_phdr_info x_dl_phdr_info +#define dl_iterate_phdr x_dl_iterate_phdr + +struct dl_phdr_info +{ + uintptr_t dlpi_addr; + const char *dlpi_name; +}; + +static int +dl_iterate_phdr (int (*callback) (struct dl_phdr_info *, + size_t, void *) ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + return 0; +} + +#endif /* ! defined (HAVE_DL_ITERATE_PHDR) */ + /* The configure script must tell us whether we are 32-bit or 64-bit ELF. We could make this code test and support either possibility, but there is no point. This code only works for the currently @@ -49,6 +76,33 @@ POSSIBILITY OF SUCH DAMAGE. */ #error "Unknown BACKTRACE_ELF_SIZE" #endif +/* might #include which might define our constants + with slightly different values. Undefine them to be safe. */ + +#undef EI_NIDENT +#undef EI_MAG0 +#undef EI_MAG1 +#undef EI_MAG2 +#undef EI_MAG3 +#undef EI_CLASS +#undef EI_DATA +#undef EI_VERSION +#undef ELF_MAG0 +#undef ELF_MAG1 +#undef ELF_MAG2 +#undef ELF_MAG3 +#undef ELFCLASS32 +#undef ELFCLASS64 +#undef ELFDATA2LSB +#undef ELFDATA2MSB +#undef EV_CURRENT +#undef SHN_LORESERVE +#undef SHN_XINDEX +#undef SHT_SYMTAB +#undef SHT_STRTAB +#undef SHT_DYNSYM +#undef STT_FUNC + /* Basic types. */ typedef uint16_t Elf_Half; @@ -214,6 +268,8 @@ struct elf_symbol struct elf_syminfo_data { + /* Symbols for the next module. */ + struct elf_syminfo_data *next; /* The ELF symbols, sorted by address. */ struct elf_symbol *symbols; /* The number of symbols. */ @@ -337,12 +393,58 @@ elf_initialize_syminfo (struct backtrace_state *state, qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol), elf_symbol_compare); + sdata->next = NULL; sdata->symbols = elf_symbols; sdata->count = elf_symbol_count; return 1; } +/* Add EDATA to the list in STATE. */ + +static void +elf_add_syminfo_data (struct backtrace_state *state, + struct elf_syminfo_data *edata) +{ + if (!state->threaded) + { + struct elf_syminfo_data **pp; + + for (pp = (struct elf_syminfo_data **) &state->syminfo_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = edata; + } + else + { + while (1) + { + struct elf_syminfo_data **pp; + + pp = (struct elf_syminfo_data **) &state->syminfo_data; + + while (1) + { + struct elf_syminfo_data *p; + + /* Atomic load. */ + p = *pp; + while (!__sync_bool_compare_and_swap (pp, p, p)) + p = *pp; + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, edata)) + break; + } + } +} + /* Return the symbol name and value for a PC. */ static void @@ -364,14 +466,12 @@ elf_syminfo (struct backtrace_state *state, uintptr_t pc, callback (data, pc, sym->name, sym->address); } -/* Initialize the backtrace data we need from an ELF executable. At - the ELF level, all we need to do is find the debug info - sections. */ +/* Add the backtrace data for one ELF file. */ -int -backtrace_initialize (struct backtrace_state *state, int descriptor, - backtrace_error_callback error_callback, - void *data, fileline *fileline_fn) +static int +elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym, int *found_dwarf) { struct backtrace_view ehdr_view; Elf_Ehdr ehdr; @@ -400,6 +500,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, struct backtrace_view debug_view; int debug_view_valid; + *found_sym = 0; + *found_dwarf = 0; + shdrs_view_valid = 0; names_view_valid = 0; symtab_view_valid = 0; @@ -516,6 +619,8 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, dynsym_shndx = 0; memset (sections, 0, sizeof sections); + + /* Look for the symbol table. */ for (i = 1; i < shnum; ++i) { const Elf_Shdr *shdr; @@ -552,12 +657,7 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, if (symtab_shndx == 0) symtab_shndx = dynsym_shndx; - if (symtab_shndx == 0) - { - state->syminfo_fn = elf_nosyms; - state->syminfo_data = NULL; - } - else + if (symtab_shndx != 0) { const Elf_Shdr *symtab_shdr; unsigned int strtab_shndx; @@ -604,8 +704,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, string table permanently. */ backtrace_release_view (state, &symtab_view, error_callback, data); - state->syminfo_fn = elf_syminfo; - state->syminfo_data = sdata; + *found_sym = 1; + + elf_add_syminfo_data (state, sdata); } /* FIXME: Need to handle compressed debug sections. */ @@ -635,7 +736,6 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, if (!backtrace_close (descriptor, error_callback, data)) goto fail; *fileline_fn = elf_nodebug; - state->fileline_data = NULL; return 1; } @@ -654,21 +754,23 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, sections[i].data = ((const unsigned char *) debug_view.data + (sections[i].offset - min_offset)); - if (!backtrace_dwarf_initialize (state, - sections[DEBUG_INFO].data, - sections[DEBUG_INFO].size, - sections[DEBUG_LINE].data, - sections[DEBUG_LINE].size, - sections[DEBUG_ABBREV].data, - sections[DEBUG_ABBREV].size, - sections[DEBUG_RANGES].data, - sections[DEBUG_RANGES].size, - sections[DEBUG_STR].data, - sections[DEBUG_STR].size, - ehdr.e_ident[EI_DATA] == ELFDATA2MSB, - error_callback, data, fileline_fn)) + if (!backtrace_dwarf_add (state, base_address, + sections[DEBUG_INFO].data, + sections[DEBUG_INFO].size, + sections[DEBUG_LINE].data, + sections[DEBUG_LINE].size, + sections[DEBUG_ABBREV].data, + sections[DEBUG_ABBREV].size, + sections[DEBUG_RANGES].data, + sections[DEBUG_RANGES].size, + sections[DEBUG_STR].data, + sections[DEBUG_STR].size, + ehdr.e_ident[EI_DATA] == ELFDATA2MSB, + error_callback, data, fileline_fn)) goto fail; + *found_dwarf = 1; + return 1; fail: @@ -686,3 +788,115 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, backtrace_close (descriptor, error_callback, data); return 0; } + +/* Data passed to phdr_callback. */ + +struct phdr_data +{ + struct backtrace_state *state; + backtrace_error_callback error_callback; + void *data; + fileline *fileline_fn; + int *found_sym; + int *found_dwarf; +}; + +/* Callback passed to dl_iterate_phdr. Load debug info from shared + libraries. */ + +static int +phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, + void *pdata) +{ + struct phdr_data *pd = (struct phdr_data *) pdata; + int descriptor; + fileline elf_fileline_fn; + int found_dwarf; + + /* There is not much we can do if we don't have the module name. If + the base address is 0, this is probably the executable, which we + already loaded. */ + if (info->dlpi_name == NULL + || info->dlpi_name[0] == '\0' + || info->dlpi_addr == 0) + return 0; + + descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data); + if (descriptor < 0) + return 0; + + if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback, + pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf)) + { + if (found_dwarf) + { + *pd->found_dwarf = 1; + *pd->fileline_fn = elf_fileline_fn; + } + } + + return 0; +} + +/* Initialize the backtrace data we need from an ELF executable. At + the ELF level, all we need to do is find the debug info + sections. */ + +int +backtrace_initialize (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + int found_sym; + int found_dwarf; + syminfo elf_syminfo_fn; + fileline elf_fileline_fn; + struct phdr_data pd; + + if (!elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn, + &found_sym, &found_dwarf)) + return 0; + + pd.state = state; + pd.error_callback = error_callback; + pd.data = data; + pd.fileline_fn = fileline_fn; + pd.found_sym = &found_sym; + pd.found_dwarf = &found_dwarf; + + dl_iterate_phdr (phdr_callback, (void *) &pd); + + elf_syminfo_fn = found_sym ? elf_syminfo : elf_nosyms; + if (!state->threaded) + { + if (state->syminfo_fn == NULL || found_sym) + state->syminfo_fn = elf_syminfo_fn; + } + else + { + __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_syminfo_fn); + if (found_sym) + __sync_bool_compare_and_swap (&state->syminfo_fn, elf_nosyms, + elf_syminfo_fn); + } + + if (!state->threaded) + { + if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug) + *fileline_fn = elf_fileline_fn; + } + else + { + fileline current_fn; + + /* Atomic load. */ + current_fn = state->fileline_fn; + while (!__sync_bool_compare_and_swap (&state->fileline_fn, current_fn, + current_fn)) + current_fn = state->fileline_fn; + if (current_fn == NULL || current_fn == elf_nodebug) + *fileline_fn = elf_fileline_fn; + } + + return 1; +} diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 4a7407a..b1afca0 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -215,21 +215,22 @@ extern int backtrace_initialize (struct backtrace_state *state, void *data, fileline *fileline_fn); -/* Prepare to read file/line information from DWARF debug data. */ - -extern int backtrace_dwarf_initialize (struct backtrace_state *state, - const unsigned char* dwarf_info, - size_t dwarf_info_size, - const unsigned char *dwarf_line, - size_t dwarf_line_size, - const unsigned char *dwarf_abbrev, - size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, - size_t dwarf_range_size, - const unsigned char *dwarf_str, - size_t dwarf_str_size, - int is_bigendian, - backtrace_error_callback error_callback, - void *data, fileline *fileline_fn); +/* Add file/line information for a DWARF module. */ + +extern int backtrace_dwarf_add (struct backtrace_state *state, + uintptr_t base_address, + const unsigned char* dwarf_info, + size_t dwarf_info_size, + const unsigned char *dwarf_line, + size_t dwarf_line_size, + const unsigned char *dwarf_abbrev, + size_t dwarf_abbrev_size, + const unsigned char *dwarf_ranges, + size_t dwarf_range_size, + const unsigned char *dwarf_str, + size_t dwarf_str_size, + int is_bigendian, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn); #endif -- 2.7.4