From patchwork Tue Oct 9 18:20:48 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 190400 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id E575A2C007C for ; Wed, 10 Oct 2012 05:21:15 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1350411676; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Received:From:To:Subject:Date:Message-ID:User-Agent:MIME-Version: Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=9EJMTNl 8AAa8xvrYMB6NN6/u9xo=; b=W+CN9oDqbUU+ZqGULR5XQp99LxzSfO+Flmh9Cpc X2RlU6TntOvFz8gmAHg63vvgz3eefiDPKn5TH/0RQmji/k9esFkzJ33se5UJLm5n MC7x6NXYbCRC3//Mv0UN06xdB4X/k74a1WLmxNXtH0NI/GKDVhTQKlzgQnWVqKKl qaIA= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:Received:Received:From:To:Subject:Date:Message-ID:User-Agent:MIME-Version:Content-Type:X-Gm-Message-State:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=XKq8CpSmtq4cDs2gOJKBwDwiCLDktlYhFiwkUcSqTpYiQvFFTImg+2dGrti4YY CNYrexoowQSqQQ4ctqYO/7ZgtifkL07f97fKZ8H1Fts6CIrK60Lq8csJ8mRnhjIU sT2A/NGrkwu8HS9GJrGCM9Z8MdP6VYUk4EdA9Gk+3v3KY=; Received: (qmail 15729 invoked by alias); 9 Oct 2012 18:21:07 -0000 Received: (qmail 15480 invoked by uid 22791); 9 Oct 2012 18:21:04 -0000 X-SWARE-Spam-Status: No, hits=-6.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, TW_FC, T_FRT_ADULT2, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from mail-pb0-f47.google.com (HELO mail-pb0-f47.google.com) (209.85.160.47) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 09 Oct 2012 18:20:53 +0000 Received: by mail-pb0-f47.google.com with SMTP id ro12so5692772pbb.20 for ; Tue, 09 Oct 2012 11:20:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:user-agent:mime-version :content-type:x-gm-message-state; bh=QdIPMzfYWEX2R/qV6fkME9YUY8UkKTqxUepwacqVpP4=; b=L6XEjwN/jr8VJTtVWd6jTQhZBulQoIQZXOG/ILfViZpH7neNnHGe7DmU4viLL7wf0D 31ipUgbXONrCn2VUCS3YG59csyYuYSHoFG4NRdRjXnXSCinqzJi0yRsrPpshfNtpVY7P XscUQUxNiht6brILo8nrKJvPiNJ5OJ0jvL+PswIyEAc4pi3shyZY7HanqecBU+6yPvXJ 6VWG8mUH+YahedgGNtf/MKLWeYp4WeMlf7SVyMOxBC2o1gndwVpMkqgLX9Ut8c95S2hZ EC0ghgzKuXS+gYLfXaY3JjER7AbQXIra+YSbA6yu5xuPkS5z+h7Vfz17WtHkFu46pTcq 0DCA== Received: by 10.68.238.35 with SMTP id vh3mr61745242pbc.42.1349806852093; Tue, 09 Oct 2012 11:20:52 -0700 (PDT) Received: from coign.google.com ([2620:0:1000:2301:f2de:f1ff:fe40:72a8]) by mx.google.com with ESMTPS id nd6sm9181693pbc.68.2012.10.09.11.20.49 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 09 Oct 2012 11:20:50 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org Subject: libbacktrace patch committed: Trace through shared libraries Date: Tue, 09 Oct 2012 11:20:48 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQnIBeWQ3anYLgEz8qcyRH4GrJy/2XSeQNqdDrf429/mz7tfMSHgCgCoGFZhiUQ6eeRIF4wxo7M5TI+2usoUhhXCRYx8m0078A6fAiZapGFi2s/AISfWA1RmhqTgpM/BdoimZ5n293xvYy9jeft1n9Ts3S3mivmAxsWKU2qlUdjnOCP+9Urt6FoPwc3PiaWeairGXf6oNaVA0vZURkZDJc1/51Ns5w== X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch to libbacktrace adds support for tracing through shared libraries. The libraries are found by calling dl_iterate_phdr, when it is available. This patch has some preliminary support for tracing through libaries opened via dlopen, but there is no code for actually finding such libraries. It would require keeping track of the modules that have been read, and, when some PC is found with no definition, calling dl_iterate_phdr again to see if any libraries have been dlopen'ed. I do not know how to support dlclose. Patch bootstrapped and ran libbacktrace and Go testsuites on x86_64-unknown-linux-gnu. Committed to mainline. Ian 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. Index: dwarf.c =================================================================== --- dwarf.c (revision 192266) +++ dwarf.c (working copy) @@ -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, 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, 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 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 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_stat 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_stat 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_stat 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, 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 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_stat / 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_stat 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 * 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 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_st 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_st { 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, 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 * 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 * 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 * 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 backt 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 backt 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; Index: elf.c =================================================================== --- elf.c (revision 192266) +++ elf.c (working copy) @@ -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 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 *sta 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_s 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_s 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_s 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_s 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_s 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_s 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_s 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; +} Index: internal.h =================================================================== --- internal.h (revision 192266) +++ internal.h (working copy) @@ -215,21 +215,22 @@ extern int backtrace_initialize (struct void *data, fileline *fileline_fn); -/* Prepare to read file/line information from DWARF debug data. */ +/* Add file/line information for a DWARF module. */ -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); +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 Index: configure.ac =================================================================== --- configure.ac (revision 192266) +++ configure.ac (working copy) @@ -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