Bug 25398 - [dwz, fixed-needs-testcase] CU referenced from PU
Summary: [dwz, fixed-needs-testcase] CU referenced from PU
Status: RESOLVED FIXED
Alias: None
Product: dwz
Classification: Unclassified
Component: default (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-01-16 10:20 UTC by Tom de Vries
Modified: 2021-03-02 07:59 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tom de Vries 2020-01-16 10:20:12 UTC
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
...
Comment 1 Tom de Vries 2020-01-16 10:23:29 UTC
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)
                        {
...
Comment 2 Tom de Vries 2020-01-16 10:24:49 UTC
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)
...
Comment 3 Tom de Vries 2020-01-16 14:36:44 UTC
Fixed here : https://sourceware.org/git/?p=dwz.git;a=commit;h=bce3238e163b6175f24a6805d2a8b97eed2e8ab7.

Still need a test-case.
Comment 4 Tom de Vries 2021-02-25 14:50:51 UTC
(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
Comment 5 Tom de Vries 2021-03-02 07:59:21 UTC
Still no test-case, but marking as fixed.