Consider the benchmark cc1. When compiled like this: ... $ dwz cc1 -o 1 -lnone --no-odr ... we find this duplicate chain: ... duplicate chain: 6a1a881 O 13b1049c f3237179 rounding enumeration_type (type: 6a1a4ec unsigned int base_type) 6a25115 O 13b1049c f3237179 rounding enumeration_type (type: 6a24dd3 unsigned int base_type) 6a26b99 O 13b1049c f3237179 rounding enumeration_type (type: 6a2685c unsigned int base_type) 6a27e84 O 13b1049c f3237179 rounding enumeration_type (type: 6a27b47 unsigned int base_type) 6a29495 O 13b1049c f3237179 rounding enumeration_type (type: 6a29158 unsigned int base_type) ... which is referencing this (very long, so abbreviated) duplicate chain: ... duplicate chain: 59 O ec21c4f2 ec21c4f2 unsigned int base_type 762fa O ec21c4f2 ec21c4f2 unsigned int base_type 7f7e7 O ec21c4f2 ec21c4f2 unsigned int base_type b4999 O ec21c4f2 ec21c4f2 unsigned int base_type ... 6a19d58 O ec21c4f2 ec21c4f2 unsigned int base_type 6a1a4ec O ec21c4f2 ec21c4f2 unsigned int base_type 6a24dd3 O ec21c4f2 ec21c4f2 unsigned int base_type ... Each of the chains is copied to a partial unit. When we write out the die representing the first duplicate chain, we hit code this in write_die: ... if (refdt->die_dup && refdt->die_op_type_referenced) { if (cu == die_cu (refdt->die_dup)) refd = die_find_dup (refdt, refdt->die_dup, refd); } else if (refdt->die_dup) refd = die_find_dup (refdt, refdt->die_dup, refd); ... with refdt->die_dup && refdt->die_op_type_reference true: ... (gdb) p /x *refdt $5 = { die_offset = 0x6a1a4ec, die_tag = 0x24, die_ck_state = 0x1, die_op_call2_referenced = 0x0, die_op_type_referenced = 0x1, <=== die_named_namespace = 0x0, die_ref_hash_computed = 0x1, die_toplevel = 0x1, die_remove = 0x0, die_no_multifile = 0x0, die_referenced = 0x0, die_intercu_referenced = 0x0, die_collapsed_children = 0x0, die_collapsed_child = 0x0, die_root = 0x0, die_odr_state = 0x0, die_parent = 0x7fffb00a0bf0, die_child = 0x0, die_sib = 0x7fffb00a0ca0, die_abbrev = 0x7fffb009f940, die_size = 0x7, die_ref_seen = 0x0, u = { p1 = {die_hash = 0xaf5b0d28, die_ref_hash = 0x7fff, die_hash2 = 0x28, die_enter = 0x0, die_exit = 0x1}, p2 = {die_new_abbrev = 0x7fffaf5b0d28, die_new_offset = 0x28, die_intracu_udata_size = 0x0} }, die_dup = 0x7fffb025f5d8, <=== die_nextdup = 0x7fffb00dc5c0 } ... and cu and die_cu (refdt->die_dup) being different partial units: ... (gdb) p *cu $6 = { cu_files = 0x0, cu_nfiles = 0, cu_kind = CU_PU, cu_next = 0x7fffaeb8bdf0, cu_offset = 2099, cu_version = 4, cu_comp_dir = 0x0, cu_die = 0x7fffaeb8a840, cu_abbrev = 0x0, cu_new_abbrev = 0x0, u1 = { cu_new_abbrev_owner = 0x7fffb0399ca0, cu_icu = 0x7fffb0399ca0 }, u2 = { cu_new_abbrev_offset = 5832, cu_largest_entry = 5832 }, cu_new_offset = 1244546, cu_chunk = 0, cu_intracu_form = DW_FORM_ref_udata, initial_intracusize = 2, lang = 0 } (gdb) p *die_cu (refdt->die_dup) $7 = { cu_files = 0x0, cu_nfiles = 0, cu_kind = CU_PU, cu_next = 0x7fffb025f630, cu_offset = 1, cu_version = 4, cu_comp_dir = 0x0, cu_die = 0x7fffb025f580, cu_abbrev = 0x0, cu_new_abbrev = 0x0, u1 = { cu_new_abbrev_owner = 0x7fffb042a200, cu_icu = 0x7fffb042a200 }, u2 = { cu_new_abbrev_offset = 9661, cu_largest_entry = 9661 }, cu_new_offset = 28, cu_chunk = 0, cu_intracu_form = DW_FORM_ref_udata, initial_intracusize = 1, lang = 0 } ... so, the die_find_dup is not applied. This results in this DIE in a PU: ... <0><12fd8d>: Abbrev Number: 11 (DW_TAG_partial_unit) ... <1><12fd9b>: Abbrev Number: 7 (DW_TAG_enumeration_type) <12fd9c> DW_AT_name : rounding <12fda0> DW_AT_encoding : 7 (unsigned) <12fda1> DW_AT_byte_size : 4 <12fda2> DW_AT_type : <0x3a81757> <12fda6> DW_AT_decl_file : 10 <12fda7> DW_AT_decl_line : 69 <12fda8> DW_AT_decl_column : 8 <12fda9> DW_AT_sibling : <0x12fde1> ... referring to this DIE in a CU: ... <0><3a8173a>: Abbrev Number: 41 (DW_TAG_compile_unit) <3a81740> DW_AT_name : ../../libdecnumber/decNumber.c ... <1><3a81757>: Abbrev Number: 27 (DW_TAG_base_type) <3a81758> DW_AT_byte_size : 4 <3a81759> DW_AT_encoding : 7 (unsigned) <3a8175a> DW_AT_name : unsigned int ...
Assert that catches the situation: ... diff --git a/dwz.c b/dwz.c index 0087ac7..4188e1a 100644 --- a/dwz.c +++ b/dwz.c @@ -10938,6 +10938,8 @@ write_die (unsigned char *ptr, dw_cu_ref cu, dw_die_ref die, { dw_cu_ref refdcu = die_cu (refd); value = refd->u.p2.die_new_offset; + assert (IMPLIES (cu->cu_kind == CU_PU, + die_cu (refd)->cu_kind == CU_PU)); assert (value && refdcu->cu_kind != CU_ALT); if (t->attr[j].form == DW_FORM_ref_addr) { ...
Tentative patch: ... diff --git a/dwz.c b/dwz.c index 4188e1a..0c58a07 100644 --- a/dwz.c +++ b/dwz.c @@ -10924,7 +10924,7 @@ write_die (unsigned char *ptr, dw_cu_ref cu, dw_die_ref die, refdt = refdt->die_parent; if (refdt->die_dup && refdt->die_op_type_referenced) { - if (cu == die_cu (refdt->die_dup)) + if (cu == die_cu (refdt->die_dup) || cu->cu_kind == CU_PU) refd = die_find_dup (refdt, refdt->die_dup, refd); } else if (refdt->die_dup) ...
Fixed here : https://sourceware.org/git/?p=dwz.git;a=commit;h=bce3238e163b6175f24a6805d2a8b97eed2e8ab7. Still need a test-case.
(In reply to Tom de Vries from comment #1) > Assert that catches the situation: > ... > diff --git a/dwz.c b/dwz.c > index 0087ac7..4188e1a 100644 > --- a/dwz.c > +++ b/dwz.c > @@ -10938,6 +10938,8 @@ write_die (unsigned char *ptr, dw_cu_ref cu, > dw_die_ref die, > { > dw_cu_ref refdcu = die_cu (refd); > value = refd->u.p2.die_new_offset; > + assert (IMPLIES (cu->cu_kind == CU_PU, > + die_cu (refd)->cu_kind == CU_PU)); > assert (value && refdcu->cu_kind != CU_ALT); > if (t->attr[j].form == DW_FORM_ref_addr) > { > ... https://sourceware.org/pipermail/dwz/2021q1/000976.html
Still no test-case, but marking as fixed.