From patchwork Tue Dec 10 03:41:52 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: 1206904 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-515568-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="xEr83F1F"; 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="RVBMqFU9"; 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 47X5V13VtBz9sR7 for ; Tue, 10 Dec 2019 14:42:17 +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=HaTxvp2avgaNO1Jp05yK+29JbLnzcWYuVqlXYuvWdMLGjH U/+oKCdjYTAPalWzZfBOm0z9msRNnkIoEvdRoF9i+QisL/ch6A6qxRfVLfgR+kWW HDLTS5hD73RPbcIquu++/GP3MzW/KTiboY2cQG4nwDYkl1FlEhGQzyY9Dplq0= 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=OPLKOKKtzrk5l77uIEwbv71V8a4=; b=xEr83F1FXn2NsRuW19E8 rkWlLElIXMlsuBZhjImo09tU7iXHJqrU6MWpPafGyhETbupt2JnzMnz313Rbpeov M7txf1r6TrMwQUePQSwZxPss0muYL7LUqgIdfTVGE2/9G495095420nCdgaW8XQ4 /iLCCAXWXT4Ebd2T9S4quCY= Received: (qmail 125172 invoked by alias); 10 Dec 2019 03:42:09 -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 125160 invoked by uid 89); 10 Dec 2019 03:42:09 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-10.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy=recording, 279094, HX-HELO:sk:mail-ed, H*r:sk:mail-ed X-HELO: mail-ed1-f51.google.com Received: from mail-ed1-f51.google.com (HELO mail-ed1-f51.google.com) (209.85.208.51) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 10 Dec 2019 03:42:06 +0000 Received: by mail-ed1-f51.google.com with SMTP id v28so14656106edw.12 for ; Mon, 09 Dec 2019 19:42:05 -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=/vhQbO0uocYzHvSLeNGU2ETT++JyzNPEY/OsTgy1tyg=; b=RVBMqFU9jbMojFkZEs+veV8AqpfSrn3Ak0OOUohPb6vlSKYXLhbbUSIEWubpeW2KUJ wlRmOJWNEU0BQDRe/FQIiXDWaAuIAMYC4GoJnYU95KwxwuEpkZ9QDMjfb7L2Z8RlJ6A7 AcdjKmJDyxoaE91TrAVSXqFrz0hTh/ziCAU6kOgTKCmfWkt+cPXTYmzsITjlcWWhJAUT 9PKF4NjMHQs0OH4MZ1A2HpOURtYkAtV57m28q5+B6lyTdIXE5jDXDKUb5Vcds+bSJID/ JcpVry+kXUKjE+NWt5LT3D0BJx5hpzCKz0/rSsQ7E8XN77maqW9E/+Cyu9KBcrRo1KxO +6Lg== MIME-Version: 1.0 From: Ian Lance Taylor Date: Mon, 9 Dec 2019 19:41:52 -0800 Message-ID: Subject: libbacktrace patch committed: Remove duplication of address handling To: gcc-patches , gofrontend-dev Before this patch libbacktrace duplicated the handling of DW_AT_low_pc, DW_AT_high_pc, and DW_AT_ranges, once to build a mapping from addresses to compilation units, and then again to build a mapping from addresses to functions within a compilation unit. This patch removes the duplication into a pair of functions, one of which takes a function pointer to actually add the appropriate mapping. This is a step toward adding DWARF 5 support, as DWARF 5 requires handling more cases here, and it seemed painful to introduce further duplication. Bootstrapped and ran libbacktrace and Go testsuites on x86_64-pc-linux-gnu. Committed to mainline. Ian Index: dwarf.c =================================================================== --- dwarf.c (revision 279094) +++ dwarf.c (working copy) @@ -945,31 +945,28 @@ function_addrs_search (const void *vkey, return 0; } -/* Add a new compilation unit address range to a vector. Returns 1 on - success, 0 on failure. */ +/* Add a new compilation unit address range to a vector. This is + called via add_ranges. Returns 1 on success, 0 on failure. */ static int -add_unit_addr (struct backtrace_state *state, uintptr_t base_address, - struct unit_addrs addrs, +add_unit_addr (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, backtrace_error_callback error_callback, void *data, - struct unit_addrs_vector *vec) + void *pvec) { + struct unit *u = (struct unit *) rdata; + struct unit_addrs_vector *vec = (struct unit_addrs_vector *) pvec; 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) { p = (struct unit_addrs *) vec->vec.base + (vec->count - 1); - if ((addrs.low == p->high || addrs.low == p->high + 1) - && addrs.u == p->u) + if ((lowpc == p->high || lowpc == p->high + 1) + && u == p->u) { - if (addrs.high > p->high) - p->high = addrs.high; + if (highpc > p->high) + p->high = highpc; return 1; } } @@ -980,8 +977,12 @@ add_unit_addr (struct backtrace_state *s if (p == NULL) return 0; - *p = addrs; + p->low = lowpc; + p->high = highpc; + p->u = u; + ++vec->count; + return 1; } @@ -1262,29 +1263,122 @@ lookup_abbrev (struct abbrevs *abbrevs, return (const struct abbrev *) p; } -/* Add non-contiguous address ranges for a compilation unit. Returns - 1 on success, 0 on failure. */ +/* This struct is used to gather address range information while + reading attributes. We use this while building a mapping from + address ranges to compilation units and then again while mapping + from address ranges to function entries. Normally either + lowpc/highpc is set or ranges is set. */ + +struct pcrange { + uint64_t lowpc; /* The low PC value. */ + int have_lowpc; /* Whether a low PC value was found. */ + 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. */ + uint64_t ranges; /* Offset in ranges section. */ + int have_ranges; /* Whether ranges is valid. */ +}; + +/* Update PCRANGE from an attribute value. */ + +static void +update_pcrange (const struct attr* attr, const struct attr_val* val, + struct pcrange *pcrange) +{ + switch (attr->name) + { + case DW_AT_low_pc: + if (val->encoding == ATTR_VAL_ADDRESS) + { + pcrange->lowpc = val->u.uint; + pcrange->have_lowpc = 1; + } + break; + + case DW_AT_high_pc: + if (val->encoding == ATTR_VAL_ADDRESS) + { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + } + else if (val->encoding == ATTR_VAL_UINT) + { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + pcrange->highpc_is_relative = 1; + } + break; + + case DW_AT_ranges: + if (val->encoding == ATTR_VAL_UINT + || val->encoding == ATTR_VAL_REF_SECTION) + { + pcrange->ranges = val->u.uint; + pcrange->have_ranges = 1; + } + break; + + default: + break; + } +} + +/* 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_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) +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) { struct dwarf_buf ranges_buf; - if (ranges >= dwarf_ranges_size) + if (pcrange->have_lowpc && pcrange->have_highpc) + { + 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 (!pcrange->have_ranges) + { + /* Did not find any address ranges to add. */ + return 1; + } + + if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES]) { error_callback (data, "ranges offset out of range", 0); return 0; } ranges_buf.name = ".debug_ranges"; - ranges_buf.start = dwarf_ranges; - ranges_buf.buf = dwarf_ranges + ranges; - ranges_buf.left = dwarf_ranges_size - ranges; + ranges_buf.start = dwarf_sections->data[DEBUG_RANGES]; + ranges_buf.buf = dwarf_sections->data[DEBUG_RANGES] + pcrange->ranges; + ranges_buf.left = dwarf_sections->size[DEBUG_RANGES] - pcrange->ranges; ranges_buf.is_bigendian = is_bigendian; ranges_buf.error_callback = error_callback; ranges_buf.data = data; @@ -1308,13 +1402,10 @@ add_unit_ranges (struct backtrace_state base = high; else { - struct unit_addrs a; - - a.low = low + base; - a.high = high + base; - a.u = u; - if (!add_unit_addr (state, base_address, a, error_callback, data, - addrs)) + if (!add_range (state, rdata, + low + base + base_address, + high + base + base_address, + error_callback, data, vec)) return 0; } } @@ -1332,9 +1423,7 @@ add_unit_ranges (struct backtrace_state static int find_address_ranges (struct backtrace_state *state, uintptr_t base_address, struct dwarf_buf *unit_buf, - const unsigned char *dwarf_str, size_t dwarf_str_size, - const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, + const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *altlink, backtrace_error_callback error_callback, void *data, struct unit *u, struct unit_addrs_vector *addrs, @@ -1344,13 +1433,7 @@ find_address_ranges (struct backtrace_st { uint64_t code; const struct abbrev *abbrev; - uint64_t lowpc; - int have_lowpc; - uint64_t highpc; - int have_highpc; - int highpc_is_relative; - uint64_t ranges; - int have_ranges; + struct pcrange pcrange; size_t i; code = read_uleb128 (unit_buf); @@ -1364,53 +1447,22 @@ find_address_ranges (struct backtrace_st if (unit_tag != NULL) *unit_tag = abbrev->tag; - lowpc = 0; - have_lowpc = 0; - highpc = 0; - have_highpc = 0; - highpc_is_relative = 0; - ranges = 0; - have_ranges = 0; + memset (&pcrange, 0, sizeof pcrange); 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_str, dwarf_str_size, altlink, &val)) + dwarf_sections->data[DEBUG_STR], + dwarf_sections->size[DEBUG_STR], + altlink, &val)) return 0; switch (abbrev->attrs[i].name) { - case DW_AT_low_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - lowpc = val.u.uint; - have_lowpc = 1; - } - break; - - case DW_AT_high_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - highpc = val.u.uint; - have_highpc = 1; - } - else if (val.encoding == ATTR_VAL_UINT) - { - highpc = val.u.uint; - have_highpc = 1; - highpc_is_relative = 1; - } - break; - - case DW_AT_ranges: - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_SECTION) - { - ranges = val.u.uint; - have_ranges = 1; - } + case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: + update_pcrange (&abbrev->attrs[i], &val, &pcrange); break; case DW_AT_stmt_list: @@ -1440,43 +1492,25 @@ find_address_ranges (struct backtrace_st if (abbrev->tag == DW_TAG_compile_unit || abbrev->tag == DW_TAG_subprogram) { - if (have_ranges) - { - if (!add_unit_ranges (state, base_address, u, ranges, lowpc, - is_bigendian, dwarf_ranges, - dwarf_ranges_size, error_callback, - data, addrs)) - return 0; - } - else if (have_lowpc && have_highpc) - { - struct unit_addrs a; - - if (highpc_is_relative) - highpc += lowpc; - a.low = lowpc; - a.high = highpc; - a.u = u; - - if (!add_unit_addr (state, base_address, a, error_callback, data, - addrs)) - return 0; - } + if (!add_ranges (state, dwarf_sections, base_address, + is_bigendian, u, pcrange.lowpc, &pcrange, + add_unit_addr, (void *) u, error_callback, data, + (void *) addrs)) + return 0; /* If we found the PC range in the DW_TAG_compile_unit, we can stop now. */ if (abbrev->tag == DW_TAG_compile_unit - && (have_ranges || (have_lowpc && have_highpc))) + && (pcrange.have_ranges + || (pcrange.have_lowpc && pcrange.have_highpc))) return 1; } if (abbrev->has_children) { if (!find_address_ranges (state, base_address, unit_buf, - dwarf_str, dwarf_str_size, - dwarf_ranges, dwarf_ranges_size, - is_bigendian, altlink, error_callback, data, - u, addrs, NULL)) + dwarf_sections, is_bigendian, altlink, + error_callback, data, u, addrs, NULL)) return 0; } } @@ -1599,11 +1633,7 @@ build_address_map (struct backtrace_stat u->function_addrs = NULL; u->function_addrs_count = 0; - if (!find_address_ranges (state, base_address, &unit_buf, - dwarf_sections->data[DEBUG_STR], - dwarf_sections->size[DEBUG_STR], - dwarf_sections->data[DEBUG_RANGES], - dwarf_sections->size[DEBUG_RANGES], + if (!find_address_ranges (state, base_address, &unit_buf, dwarf_sections, is_bigendian, altlink, error_callback, data, u, addrs, &unit_tag)) goto fail; @@ -2304,25 +2334,22 @@ read_referenced_name (struct dwarf_data return ret; } -/* Add a single range to U that maps to function. Returns 1 on - success, 0 on error. */ +/* Add a range to a unit that maps to a function. This is called via + add_ranges. Returns 1 on success, 0 on error. */ static int -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) +add_function_range (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *pvec) { + struct function *function = (struct function *) rdata; + struct function_vector *vec = (struct function_vector *) pvec; 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; + p = (struct function_addrs *) vec->vec.base + (vec->count - 1); if ((lowpc == p->high || lowpc == p->high + 1) && function == p->function) { @@ -2341,63 +2368,8 @@ add_function_range (struct backtrace_sta p->low = lowpc; p->high = highpc; p->function = function; - ++vec->count; - return 1; -} - -/* Add PC ranges to U that map to FUNCTION. Returns 1 on success, 0 - on error. */ - -static int -add_function_ranges (struct backtrace_state *state, struct dwarf_data *ddata, - struct unit *u, struct function *function, - uint64_t ranges, uint64_t base, - backtrace_error_callback error_callback, void *data, - struct function_vector *vec) -{ - struct dwarf_buf ranges_buf; - - if (ranges >= ddata->dwarf_sections.size[DEBUG_RANGES]) - { - error_callback (data, "function ranges offset out of range", 0); - return 0; - } - - ranges_buf.name = ".debug_ranges"; - ranges_buf.start = ddata->dwarf_sections.data[DEBUG_RANGES]; - ranges_buf.buf = ddata->dwarf_sections.data[DEBUG_RANGES] + ranges; - ranges_buf.left = ddata->dwarf_sections.size[DEBUG_RANGES] - ranges; - ranges_buf.is_bigendian = ddata->is_bigendian; - ranges_buf.error_callback = error_callback; - ranges_buf.data = data; - ranges_buf.reported_underflow = 0; - - while (1) - { - uint64_t low; - uint64_t high; - - if (ranges_buf.reported_underflow) - return 0; - - low = read_address (&ranges_buf, u->addrsize); - high = read_address (&ranges_buf, u->addrsize); - - if (low == 0 && high == 0) - break; - if (is_highest_address (low, u->addrsize)) - base = high; - else - { - if (!add_function_range (state, ddata, function, low + base, - high + base, error_callback, data, vec)) - return 0; - } - } - - if (ranges_buf.reported_underflow) - return 0; + ++vec->count; return 1; } @@ -2421,13 +2393,7 @@ read_function_entry (struct backtrace_st struct function *function; struct function_vector *vec; size_t i; - uint64_t lowpc; - int have_lowpc; - uint64_t highpc; - int have_highpc; - int highpc_is_relative; - uint64_t ranges; - int have_ranges; + struct pcrange pcrange; int have_linkage_name; code = read_uleb128 (unit_buf); @@ -2458,13 +2424,7 @@ read_function_entry (struct backtrace_st memset (function, 0, sizeof *function); } - lowpc = 0; - have_lowpc = 0; - highpc = 0; - have_highpc = 0; - highpc_is_relative = 0; - ranges = 0; - have_ranges = 0; + memset (&pcrange, 0, sizeof pcrange); have_linkage_name = 0; for (i = 0; i < abbrev->num_attrs; ++i) { @@ -2549,35 +2509,8 @@ read_function_entry (struct backtrace_st } break; - case DW_AT_low_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - lowpc = val.u.uint; - have_lowpc = 1; - } - break; - - case DW_AT_high_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - highpc = val.u.uint; - have_highpc = 1; - } - else if (val.encoding == ATTR_VAL_UINT) - { - highpc = val.u.uint; - have_highpc = 1; - highpc_is_relative = 1; - } - break; - - case DW_AT_ranges: - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_SECTION) - { - ranges = val.u.uint; - have_ranges = 1; - } + case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: + update_pcrange (&abbrev->attrs[i], &val, &pcrange); break; default: @@ -2597,18 +2530,14 @@ read_function_entry (struct backtrace_st if (is_function) { - if (have_ranges) - { - if (!add_function_ranges (state, ddata, u, function, ranges, - base, error_callback, data, vec)) - return 0; - } - else if (have_lowpc && have_highpc) + if (pcrange.have_ranges + || (pcrange.have_lowpc && pcrange.have_highpc)) { - if (highpc_is_relative) - highpc += lowpc; - if (!add_function_range (state, ddata, function, lowpc, highpc, - error_callback, data, vec)) + if (!add_ranges (state, &ddata->dwarf_sections, + ddata->base_address, ddata->is_bigendian, + u, base, &pcrange, add_function_range, + (void *) function, error_callback, data, + (void *) vec)) return 0; } else