From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gnu.wildebeest.org (wildebeest.demon.nl [212.238.236.112]) by sourceware.org (Postfix) with ESMTPS id 9E4F43858010 for ; Sun, 17 Jan 2021 22:24:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 9E4F43858010 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=klomp.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mark@klomp.org Received: from tarox.wildebeest.org (tarox.wildebeest.org [172.31.17.39]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by gnu.wildebeest.org (Postfix) with ESMTPSA id 6D08430015C3; Sun, 17 Jan 2021 23:24:24 +0100 (CET) Received: by tarox.wildebeest.org (Postfix, from userid 1000) id 1F72040007B5; Sun, 17 Jan 2021 23:24:24 +0100 (CET) From: Mark Wielaard To: dwz@sourceware.org Cc: Mark Wielaard Subject: [PATCH] Handle DW_FORM_implicit_const Date: Sun, 17 Jan 2021 23:24:00 +0100 Message-Id: <20210117222400.22763-1-mark@klomp.org> X-Mailer: git-send-email 2.18.4 X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: dwz@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Dwz mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 17 Jan 2021 22:24:34 -0000 This handles DW_FORM_implicit_const. This form keeps the actual data value in the abbrev. This makes things tricky because we have to decide where to keep (a reference to) the value and when to update the value if necessary. To not bloat each abbrev_attr struct we add an int64 *values to the abbrev_tag struct which points to an array of values at the end. We only allocate values up to the highest implicit_const form (if any) when we know those upfront. In compute_abbrevs and recompute_abbrevs we have to allocate a maximum number of values (just like we have to allocate the maximum number of attributes). But when cloning those we reduce the number of values (and attributes) to the minimum needed. To keep the values field up to date always call pool_clone_abbrev when copying an abbrev. When a DW_AT_decl_file or DW_AT_call_file attribute references a file number using DW_FORM_implicit_const we need to be careful when updating the value for a new file table. The abbrev can be used by more than one DIE, but the value only needs to be updated once. We use the fact that file references are positive, but an implicit_const is signed. When updating we negate the new file number and don't update a negative file number. Negative file numbers are made positive again when calculating the new abbrev sizes in compute_abbrevs. * dwz.c (write_sleb128): New macro. (struct abbrev_tag): Add values pointer field. (pool_clone_abbrev): New function. (abbrev_eq2): Handle DW_FORM_implicit_const by also comparing the value. (compute_abbrev_hash): Likewise. (read_abbrev): Read DW_FORM_implicit_const. Keep track of highest_implicit_value_ndx. Allocate values. (get_AT): Return pointer to value for DW_FORM_implicit_const. (get_AT_int): Return value of DW_FORM_implicit_const. (checksum_die): Get value for DW_AT_decl_file or DW_AT_call_file from DW_FORM_implicit_const, skip for others. (checksum_ref_die): Likewise. (die_eq_1): Likewise. (mark_refs): Handle DW_FORM_implicit_const. (read_debug_info): Handle DW_FORM_implicit_const, set cu_lang for compile/partial units. (size_of_sleb128): New function. (build_abbrevs_for_die): Handle DW_FORM_implicit_const value. Use pool_clone_abbrev. (abbrev_cmp): Also compare DW_FORM_implicit_const values. (compute_abbrevs): Use pool_clone_abbrev. Switch any negative file implicit_const values back before calculating size. (write_abbrev): Also write DW_FORM_implicit_const value. (write_die): Handle DW_FORM_implicit_const, but don't change form/value for DW_AT_decl_file and DW_AT_call_file. (recompute_abbrevs): Alloc space for max_nattr values, set abbrev_tag values field. --- dwz.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 215 insertions(+), 55 deletions(-) diff --git a/dwz.c b/dwz.c index f8f2910..b3cf346 100644 --- a/dwz.c +++ b/dwz.c @@ -399,6 +399,25 @@ typedef struct } \ while (0) +#define write_sleb128(ptr, val) \ + do \ + { \ + int64_t valv = (val); \ + bool more; \ + do \ + { \ + unsigned char c = valv & 0x7f; \ + valv >>= 7; \ + more = ((valv != 0 || (c & 0x40) != 0) \ + && (valv != -1 || (c & 0x40) == 0)); \ + if (more) \ + c |= 0x80; \ + *ptr++ = c; \ + } \ + while (more); \ + } \ + while (0) + /* Macro to skip a uleb128 or sleb128 number and update ptr to the end of the number. */ #define skip_leb128(ptr) \ @@ -898,6 +917,8 @@ struct abbrev_tag bool children; /* True if any typed DWARF opcodes refer to this. */ bool op_type_referenced; + /* The values of DW_FORM_implicit_const attribute forms. */ + int64_t *values; /* Attribute/form pairs. */ struct abbrev_attr attr[0]; }; @@ -1204,6 +1225,34 @@ pool_destroy (void) (struct name *) pool_alloc_1 (offsetof (struct align_##name, s), size) #endif +static struct abbrev_tag * +pool_clone_abbrev (struct abbrev_tag *t) +{ + struct abbrev_tag *newt; + size_t newt_size; + unsigned nvalue = 0; + if (t->values != NULL) + { + unsigned i; + for (i = 0; i < t->nattr; i++) + if (t->attr[i].form == DW_FORM_implicit_const) + nvalue = i + 1; + } + newt_size = (sizeof (*newt) + + t->nattr * sizeof (struct abbrev_attr) + + nvalue * sizeof (int64_t)); + newt = pool_alloc (abbrev_tag, newt_size); + memcpy (newt, t, newt_size - (nvalue * sizeof (int64_t))); + if (nvalue == 0) + newt->values = NULL; + else + { + newt->values = (int64_t *) &newt->attr[newt->nattr]; + memcpy (newt->values, t->values, nvalue * sizeof (int64_t)); + } + return newt; +} + /* Hash function in first abbrev hash table as well as cu->cu_new_abbrev htab. */ static hashval_t @@ -1239,7 +1288,9 @@ abbrev_eq2 (const void *p, const void *q) return 0; for (i = 0; i < t1->nattr; i++) if (t1->attr[i].attr != t2->attr[i].attr - || t1->attr[i].form != t2->attr[i].form) + || t1->attr[i].form != t2->attr[i].form + || (t1->attr[i].form == DW_FORM_implicit_const + && t1->values[i] != t2->values[i])) return 0; return 1; } @@ -1257,6 +1308,8 @@ compute_abbrev_hash (struct abbrev_tag *t) { t->hash = iterative_hash_object (t->attr[i].attr, t->hash); t->hash = iterative_hash_object (t->attr[i].form, t->hash); + if (t->attr[i].form == DW_FORM_implicit_const) + t->hash = iterative_hash_object (t->values[i], t->hash); } } @@ -1278,6 +1331,7 @@ read_abbrev (DSO *dso, unsigned char *ptr) while ((attr = read_uleb128 (ptr)) != 0) { + int highest_implicit_value_ndx = -1; unsigned int nattr = 0; unsigned char *p = ptr; @@ -1287,7 +1341,12 @@ read_abbrev (DSO *dso, unsigned char *ptr) { nattr++; form = read_uleb128 (p); - if (form == 2 + if (form == DW_FORM_implicit_const) + { + skip_leb128 (p); + highest_implicit_value_ndx = nattr - 1; + } + else if (form == 2 || (form > DW_FORM_flag_present && !(form == DW_FORM_ref_sig8 || form == DW_FORM_data16 @@ -1308,19 +1367,25 @@ read_abbrev (DSO *dso, unsigned char *ptr) } t = pool_alloc (abbrev_tag, - sizeof (*t) + nattr * sizeof (struct abbrev_attr)); + sizeof (*t) + nattr * sizeof (struct abbrev_attr) + + sizeof (int64_t) * (highest_implicit_value_ndx + 1)); t->entry = attr; t->hash = attr; - t->nattr = 0; + t->nattr = nattr; t->nusers = 0; t->tag = read_uleb128 (ptr); t->children = *ptr++ == DW_CHILDREN_yes; t->op_type_referenced = false; + t->values = (highest_implicit_value_ndx >= 0 + ? (int64_t *) &t->attr[nattr] : NULL); + nattr = 0; while ((attr = read_uleb128 (ptr)) != 0) { form = read_uleb128 (ptr); - t->attr[t->nattr].attr = attr; - t->attr[t->nattr++].form = form; + if (form == DW_FORM_implicit_const) + t->values[nattr] = read_sleb128 (ptr); + t->attr[nattr].attr = attr; + t->attr[nattr++].form = form; } skip_leb128 (ptr); if (t->nattr > max_nattr) @@ -2164,6 +2229,8 @@ get_AT (dw_die_ref die, enum dwarf_attribute at, enum dwarf_form *formp) if (t->attr[i].attr == at) { *formp = form; + if (form == DW_FORM_implicit_const) + return (unsigned char *) &t->values[i]; return ptr; } @@ -2212,6 +2279,8 @@ get_AT_int (dw_die_ref die, enum dwarf_attribute at, bool *present, case DW_FORM_ref_udata: case DW_FORM_udata: return read_uleb128 (ptr); + case DW_FORM_implicit_const: + return *(uint64_t *)ptr; /* See get_AT. */ default: *present = false; return 0; @@ -3371,6 +3440,8 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) case DW_FORM_data8: value = read_64 (ptr); handled = true; break; case DW_FORM_udata: value = read_uleb128 (ptr); handled = true; break; + case DW_FORM_implicit_const: + value = t->values[i]; handled = true; break; default: error (0, 0, "%s: Unhandled %s for %s", dso->filename, get_DW_FORM_str (form), @@ -3505,6 +3576,7 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) ptr += ptr_size; break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -3937,6 +4009,7 @@ checksum_ref_die (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, ptr += ptr_size; break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -4629,6 +4702,7 @@ die_eq_1 (dw_cu_ref cu1, dw_cu_ref cu2, case DW_FORM_data4: value1 = read_32 (ptr1); break; case DW_FORM_data8: value1 = read_64 (ptr1); break; case DW_FORM_udata: value1 = read_uleb128 (ptr1); break; + case DW_FORM_implicit_const: value1 = t1->values[i]; break; default: abort (); } switch (form2) @@ -4638,6 +4712,7 @@ die_eq_1 (dw_cu_ref cu1, dw_cu_ref cu2, case DW_FORM_data4: value2 = read_32 (ptr2); break; case DW_FORM_data8: value2 = read_64 (ptr2); break; case DW_FORM_udata: value2 = read_uleb128 (ptr2); break; + case DW_FORM_implicit_const: value2 = t2->values[j]; break; default: abort (); } if (ignore_locus) @@ -4758,6 +4833,7 @@ die_eq_1 (dw_cu_ref cu1, dw_cu_ref cu2, ptr2 += ptr_size; break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -5850,6 +5926,7 @@ mark_refs (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, int mode) ptr += ptr_size; break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -6803,6 +6880,13 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) break; case DW_FORM_flag_present: break; + case DW_FORM_implicit_const: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + cu->lang = t->values[i]; + break; case DW_FORM_data1: if (lang_p && (die->die_tag == DW_TAG_compile_unit @@ -9495,6 +9579,24 @@ size_of_uleb128 (uint64_t val) return size; } +/* Return number of bytes needed to encode VAL using + sleb128. */ +static unsigned int +size_of_sleb128 (int64_t val) +{ + unsigned int size = 0; + unsigned char c; + do + { + c = val & 0x7f; + val >>= 7; + size++; + } + while ((val != 0 || (c & 0x40) != 0) + && (val != -1 || (c & 0x40) == 0)); + return size; +} + /* Hash table mapping original file IDs to new ids. */ static htab_t line_htab; /* Current new maximum file ID. */ @@ -10319,6 +10421,8 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, { t->attr[i].attr = reft->attr[i].attr; t->attr[i].form = reft->attr[i].form; + if (t->attr[i].form == DW_FORM_implicit_const) + t->values[i] = reft->values[i]; } t->nattr = reft->nattr; } @@ -10331,7 +10435,7 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, uint32_t form = reft->attr[i].form; size_t len = 0; dw_die_ref refd; - uint64_t value; + uint64_t value = 0; unsigned char *orig_ptr = ptr; while (form == DW_FORM_indirect) @@ -10348,30 +10452,40 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, case DW_FORM_data4: value = read_32 (ptr); break; case DW_FORM_data8: value = read_64 (ptr); break; case DW_FORM_udata: value = read_uleb128 (ptr); break; + case DW_FORM_implicit_const: break; default: error (0, 0, "Unhandled %s for %s", get_DW_FORM_str (form), get_DW_AT_str (reft->attr[i].attr)); return 1; } - value = line_htab_lookup (refcu, value); - if (value <= 0xff) + /* Note that the value is only used for calculating the + DIE size and possibly change form. Doesn't change the + implicit_const from or value. */ + if (form != DW_FORM_implicit_const) { - form = DW_FORM_data1; - die->die_size++; - } - else if (value <= 0xffff) - { - form = DW_FORM_data2; - die->die_size += 2; - } - else - { - form = DW_FORM_data4; - die->die_size += 4; + value = line_htab_lookup (refcu, value); + if (value <= 0xff) + { + form = DW_FORM_data1; + die->die_size++; + } + else if (value <= 0xffff) + { + form = DW_FORM_data2; + die->die_size += 2; + } + else + { + form = DW_FORM_data4; + die->die_size += 4; + } } t->attr[j].attr = reft->attr[i].attr; - t->attr[j++].form = form; + t->attr[j].form = form; + if (form == DW_FORM_implicit_const) + t->values[j] = reft->values[i]; + j++; continue; } @@ -10475,6 +10589,7 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, } break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -10657,7 +10772,10 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, if (form == DW_FORM_block1) ptr += len; t->attr[j].attr = reft->attr[i].attr; - t->attr[j++].form = reft->attr[i].form; + t->attr[j].form = reft->attr[i].form; + if (reft->attr[i].form == DW_FORM_implicit_const) + t->values[j] = reft->values[i]; + j++; die->die_size += ptr - orig_ptr; } t->nattr = j; @@ -10785,11 +10903,7 @@ build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, } else { - struct abbrev_tag *newt - = pool_alloc (abbrev_tag, - sizeof (*newt) + t->nattr * sizeof (struct abbrev_attr)); - memcpy (newt, t, - sizeof (*newt) + t->nattr * sizeof (struct abbrev_attr)); + struct abbrev_tag *newt = pool_clone_abbrev (t); *slot = newt; die->u.p2.die_new_abbrev = newt; } @@ -10882,6 +10996,13 @@ abbrev_cmp (const void *p, const void *q) return -1; if (t1->attr[i].form > t2->attr[i].form) return 1; + if (t1->attr[i].form == DW_FORM_implicit_const) + { + if (t1->values[i] < t2->values[i]) + return -1; + if (t1->values[i] > t2->values[i]) + return 1; + } } return 0; } @@ -11070,7 +11191,9 @@ compute_abbrevs (DSO *dso) t = (struct abbrev_tag *) obstack_alloc (&ob2, sizeof (*t) - + (max_nattr + 4) * sizeof (struct abbrev_attr)); + + (max_nattr + 4) * sizeof (struct abbrev_attr) + + (max_nattr + 4) * sizeof (int64_t)); + t->values = (int64_t *) &t->attr[max_nattr + 4]; for (cu = first_cu, ncus = 0; cu; cu = cu->cu_next) { unsigned int intracu, ndies = 0, tagsize = 0, nchildren = 0; @@ -11327,13 +11450,7 @@ compute_abbrevs (DSO *dso) { struct abbrev_tag *newt; arr[k]->entry = ++entry; - newt = pool_alloc (abbrev_tag, - sizeof (*newt) - + arr[k]->nattr - * sizeof (struct abbrev_attr)); - memcpy (newt, arr[k], - sizeof (*newt) - + arr[k]->nattr * sizeof (struct abbrev_attr)); + newt = pool_clone_abbrev (arr[k]); *slot = newt; } } @@ -11432,13 +11549,7 @@ compute_abbrevs (DSO *dso) { struct abbrev_tag *newt; arr[k]->entry = ++entry; - newt = pool_alloc (abbrev_tag, - sizeof (*newt) - + arr[k]->nattr - * sizeof (struct abbrev_attr)); - memcpy (newt, arr[k], - sizeof (*newt) - + arr[k]->nattr * sizeof (struct abbrev_attr)); + newt = pool_clone_abbrev (arr[k]); *slot = newt; } } @@ -11481,6 +11592,21 @@ compute_abbrevs (DSO *dso) { abbrev_size += size_of_uleb128 (arr[i]->attr[j].attr); abbrev_size += size_of_uleb128 (arr[i]->attr[j].form); + if (arr[i]->attr[j].form == DW_FORM_implicit_const) + { + /* If this is a shared abbrev for a file reference + attribute, update to the new file number (in the + mulifile .debug_line). Note that this might + change the abbrev size... */ + if (unlikely (wr_multifile || op_multifile) + && (arr[i]->attr[j].attr == DW_AT_decl_file + || arr[i]->attr[j].attr == DW_AT_call_file)) + { + if (arr[i]->values[j] < 0) + arr[i]->values[j] = -arr[i]->values[j]; + } + abbrev_size += size_of_sleb128 (arr[i]->values[j]); + } } abbrev_size += 2; } @@ -11571,6 +11697,8 @@ write_abbrev (void) { write_uleb128 (ptr, arr[i]->attr[j].attr); write_uleb128 (ptr, arr[i]->attr[j].form); + if (arr[i]->attr[j].form == DW_FORM_implicit_const) + write_sleb128 (ptr, arr[i]->values[j]); } *ptr++ = 0; *ptr++ = 0; @@ -11932,22 +12060,51 @@ write_die (unsigned char *ptr, dw_cu_ref cu, dw_die_ref die, && (reft->attr[i].attr == DW_AT_decl_file || reft->attr[i].attr == DW_AT_call_file)) { + bool update = false; switch (form) { - case DW_FORM_data1: value = read_8 (inptr); break; - case DW_FORM_data2: value = read_16 (inptr); break; - case DW_FORM_data4: value = read_32 (inptr); break; - case DW_FORM_data8: value = read_64 (inptr); break; - case DW_FORM_udata: value = read_uleb128 (inptr); break; + case DW_FORM_data1: + value = read_8 (inptr); + update = true; + break; + case DW_FORM_data2: + value = read_16 (inptr); + update = true; + break; + case DW_FORM_data4: + value = read_32 (inptr); + update = true; + break; + case DW_FORM_data8: + value = read_64 (inptr); + update = true; + break; + case DW_FORM_udata: + value = read_uleb128 (inptr); + update = true; + break; + case DW_FORM_implicit_const: + /* Negative means, already transformed. */ + if (reft->values[i] >= 0) + update = true; + value = reft->values[i]; + break; default: abort (); } - value = line_htab_lookup (refcu, value); - switch (t->attr[j].form) + if (update) { - case DW_FORM_data1: write_8 (ptr, value); break; - case DW_FORM_data2: write_16 (ptr, value); break; - case DW_FORM_data4: write_32 (ptr, value); break; - default: abort (); + value = line_htab_lookup (refcu, value); + switch (t->attr[j].form) + { + case DW_FORM_data1: write_8 (ptr, value); break; + case DW_FORM_data2: write_16 (ptr, value); break; + case DW_FORM_data4: write_32 (ptr, value); break; + case DW_FORM_udata: write_uleb128 (ptr, value); break; + case DW_FORM_implicit_const: + reft->values[i] = -value; /* Note, negated. */ + break; + default: abort (); + } } j++; continue; @@ -12039,6 +12196,7 @@ write_die (unsigned char *ptr, dw_cu_ref cu, dw_die_ref die, } break; case DW_FORM_flag_present: + case DW_FORM_implicit_const: break; case DW_FORM_flag: case DW_FORM_data1: @@ -12375,7 +12533,9 @@ recompute_abbrevs (dw_cu_ref cu, unsigned int cu_size) t = (struct abbrev_tag *) obstack_alloc (&ob2, sizeof (*t) - + (max_nattr + 4) * sizeof (struct abbrev_attr)); + + (max_nattr + 4) * sizeof (struct abbrev_attr) + + (max_nattr + 4) * sizeof (int64_t)); + t->values = (int64_t *) &t->attr[max_nattr + 4]; build_abbrevs_for_die (cu->u1.cu_new_abbrev_owner ? cu->u1.cu_new_abbrev_owner->cu_new_abbrev -- 2.18.4