From patchwork Fri Dec 13 20:04:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 1209410 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-515967-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=golang.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Kr78G1qC"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=golang-org.20150623.gappssmtp.com header.i=@golang-org.20150623.gappssmtp.com header.b="aUgTlXDz"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47ZM8w5kchz9sPL for ; Sat, 14 Dec 2019 07:05:18 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; q= dns; s=default; b=RjAHau3cStw3/8sefd2NFobdeuzg74ZV0ipZvUyKbsibpv 8KQhX99e4I0+uoEn0Ln7MZ5K3wuKOiuP/f0+QW8Bbyy8arCUjsGtNrF9IcvZrsyM Xf2gQqh6f86fA2/pzcI3U0++nSkC3U/5r18jNPT7HujD/lyPe9wAqQIK9vpdg= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; s= default; bh=XtJgyeUyQ2wcoorel7YcWg99+pU=; b=Kr78G1qCDZCet+VUMih+ Lljjocq5wXNvyEhaG7TLDQnJ+bG0nD8DYdLfwsOFa7k8hQjbZv4sDN5iOXZh5dhS +SbUx05oxDtdVjsfjny+Yz428oig9nLi77xP1t9ffkvvW5roJfR3Y4SAwwi9dZP1 z8+NatiBSnGpp4Su7rZdF1Q= Received: (qmail 81647 invoked by alias); 13 Dec 2019 20:05:10 -0000 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 Received: (qmail 81639 invoked by uid 89); 13 Dec 2019 20:05:10 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-13.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_NONE, SPF_PASS, T_FILL_THIS_FORM_SHORT autolearn=ham version=3.3.1 spammy=tip, HX-Received:6402, sk:DW_AT_r, Primary X-HELO: mail-ed1-f42.google.com Received: from mail-ed1-f42.google.com (HELO mail-ed1-f42.google.com) (209.85.208.42) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 13 Dec 2019 20:05:04 +0000 Received: by mail-ed1-f42.google.com with SMTP id c26so88148eds.8 for ; Fri, 13 Dec 2019 12:05:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=golang-org.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to; bh=EoGGGJjNcxkH50fWvmMbxGmKVwlmms1mcLaAeeiieh4=; b=aUgTlXDzTZW0ErC9tfrv1wUkUYxKu2g8v3AQ1Zm+Jf9ixRcGqCinIRBw6qhP7Y6HFm PY2wSi3wPD4WGpYMc/A+otZ4MYHCV/9T1a8zlH1MWEJkjV4+WWS04YawCgnnXz1k59sT 5ZmRMy7aBoP8bW38BbnyiHVikWnlpIEMGvVGSYaeKrHffepiwhN5652S0q6OMLdZpvoO swOsk8thYI96IJ5dhAhyL1dUONmj4j3nO9jbdNJfVpwbgX3fjcaoknNfcz7p/HUmjfas P1X9TWLAQZX4jRte+RkIhPgGpVMjouOELbflN6yx5U4JiMGSuKs1fHY42K1C6+lHF2UI 8iaA== MIME-Version: 1.0 From: Ian Lance Taylor Date: Fri, 13 Dec 2019 12:04:49 -0800 Message-ID: Subject: libbacktrace patch committed: Add DWARF 5 support To: gcc-patches This patch to libbacktrace adds DWARF 5 support. I tested this with GCC 8, GCC tip, and clang 8, using the -gdwarf-5 option. Bootstrapped and ran libbacktrace and libgo tests on x86_64-pc-linux-gnu. Committed to mainline. Ian 2019-12-13 Ian Lance Taylor Add DWARF 5 support. * dwarf.c (struct attr): Add val field. (enum attr_val_encoding): Add ATTR_VAL_ADDDRESS_INDEX, ATTR_VAL_STRING_INDEX, ATTR_VAL_RNGLISTS_INDEX. (struct line_header): Add addrsize field. (struct line_header_format): Define. (struct unit): Add str_offsets_base, addr_base, and rnglists_base fields. (read_uint24): New static function. (read_attribute): Add implicit_val parameter. Replace dwarf_str and dwarf_str_size parameters with dwarf_sections parameter. Add support for new DWARF 5 forms. Change all callers. (resolve_string): New static function. (resolve_addr_index): Likewise. (read_abbrevs): Support DW_FORM_implicit_const. (struct pcrange): Add lowpc_is_addr_index, highpc_is_addr_Index, and ranges_is_index fields. (update_pcrange): Support DWARF 5 encodings. (add_high_low_range): New static function, split out of add_ranges. (add_ranges_from_ranges): Likewise. (add_ranges_from_rnglists): New static function. (add_ranges): Just call new helper functions. (find_address_ranges): Use resolve_string for strings, after reading all attributes. Handle new DWARF 5 attributes. (build_address_map): Support DWARF 5 compilation units. (read_v2_paths): New static function, split out of read_line_header. (read_lnct): New static function. (read_line_header_format_entries): Likewise. (read_line_header): Add ddata parameter. Support DWARF 5 line headers. Call new helper functions. Change all callers. (read_line_program): Use addrsize from line program header. Don't special case directory index 0 for DWARF 5. (read_referenced_name): Use resolve_string. (read_function_entry): Handle DWARF 5 encodings. Use resolve_string. * internal.h (enum dwarf_section): Add DEBUG_ADDR, DEBUG_STR_OFFSETS, DEBUG_LINE_STR, DEBUG_RNGLISTS. * elf.c (dwarf_section_names): Add new section names. * pecoff.c (dwarf_section_names): Likewise. * xcoff.c (xcoff_add): Clear dwarf_sections before setting fields. * configure.ac: Define HAVE_DWARF5 automake conditional. * Makefile.am (dwarf5_SOURCES): New variable if HAVE_DWARF5. (dwarf5_CFLAGS, dwarf5_LDADD): Likewise. (dwarf5_alloc_SOURCES, dwarf5_alloc_CFLAGS): Likewise. (dwarf5_alloc_LDADD): Likewise. (BUILDTESTS): Add dwarf5 tests if HAVE_DWARF5. (CLEANFILES, clean-local): Define. Index: Makefile.am =================================================================== --- Makefile.am (revision 279211) +++ Makefile.am (working copy) @@ -385,12 +385,33 @@ BUILDTESTS += ctestg_alloc ctesta_alloc endif +if HAVE_DWARF5 + +dwarf5_SOURCES = btest.c testlib.c +dwarf5_CFLAGS = $(AM_CFLAGS) -gdwarf-5 +dwarf5_LDADD = libbacktrace.la + +BUILDTESTS += dwarf5 + +dwarf5_alloc_SOURCES = $(dwarf5_SOURCES) +dwarf5_alloc_CFLAGS = $(dwarf5_CFLAGS) +dwarf5_alloc_LDADD = libbacktrace_alloc.la + +BUILDTESTS += dwarf5_alloc + +endif + endif NATIVE check_PROGRAMS += $(BUILDTESTS) TESTS += $(BUILDTESTS) +CLEANFILES = $(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build + +clean-local: + -rm -rf usr + # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking # with GCC bootstrap will cause some of the objects to depend on Index: configure.ac =================================================================== --- configure.ac (revision 279211) +++ configure.ac (working copy) @@ -420,6 +420,17 @@ AC_SUBST(PTHREAD_CFLAGS) AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) +dnl Test whether the compiler supports the -gdwarf-5 option. +AC_CACHE_CHECK([whether -gdwarf-5 is supported], +[libbacktrace_cv_lib_dwarf5], +[CFLAGS_hold=$CFLAGS +CFLAGS="$CFLAGS -gdwarf-5" +AC_COMPILE_IFELSE([AC_LANG_SOURCE([int i;])], +[libbacktrace_cv_lib_dwarf5=yes], +[libbacktrace_cv_lib_dwarf5=no]) +CFLAGS=$CFLAGS_hold]) +AM_CONDITIONAL(HAVE_DWARF5, test "$libbacktrace_cv_lib_dwarf5" = yes) + AC_CHECK_LIB([z], [compress], [AC_DEFINE(HAVE_ZLIB, 1, [Define if -lz is available.])]) AM_CONDITIONAL(HAVE_ZLIB, test "$ac_cv_lib_z_compress" = yes) Index: dwarf.c =================================================================== --- dwarf.c (revision 279211) +++ dwarf.c (working copy) @@ -92,6 +92,8 @@ struct attr enum dwarf_attribute name; /* The attribute form. */ enum dwarf_form form; + /* The attribute value, for DW_FORM_implicit_const. */ + int64_t val; }; /* A single DWARF abbreviation. */ @@ -133,22 +135,29 @@ enum attr_val_encoding ATTR_VAL_NONE, /* An address. */ ATTR_VAL_ADDRESS, + /* An index into the .debug_addr section, whose value is relative to + * the DW_AT_addr_base attribute of the compilation unit. */ + ATTR_VAL_ADDRESS_INDEX, /* A unsigned integer. */ ATTR_VAL_UINT, /* A sigd integer. */ ATTR_VAL_SINT, /* A string. */ ATTR_VAL_STRING, + /* An index into the .debug_str_offsets section. */ + ATTR_VAL_STRING_INDEX, /* An offset to other data in the containing unit. */ ATTR_VAL_REF_UNIT, - /* An offset to other data within the .dwarf_info section. */ + /* An offset to other data within the .debug_info section. */ ATTR_VAL_REF_INFO, - /* An offset to other data within the alt .dwarf_info section. */ + /* An offset to other data within the alt .debug_info section. */ ATTR_VAL_REF_ALT_INFO, /* An offset to data in some other section. */ ATTR_VAL_REF_SECTION, /* A type signature. */ ATTR_VAL_REF_TYPE, + /* An index into the .debug_rnglists section. */ + ATTR_VAL_RNGLISTS_INDEX, /* A block of data (not represented). */ ATTR_VAL_BLOCK, /* An expression (not represented). */ @@ -163,7 +172,7 @@ struct attr_val enum attr_val_encoding encoding; union { - /* ATTR_VAL_ADDRESS, ATTR_VAL_UINT, ATTR_VAL_REF*. */ + /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */ uint64_t uint; /* ATTR_VAL_SINT. */ int64_t sint; @@ -179,6 +188,8 @@ struct line_header { /* The version of the line number information. */ int version; + /* Address size. */ + int addrsize; /* The minimum instruction length. */ unsigned int min_insn_len; /* The maximum number of ops per instruction. */ @@ -201,6 +212,14 @@ struct line_header const char **filenames; }; +/* A format description from a line header. */ + +struct line_header_format +{ + int lnct; /* LNCT code. */ + enum dwarf_form form; /* Form of entry data. */ +}; + /* Map a single PC value to a file/line. We will keep a vector of these sorted by PC value. Each file/line will be correct from the PC up to the PC of the next entry if there is one. We allocate one @@ -297,6 +316,12 @@ struct unit int addrsize; /* Offset into line number information. */ off_t lineoff; + /* Offset of compilation unit in .debug_str_offsets. */ + uint64_t str_offsets_base; + /* Offset of compilation unit in .debug_addr. */ + uint64_t addr_base; + /* Offset of compilation unit in .debug_rnglists. */ + uint64_t rnglists_base; /* Primary source file. */ const char *filename; /* Compilation command working directory. */ @@ -483,6 +508,23 @@ read_uint16 (struct dwarf_buf *buf) return ((uint16_t) p[1] << 8) | (uint16_t) p[0]; } +/* Read a 24 bit value from BUF and advance 3 bytes. */ + +static uint32_t +read_uint24 (struct dwarf_buf *buf) +{ + const unsigned char *p = buf->buf; + + if (!advance (buf, 3)) + return 0; + if (buf->is_bigendian) + return (((uint32_t) p[0] << 16) | ((uint32_t) p[1] << 8) + | (uint32_t) p[2]); + else + return (((uint32_t) p[2] << 16) | ((uint32_t) p[1] << 8) + | (uint32_t) p[0]); +} + /* Read a uint32 from BUF and advance 4 bytes. */ static uint32_t @@ -709,9 +751,9 @@ free_abbrevs (struct backtrace_state *st forms, because we don't care about them. */ static int -read_attribute (enum dwarf_form form, struct dwarf_buf *buf, - int is_dwarf64, int version, int addrsize, - const unsigned char *dwarf_str, size_t dwarf_str_size, +read_attribute (enum dwarf_form form, uint64_t implicit_val, + struct dwarf_buf *buf, int is_dwarf64, int version, + int addrsize, const struct dwarf_sections *dwarf_sections, struct dwarf_data *altlink, struct attr_val *val) { /* Avoid warnings about val.u.FIELD may be used uninitialized if @@ -744,6 +786,9 @@ read_attribute (enum dwarf_form form, st val->encoding = ATTR_VAL_UINT; val->u.uint = read_uint64 (buf); return 1; + case DW_FORM_data16: + val->encoding = ATTR_VAL_BLOCK; + return advance (buf, 16); case DW_FORM_string: val->encoding = ATTR_VAL_STRING; val->u.string = read_string (buf); @@ -771,13 +816,29 @@ read_attribute (enum dwarf_form form, st uint64_t offset; offset = read_offset (buf, is_dwarf64); - if (offset >= dwarf_str_size) + if (offset >= dwarf_sections->size[DEBUG_STR]) { dwarf_buf_error (buf, "DW_FORM_strp out of range"); return 0; } val->encoding = ATTR_VAL_STRING; - val->u.string = (const char *) dwarf_str + offset; + val->u.string = + (const char *) dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + case DW_FORM_line_strp: + { + uint64_t offset; + + offset = read_offset (buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_LINE_STR]) + { + dwarf_buf_error (buf, "DW_FORM_line_strp out of range"); + return 0; + } + val->encoding = ATTR_VAL_STRING; + val->u.string = + (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset; return 1; } case DW_FORM_udata: @@ -816,9 +877,15 @@ read_attribute (enum dwarf_form form, st uint64_t form; form = read_uleb128 (buf); - return read_attribute ((enum dwarf_form) form, buf, is_dwarf64, - version, addrsize, dwarf_str, dwarf_str_size, - altlink, val); + if (form == DW_FORM_implicit_const) + { + dwarf_buf_error (buf, + "DW_FORM_indirect to DW_FORM_implicit_const"); + return 0; + } + return read_attribute ((enum dwarf_form) form, 0, buf, is_dwarf64, + version, addrsize, dwarf_sections, altlink, + val); } case DW_FORM_sec_offset: val->encoding = ATTR_VAL_REF_SECTION; @@ -835,6 +902,88 @@ read_attribute (enum dwarf_form form, st val->encoding = ATTR_VAL_REF_TYPE; val->u.uint = read_uint64 (buf); return 1; + case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2: + case DW_FORM_strx3: case DW_FORM_strx4: + { + uint64_t offset; + + switch (form) + { + case DW_FORM_strx: + offset = read_uleb128 (buf); + break; + case DW_FORM_strx1: + offset = read_byte (buf); + break; + case DW_FORM_strx2: + offset = read_uint16 (buf); + break; + case DW_FORM_strx3: + offset = read_uint24 (buf); + break; + case DW_FORM_strx4: + offset = read_uint32 (buf); + break; + default: + /* This case can't happen. */ + return 0; + } + val->encoding = ATTR_VAL_STRING_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2: + case DW_FORM_addrx3: case DW_FORM_addrx4: + { + uint64_t offset; + + switch (form) + { + case DW_FORM_addrx: + offset = read_uleb128 (buf); + break; + case DW_FORM_addrx1: + offset = read_byte (buf); + break; + case DW_FORM_addrx2: + offset = read_uint16 (buf); + break; + case DW_FORM_addrx3: + offset = read_uint24 (buf); + break; + case DW_FORM_addrx4: + offset = read_uint32 (buf); + break; + default: + /* This case can't happen. */ + return 0; + } + val->encoding = ATTR_VAL_ADDRESS_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_ref_sup4: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint32 (buf); + return 1; + case DW_FORM_ref_sup8: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint64 (buf); + return 1; + case DW_FORM_implicit_const: + val->encoding = ATTR_VAL_UINT; + val->u.uint = implicit_val; + return 1; + case DW_FORM_loclistx: + /* We don't distinguish this from DW_FORM_sec_offset. It + * shouldn't matter since we don't care about loclists. */ + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uleb128 (buf); + return 1; + case DW_FORM_rnglistx: + val->encoding = ATTR_VAL_RNGLISTS_INDEX; + val->u.uint = read_uleb128 (buf); + return 1; case DW_FORM_GNU_addr_index: val->encoding = ATTR_VAL_REF_SECTION; val->u.uint = read_uleb128 (buf); @@ -852,9 +1001,10 @@ read_attribute (enum dwarf_form form, st } val->encoding = ATTR_VAL_REF_ALT_INFO; return 1; - case DW_FORM_GNU_strp_alt: + case DW_FORM_strp_sup: case DW_FORM_GNU_strp_alt: { uint64_t offset; + offset = read_offset (buf, is_dwarf64); if (altlink == NULL) { @@ -863,7 +1013,7 @@ read_attribute (enum dwarf_form form, st } if (offset >= altlink->dwarf_sections.size[DEBUG_STR]) { - dwarf_buf_error (buf, "DW_FORM_GNU_strp_alt out of range"); + dwarf_buf_error (buf, "DW_FORM_strp_sup out of range"); return 0; } val->encoding = ATTR_VAL_STRING; @@ -877,6 +1027,95 @@ read_attribute (enum dwarf_form form, st } } +/* If we can determine the value of a string attribute, set *STRING to + point to the string. Return 1 on success, 0 on error. If we don't + know the value, we consider that a success, and we don't change + *STRING. An error is only reported for some sort of out of range + offset. */ + +static int +resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64, + int is_bigendian, uint64_t str_offsets_base, + const struct attr_val *val, + backtrace_error_callback error_callback, void *data, + const char **string) +{ + switch (val->encoding) + { + case ATTR_VAL_STRING: + *string = val->u.string; + return 1; + + case ATTR_VAL_STRING_INDEX: + { + uint64_t offset; + struct dwarf_buf offset_buf; + + offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base; + if (offset + (is_dwarf64 ? 8 : 4) + >= dwarf_sections->size[DEBUG_STR_OFFSETS]) + { + error_callback (data, "DW_FORM_strx value out of range", 0); + return 0; + } + + offset_buf.name = ".debug_str_offsets"; + offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS]; + offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset; + offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset; + offset_buf.is_bigendian = is_bigendian; + offset_buf.error_callback = error_callback; + offset_buf.data = data; + offset_buf.reported_underflow = 0; + + offset = read_offset (&offset_buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_STR]) + { + dwarf_buf_error (&offset_buf, "DW_FORM_strx offset out of range"); + return 0; + } + *string = (const char *) dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + + default: + return 1; + } +} + +/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX. + Return 1 on success, 0 on error. */ + +static int +resolve_addr_index (const struct dwarf_sections *dwarf_sections, + uint64_t addr_base, int addrsize, int is_bigendian, + uint64_t addr_index, + backtrace_error_callback error_callback, void *data, + uint64_t *address) +{ + uint64_t offset; + struct dwarf_buf addr_buf; + + offset = addr_index * addrsize + addr_base; + if (offset + addrsize >= dwarf_sections->size[DEBUG_ADDR]) + { + error_callback (data, "DW_FORM_addrx value out of range", 0); + return 0; + } + + addr_buf.name = ".debug_addr"; + addr_buf.start = dwarf_sections->data[DEBUG_ADDR]; + addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset; + addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset; + addr_buf.is_bigendian = is_bigendian; + addr_buf.error_callback = error_callback; + addr_buf.data = data; + addr_buf.reported_underflow = 0; + + *address = read_address (&addr_buf, addrsize); + return 1; +} + /* Compare a unit offset against a unit for bsearch. */ static int @@ -1142,7 +1381,13 @@ read_abbrevs (struct backtrace_state *st read_byte (&count_buf); // Skip attributes. while (read_uleb128 (&count_buf) != 0) - read_uleb128 (&count_buf); + { + uint64_t form; + + form = read_uleb128 (&count_buf); + if ((enum dwarf_form) form == DW_FORM_implicit_const) + read_sleb128 (&count_buf); + } // Skip form of last attribute. read_uleb128 (&count_buf); } @@ -1185,8 +1430,12 @@ read_abbrevs (struct backtrace_state *st num_attrs = 0; while (read_uleb128 (&count_buf) != 0) { + uint64_t form; + ++num_attrs; - read_uleb128 (&count_buf); + form = read_uleb128 (&count_buf); + if ((enum dwarf_form) form == DW_FORM_implicit_const) + read_sleb128 (&count_buf); } if (num_attrs == 0) @@ -1214,6 +1463,10 @@ read_abbrevs (struct backtrace_state *st break; attrs[num_attrs].name = (enum dwarf_attribute) name; attrs[num_attrs].form = (enum dwarf_form) form; + if ((enum dwarf_form) form == DW_FORM_implicit_const) + attrs[num_attrs].val = read_sleb128 (&abbrev_buf); + else + attrs[num_attrs].val = 0; ++num_attrs; } } @@ -1272,11 +1525,14 @@ lookup_abbrev (struct abbrevs *abbrevs, struct pcrange { uint64_t lowpc; /* The low PC value. */ int have_lowpc; /* Whether a low PC value was found. */ + int lowpc_is_addr_index; /* Whether lowpc is in .debug_addr. */ uint64_t highpc; /* The high PC value. */ int have_highpc; /* Whether a high PC value was found. */ int highpc_is_relative; /* Whether highpc is relative to lowpc. */ + int highpc_is_addr_index; /* Whether highpc is in .debug_addr. */ uint64_t ranges; /* Offset in ranges section. */ int have_ranges; /* Whether ranges is valid. */ + int ranges_is_index; /* Whether ranges is DW_FORM_rnglistx. */ }; /* Update PCRANGE from an attribute value. */ @@ -1293,6 +1549,12 @@ update_pcrange (const struct attr* attr, pcrange->lowpc = val->u.uint; pcrange->have_lowpc = 1; } + else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) + { + pcrange->lowpc = val->u.uint; + pcrange->have_lowpc = 1; + pcrange->lowpc_is_addr_index = 1; + } break; case DW_AT_high_pc: @@ -1307,6 +1569,12 @@ update_pcrange (const struct attr* attr, pcrange->have_highpc = 1; pcrange->highpc_is_relative = 1; } + else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) + { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + pcrange->highpc_is_addr_index = 1; + } break; case DW_AT_ranges: @@ -1316,6 +1584,12 @@ update_pcrange (const struct attr* attr, pcrange->ranges = val->u.uint; pcrange->have_ranges = 1; } + else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX) + { + pcrange->ranges = val->u.uint; + pcrange->have_ranges = 1; + pcrange->ranges_is_index = 1; + } break; default: @@ -1323,51 +1597,73 @@ update_pcrange (const struct attr* attr, } } -/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is - passed to ADD_RANGE, and is either a struct unit * or a struct - function *. VEC is the vector we are adding ranges to, and is - either a struct unit_addrs_vector * or a struct function_vector *. - Returns 1 on success, 0 on error. */ +/* Call ADD_RANGE for a low/high PC pair. Returns 1 on success, 0 on + error. */ static int -add_ranges (struct backtrace_state *state, - const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, - struct unit *u, uint64_t base, const struct pcrange *pcrange, - int (*add_range) (struct backtrace_state *state, void *rdata, - uint64_t lowpc, uint64_t highpc, - backtrace_error_callback error_callback, - void *data, void *vec), - void *rdata, - backtrace_error_callback error_callback, void *data, - void *vec) +add_low_high_range (struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, + void *rdata, uint64_t lowpc, + uint64_t highpc, + backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) { - struct dwarf_buf ranges_buf; + uint64_t lowpc; + uint64_t highpc; - if (pcrange->have_lowpc && pcrange->have_highpc) + lowpc = pcrange->lowpc; + if (pcrange->lowpc_is_addr_index) { - uint64_t lowpc; - uint64_t highpc; - - lowpc = pcrange->lowpc; - highpc = pcrange->highpc; - if (pcrange->highpc_is_relative) - highpc += lowpc; - - /* Add in the base address of the module when recording PC - values, so that we can look up the PC directly. */ - lowpc += base_address; - highpc += base_address; - - return add_range (state, rdata, lowpc, highpc, error_callback, data, - vec); + if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, lowpc, error_callback, data, + &lowpc)) + return 0; } - if (!pcrange->have_ranges) + highpc = pcrange->highpc; + if (pcrange->highpc_is_addr_index) { - /* Did not find any address ranges to add. */ - return 1; + if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, highpc, error_callback, data, + &highpc)) + return 0; } + if (pcrange->highpc_is_relative) + highpc += lowpc; + + /* Add in the base address of the module when recording PC values, + so that we can look up the PC directly. */ + lowpc += base_address; + highpc += base_address; + + return add_range (state, rdata, lowpc, highpc, error_callback, data, vec); +} + +/* Call ADD_RANGE for each range read from .debug_ranges, as used in + DWARF versions 2 through 4. */ + +static int +add_ranges_from_ranges ( + struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + struct dwarf_buf ranges_buf; if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES]) { @@ -1416,6 +1712,220 @@ add_ranges (struct backtrace_state *stat return 1; } +/* Call ADD_RANGE for each range read from .debug_rnglists, as used in + DWARF version 5. */ + +static int +add_ranges_from_rnglists ( + struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + uint64_t offset; + struct dwarf_buf rnglists_buf; + + if (!pcrange->ranges_is_index) + offset = pcrange->ranges; + else + offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4); + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) + { + error_callback (data, "rnglists offset out of range", 0); + return 0; + } + + rnglists_buf.name = ".debug_rnglists"; + rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS]; + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + rnglists_buf.is_bigendian = is_bigendian; + rnglists_buf.error_callback = error_callback; + rnglists_buf.data = data; + rnglists_buf.reported_underflow = 0; + + if (pcrange->ranges_is_index) + { + offset = read_offset (&rnglists_buf, u->is_dwarf64); + offset += u->rnglists_base; + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) + { + error_callback (data, "rnglists index offset out of range", 0); + return 0; + } + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + } + + while (1) + { + unsigned char rle; + + rle = read_byte (&rnglists_buf); + if (rle == DW_RLE_end_of_list) + break; + switch (rle) + { + case DW_RLE_base_addressx: + { + uint64_t index; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &base)) + return 0; + } + break; + + case DW_RLE_startx_endx: + { + uint64_t index; + uint64_t low; + uint64_t high; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &low)) + return 0; + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &high)) + return 0; + if (!add_range (state, rdata, low + base_address, + high + base_address, error_callback, data, + vec)) + return 0; + } + break; + + case DW_RLE_startx_length: + { + uint64_t index; + uint64_t low; + uint64_t length; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &low)) + return 0; + length = read_uleb128 (&rnglists_buf); + low += base_address; + if (!add_range (state, rdata, low, low + length, + error_callback, data, vec)) + return 0; + } + break; + + case DW_RLE_offset_pair: + { + uint64_t low; + uint64_t high; + + low = read_uleb128 (&rnglists_buf); + high = read_uleb128 (&rnglists_buf); + if (!add_range (state, rdata, low + base + base_address, + high + base + base_address, + error_callback, data, vec)) + return 0; + } + break; + + case DW_RLE_base_address: + base = read_address (&rnglists_buf, u->addrsize); + break; + + case DW_RLE_start_end: + { + uint64_t low; + uint64_t high; + + low = read_address (&rnglists_buf, u->addrsize); + high = read_address (&rnglists_buf, u->addrsize); + if (!add_range (state, rdata, low + base_address, + high + base_address, error_callback, data, + vec)) + return 0; + } + break; + + case DW_RLE_start_length: + { + uint64_t low; + uint64_t length; + + low = read_address (&rnglists_buf, u->addrsize); + length = read_uleb128 (&rnglists_buf); + low += base_address; + if (!add_range (state, rdata, low, low + length, + error_callback, data, vec)) + return 0; + } + break; + + default: + dwarf_buf_error (&rnglists_buf, "unrecognized DW_RLE value"); + return 0; + } + } + + if (rnglists_buf.reported_underflow) + return 0; + + return 1; +} + +/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is + passed to ADD_RANGE, and is either a struct unit * or a struct + function *. VEC is the vector we are adding ranges to, and is + either a struct unit_addrs_vector * or a struct function_vector *. + Returns 1 on success, 0 on error. */ + +static int +add_ranges (struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + if (pcrange->have_lowpc && pcrange->have_highpc) + return add_low_high_range (state, dwarf_sections, base_address, + is_bigendian, u, pcrange, add_range, rdata, + error_callback, data, vec); + + if (!pcrange->have_ranges) + { + /* Did not find any address ranges to add. */ + return 1; + } + + if (u->version < 5) + return add_ranges_from_ranges (state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); + else + return add_ranges_from_rnglists (state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); +} + /* Find the address range covered by a compilation unit, reading from UNIT_BUF and adding values to U. Returns 1 if all data could be read, 0 if there is some error. */ @@ -1434,6 +1944,10 @@ find_address_ranges (struct backtrace_st uint64_t code; const struct abbrev *abbrev; struct pcrange pcrange; + struct attr_val name_val; + int have_name_val; + struct attr_val comp_dir_val; + int have_comp_dir_val; size_t i; code = read_uleb128 (unit_buf); @@ -1448,15 +1962,17 @@ find_address_ranges (struct backtrace_st *unit_tag = abbrev->tag; memset (&pcrange, 0, sizeof pcrange); + memset (&name_val, 0, sizeof name_val); + have_name_val = 0; + memset (&comp_dir_val, 0, sizeof comp_dir_val); + have_comp_dir_val = 0; for (i = 0; i < abbrev->num_attrs; ++i) { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - dwarf_sections->data[DEBUG_STR], - dwarf_sections->size[DEBUG_STR], - altlink, &val)) + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + unit_buf, u->is_dwarf64, u->version, + u->addrsize, dwarf_sections, altlink, &val)) return 0; switch (abbrev->attrs[i].name) @@ -1473,15 +1989,37 @@ find_address_ranges (struct backtrace_st break; case DW_AT_name: - if (abbrev->tag == DW_TAG_compile_unit - && val.encoding == ATTR_VAL_STRING) - u->filename = val.u.string; + if (abbrev->tag == DW_TAG_compile_unit) + { + name_val = val; + have_name_val = 1; + } break; case DW_AT_comp_dir: + if (abbrev->tag == DW_TAG_compile_unit) + { + comp_dir_val = val; + have_comp_dir_val = 1; + } + break; + + case DW_AT_str_offsets_base: + if (abbrev->tag == DW_TAG_compile_unit + && val.encoding == ATTR_VAL_REF_SECTION) + u->str_offsets_base = val.u.uint; + break; + + case DW_AT_addr_base: + if (abbrev->tag == DW_TAG_compile_unit + && val.encoding == ATTR_VAL_REF_SECTION) + u->addr_base = val.u.uint; + break; + + case DW_AT_rnglists_base: if (abbrev->tag == DW_TAG_compile_unit - && val.encoding == ATTR_VAL_STRING) - u->comp_dir = val.u.string; + && val.encoding == ATTR_VAL_REF_SECTION) + u->rnglists_base = val.u.uint; break; default: @@ -1489,6 +2027,23 @@ find_address_ranges (struct backtrace_st } } + // Resolve strings after we're sure that we have seen + // DW_AT_str_offsets_base. + if (have_name_val) + { + if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &name_val, + error_callback, data, &u->filename)) + return 0; + } + if (have_comp_dir_val) + { + if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &comp_dir_val, + error_callback, data, &u->comp_dir)) + return 0; + } + if (abbrev->tag == DW_TAG_compile_unit || abbrev->tag == DW_TAG_subprogram) { @@ -1565,6 +2120,7 @@ build_address_map (struct backtrace_stat int is_dwarf64; struct dwarf_buf unit_buf; int version; + int unit_type; uint64_t abbrev_offset; int addrsize; struct unit *u; @@ -1583,12 +2139,24 @@ build_address_map (struct backtrace_stat goto fail; version = read_uint16 (&unit_buf); - if (version < 2 || version > 4) + if (version < 2 || version > 5) { dwarf_buf_error (&unit_buf, "unrecognized DWARF version"); goto fail; } + if (version < 5) + unit_type = 0; + else + { + unit_type = read_byte (&unit_buf); + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) + { + /* This unit doesn't have anything we need. */ + continue; + } + } + pu = ((struct unit **) backtrace_vector_grow (state, sizeof (struct unit *), error_callback, data, &units)); @@ -1603,6 +2171,11 @@ build_address_map (struct backtrace_stat *pu = u; ++units_count; + if (version < 5) + addrsize = 0; /* Set below. */ + else + addrsize = read_byte (&unit_buf); + memset (&u->abbrevs, 0, sizeof u->abbrevs); abbrev_offset = read_offset (&unit_buf, is_dwarf64); if (!read_abbrevs (state, abbrev_offset, @@ -1611,7 +2184,21 @@ build_address_map (struct backtrace_stat is_bigendian, error_callback, data, &u->abbrevs)) goto fail; - addrsize = read_byte (&unit_buf); + if (version < 5) + addrsize = read_byte (&unit_buf); + + switch (unit_type) + { + case 0: + break; + case DW_UT_compile: case DW_UT_partial: + break; + case DW_UT_skeleton: case DW_UT_split_compile: + read_uint64 (&unit_buf); /* dwo_id */ + break; + default: + break; + } u->low_offset = unit_offset; unit_offset += len + (is_dwarf64 ? 12 : 4); @@ -1720,55 +2307,21 @@ free_line_header (struct backtrace_state error_callback, data); } -/* Read the line header. Return 1 on success, 0 on failure. */ +/* Read the directories and file names for a line header for version + 2, setting fields in HDR. Return 1 on success, 0 on failure. */ static int -read_line_header (struct backtrace_state *state, struct unit *u, - int is_dwarf64, struct dwarf_buf *line_buf, - struct line_header *hdr) +read_v2_paths (struct backtrace_state *state, struct unit *u, + struct dwarf_buf *hdr_buf, struct line_header *hdr) { - uint64_t hdrlen; - struct dwarf_buf hdr_buf; const unsigned char *p; const unsigned char *pend; size_t i; - hdr->version = read_uint16 (line_buf); - if (hdr->version < 2 || hdr->version > 4) - { - dwarf_buf_error (line_buf, "unsupported line number version"); - return 0; - } - - hdrlen = read_offset (line_buf, is_dwarf64); - - hdr_buf = *line_buf; - hdr_buf.left = hdrlen; - - if (!advance (line_buf, hdrlen)) - return 0; - - hdr->min_insn_len = read_byte (&hdr_buf); - if (hdr->version < 4) - hdr->max_ops_per_insn = 1; - else - hdr->max_ops_per_insn = read_byte (&hdr_buf); - - /* We don't care about default_is_stmt. */ - read_byte (&hdr_buf); - - hdr->line_base = read_sbyte (&hdr_buf); - hdr->line_range = read_byte (&hdr_buf); - - hdr->opcode_base = read_byte (&hdr_buf); - hdr->opcode_lengths = hdr_buf.buf; - if (!advance (&hdr_buf, hdr->opcode_base - 1)) - return 0; - /* Count the number of directory entries. */ hdr->dirs_count = 0; - p = hdr_buf.buf; - pend = p + hdr_buf.left; + p = hdr_buf->buf; + pend = p + hdr_buf->left; while (p < pend && *p != '\0') { p += strnlen((const char *) p, pend - p) + 1; @@ -1781,29 +2334,30 @@ read_line_header (struct backtrace_state hdr->dirs = ((const char **) backtrace_alloc (state, hdr->dirs_count * sizeof (const char *), - line_buf->error_callback, line_buf->data)); + hdr_buf->error_callback, + hdr_buf->data)); if (hdr->dirs == NULL) return 0; } i = 0; - while (*hdr_buf.buf != '\0') + while (*hdr_buf->buf != '\0') { - if (hdr_buf.reported_underflow) + if (hdr_buf->reported_underflow) return 0; - hdr->dirs[i] = read_string (&hdr_buf); + hdr->dirs[i] = read_string (hdr_buf); if (hdr->dirs[i] == NULL) return 0; ++i; } - if (!advance (&hdr_buf, 1)) + if (!advance (hdr_buf, 1)) return 0; /* Count the number of file entries. */ hdr->filenames_count = 0; - p = hdr_buf.buf; - pend = p + hdr_buf.left; + p = hdr_buf->buf; + pend = p + hdr_buf->left; while (p < pend && *p != '\0') { p += strnlen ((const char *) p, pend - p) + 1; @@ -1816,23 +2370,23 @@ read_line_header (struct backtrace_state hdr->filenames = ((const char **) backtrace_alloc (state, hdr->filenames_count * sizeof (char *), - line_buf->error_callback, - line_buf->data)); + hdr_buf->error_callback, + hdr_buf->data)); if (hdr->filenames == NULL) return 0; i = 0; - while (*hdr_buf.buf != '\0') + while (*hdr_buf->buf != '\0') { const char *filename; uint64_t dir_index; - if (hdr_buf.reported_underflow) + if (hdr_buf->reported_underflow) return 0; - filename = read_string (&hdr_buf); + filename = read_string (hdr_buf); if (filename == NULL) return 0; - dir_index = read_uleb128 (&hdr_buf); + dir_index = read_uleb128 (hdr_buf); if (IS_ABSOLUTE_PATH (filename) || (dir_index == 0 && u->comp_dir == NULL)) hdr->filenames[i] = filename; @@ -1849,16 +2403,16 @@ read_line_header (struct backtrace_state dir = hdr->dirs[dir_index - 1]; else { - dwarf_buf_error (line_buf, + dwarf_buf_error (hdr_buf, ("invalid directory index in " "line number program header")); return 0; } dir_len = strlen (dir); filename_len = strlen (filename); - s = ((char *) - backtrace_alloc (state, dir_len + filename_len + 2, - line_buf->error_callback, line_buf->data)); + s = ((char *) backtrace_alloc (state, dir_len + filename_len + 2, + hdr_buf->error_callback, + hdr_buf->data)); if (s == NULL) return 0; memcpy (s, dir, dir_len); @@ -1871,12 +2425,258 @@ read_line_header (struct backtrace_state } /* Ignore the modification time and size. */ - read_uleb128 (&hdr_buf); - read_uleb128 (&hdr_buf); + read_uleb128 (hdr_buf); + read_uleb128 (hdr_buf); ++i; } + return 1; +} + +/* Read a single version 5 LNCT entry for a directory or file name in a + line header. Sets *STRING to the resulting name, ignoring other + data. Return 1 on success, 0 on failure. */ + +static int +read_lnct (struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, struct dwarf_buf *hdr_buf, + const struct line_header *hdr, size_t formats_count, + const struct line_header_format *formats, const char **string) +{ + size_t i; + const char *dir; + const char *path; + + dir = NULL; + path = NULL; + for (i = 0; i < formats_count; i++) + { + struct attr_val val; + + if (!read_attribute (formats[i].form, 0, hdr_buf, u->is_dwarf64, + u->version, hdr->addrsize, &ddata->dwarf_sections, + ddata->altlink, &val)) + return 0; + switch (formats[i].lnct) + { + case DW_LNCT_path: + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, hdr_buf->error_callback, hdr_buf->data, + &path)) + return 0; + break; + case DW_LNCT_directory_index: + if (val.encoding == ATTR_VAL_UINT) + { + if (val.u.uint >= hdr->dirs_count) + { + dwarf_buf_error (hdr_buf, + ("invalid directory index in " + "line number program header")); + return 0; + } + dir = hdr->dirs[val.u.uint]; + } + break; + default: + /* We don't care about timestamps or sizes or hashes. */ + break; + } + } + + if (path == NULL) + { + dwarf_buf_error (hdr_buf, + "missing file name in line number program header"); + return 0; + } + + if (dir == NULL) + *string = path; + else + { + size_t dir_len; + size_t path_len; + char *s; + + dir_len = strlen (dir); + path_len = strlen (path); + s = (char *) backtrace_alloc (state, dir_len + path_len + 2, + hdr_buf->error_callback, hdr_buf->data); + if (s == NULL) + return 0; + memcpy (s, dir, dir_len); + /* FIXME: If we are on a DOS-based file system, and the + directory or the path name use backslashes, then we should + use a backslash here. */ + s[dir_len] = '/'; + memcpy (s + dir_len + 1, path, path_len + 1); + *string = s; + } + + return 1; +} + +/* Read a set of DWARF 5 line header format entries, setting *PCOUNT + and *PPATHS. Return 1 on success, 0 on failure. */ + +static int +read_line_header_format_entries (struct backtrace_state *state, + struct dwarf_data *ddata, + struct unit *u, + struct dwarf_buf *hdr_buf, + struct line_header *hdr, + size_t *pcount, + const char ***ppaths) +{ + size_t formats_count; + struct line_header_format *formats; + size_t paths_count; + const char **paths; + size_t i; + int ret; + + formats_count = read_byte (hdr_buf); + if (formats_count == 0) + formats = NULL; + else + { + formats = ((struct line_header_format *) + backtrace_alloc (state, + (formats_count + * sizeof (struct line_header_format)), + hdr_buf->error_callback, + hdr_buf->data)); + if (formats == NULL) + return 0; + + for (i = 0; i < formats_count; i++) + { + formats[i].lnct = (int) read_uleb128(hdr_buf); + formats[i].form = (enum dwarf_form) read_uleb128 (hdr_buf); + } + } + + paths_count = read_uleb128 (hdr_buf); + if (paths_count == 0) + { + *pcount = 0; + *ppaths = NULL; + ret = 1; + goto exit; + } + + paths = ((const char **) + backtrace_alloc (state, paths_count * sizeof (const char *), + hdr_buf->error_callback, hdr_buf->data)); + if (paths == NULL) + { + ret = 0; + goto exit; + } + for (i = 0; i < paths_count; i++) + { + if (!read_lnct (state, ddata, u, hdr_buf, hdr, formats_count, + formats, &paths[i])) + { + backtrace_free (state, paths, + paths_count * sizeof (const char *), + hdr_buf->error_callback, hdr_buf->data); + ret = 0; + goto exit; + } + } + + *pcount = paths_count; + *ppaths = paths; + + ret = 1; + + exit: + if (formats != NULL) + backtrace_free (state, formats, + formats_count * sizeof (struct line_header_format), + hdr_buf->error_callback, hdr_buf->data); + + return ret; +} + +/* Read the line header. Return 1 on success, 0 on failure. */ + +static int +read_line_header (struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf, + struct line_header *hdr) +{ + uint64_t hdrlen; + struct dwarf_buf hdr_buf; + + hdr->version = read_uint16 (line_buf); + if (hdr->version < 2 || hdr->version > 5) + { + dwarf_buf_error (line_buf, "unsupported line number version"); + return 0; + } + + if (hdr->version < 5) + hdr->addrsize = u->addrsize; + else + { + hdr->addrsize = read_byte (line_buf); + /* We could support a non-zero segment_selector_size but I doubt + we'll ever see it. */ + if (read_byte (line_buf) != 0) + { + dwarf_buf_error (line_buf, + "non-zero segment_selector_size not supported"); + return 0; + } + } + + hdrlen = read_offset (line_buf, is_dwarf64); + + hdr_buf = *line_buf; + hdr_buf.left = hdrlen; + + if (!advance (line_buf, hdrlen)) + return 0; + + hdr->min_insn_len = read_byte (&hdr_buf); + if (hdr->version < 4) + hdr->max_ops_per_insn = 1; + else + hdr->max_ops_per_insn = read_byte (&hdr_buf); + + /* We don't care about default_is_stmt. */ + read_byte (&hdr_buf); + + hdr->line_base = read_sbyte (&hdr_buf); + hdr->line_range = read_byte (&hdr_buf); + + hdr->opcode_base = read_byte (&hdr_buf); + hdr->opcode_lengths = hdr_buf.buf; + if (!advance (&hdr_buf, hdr->opcode_base - 1)) + return 0; + + if (hdr->version < 5) + { + if (!read_v2_paths (state, u, &hdr_buf, hdr)) + return 0; + } + else + { + if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr, + &hdr->dirs_count, + &hdr->dirs)) + return 0; + if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr, + &hdr->filenames_count, + &hdr->filenames)) + return 0; + } + if (hdr_buf.reported_underflow) return 0; @@ -1942,7 +2742,7 @@ read_line_program (struct backtrace_stat lineno = 1; break; case DW_LNE_set_address: - address = read_address (line_buf, u->addrsize); + address = read_address (line_buf, hdr->addrsize); break; case DW_LNE_define_file: { @@ -1965,7 +2765,7 @@ read_line_program (struct backtrace_stat size_t f_len; char *p; - if (dir_index == 0) + if (dir_index == 0 && hdr->version < 5) dir = u->comp_dir; else if (dir_index - 1 < hdr->dirs_count) dir = hdr->dirs[dir_index - 1]; @@ -2129,7 +2929,7 @@ read_line_info (struct backtrace_state * len = read_initial_length (&line_buf, &is_dwarf64); line_buf.left = len; - if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr)) + if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr)) goto fail; if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec)) @@ -2287,11 +3087,9 @@ read_referenced_name (struct dwarf_data { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, &unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_sections.data[DEBUG_STR], - ddata->dwarf_sections.size[DEBUG_STR], - ddata->altlink, &val)) + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + &unit_buf, u->is_dwarf64, u->version, u->addrsize, + &ddata->dwarf_sections, ddata->altlink, &val)) return NULL; switch (abbrev->attrs[i].name) @@ -2302,15 +3100,26 @@ read_referenced_name (struct dwarf_data normally not mangled. */ if (ret != NULL) break; - if (val.encoding == ATTR_VAL_STRING) - ret = val.u.string; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, error_callback, data, &ret)) + return NULL; break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: /* First name preference: override all. */ - if (val.encoding == ATTR_VAL_STRING) - return val.u.string; + { + const char *s; + + s = NULL; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, error_callback, data, &s)) + return NULL; + if (s != NULL) + return s; + } break; case DW_AT_specification: @@ -2430,19 +3239,28 @@ read_function_entry (struct backtrace_st { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_sections.data[DEBUG_STR], - ddata->dwarf_sections.size[DEBUG_STR], + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + unit_buf, u->is_dwarf64, u->version, + u->addrsize, &ddata->dwarf_sections, ddata->altlink, &val)) return 0; /* The compile unit sets the base address for any address ranges in the function entries. */ if (abbrev->tag == DW_TAG_compile_unit - && abbrev->attrs[i].name == DW_AT_low_pc - && val.encoding == ATTR_VAL_ADDRESS) - base = val.u.uint; + && abbrev->attrs[i].name == DW_AT_low_pc) + { + if (val.encoding == ATTR_VAL_ADDRESS) + base = val.u.uint; + else if (val.encoding == ATTR_VAL_ADDRESS_INDEX) + { + if (!resolve_addr_index (&ddata->dwarf_sections, + u->addr_base, u->addrsize, + ddata->is_bigendian, val.u.uint, + error_callback, data, &base)) + return 0; + } + } if (is_function) { @@ -2495,18 +3313,31 @@ read_function_entry (struct backtrace_st /* Third name preference: don't override. */ if (function->name != NULL) break; - if (val.encoding == ATTR_VAL_STRING) - function->name = val.u.string; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, + u->str_offsets_base, &val, + error_callback, data, &function->name)) + return 0; break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: /* First name preference: override all. */ - if (val.encoding == ATTR_VAL_STRING) - { - function->name = val.u.string; - have_linkage_name = 1; - } + { + const char *s; + + s = NULL; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, + u->str_offsets_base, &val, + error_callback, data, &s)) + return 0; + if (s != NULL) + { + function->name = s; + have_linkage_name = 1; + } + } break; case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: Index: elf.c =================================================================== --- elf.c (revision 279211) +++ elf.c (working copy) @@ -346,6 +346,10 @@ static const char * const dwarf_section_ ".debug_abbrev", ".debug_ranges", ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" }; /* Information we gather for the sections we care about. */ Index: internal.h =================================================================== --- internal.h (revision 279211) +++ internal.h (working copy) @@ -295,6 +295,10 @@ enum dwarf_section DEBUG_ABBREV, DEBUG_RANGES, DEBUG_STR, + DEBUG_ADDR, + DEBUG_STR_OFFSETS, + DEBUG_LINE_STR, + DEBUG_RNGLISTS, DEBUG_MAX }; Index: pecoff.c =================================================================== --- pecoff.c (revision 279211) +++ pecoff.c (working copy) @@ -141,7 +141,11 @@ static const char * const debug_section_ ".debug_line", ".debug_abbrev", ".debug_ranges", - ".debug_str" + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" }; /* Information we gather for the sections we care about. */ Index: xcoff.c =================================================================== --- xcoff.c (revision 279211) +++ xcoff.c (working copy) @@ -1286,6 +1286,8 @@ xcoff_add (struct backtrace_state *state + (dwsect[i].offset - min_offset)); } + memset (&dwarf_sections, 0, sizeof dwarf_sections); + dwarf_sections.data[DEBUG_INFO] = dwsect[DEBUG_INFO].data; dwarf_sections.size[DEBUG_INFO] = dwsect[DEBUG_INFO].size; #if BACKTRACE_XCOFF_SIZE == 32