public inbox for gdb-prs@sourceware.org
help / color / mirror / Atom feed
From: "clayborg at gmail dot com" <sourceware-bugzilla@sourceware.org>
To: gdb-prs@sourceware.org
Subject: [Bug gdb/29518] New: GDB doesn't handle DW_FORM_ref_addr DIE references correctly with .debug_types sections
Date: Wed, 24 Aug 2022 07:12:09 +0000	[thread overview]
Message-ID: <bug-29518-4717@http.sourceware.org/bugzilla/> (raw)

https://sourceware.org/bugzilla/show_bug.cgi?id=29518

            Bug ID: 29518
           Summary: GDB doesn't handle DW_FORM_ref_addr DIE references
                    correctly with .debug_types sections
           Product: gdb
           Version: HEAD
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: gdb
          Assignee: unassigned at sourceware dot org
          Reporter: clayborg at gmail dot com
  Target Milestone: ---

Created attachment 14292
  --> https://sourceware.org/bugzilla/attachment.cgi?id=14292&action=edit
Binary that reproduces the bug.

We have binaries that have .debug_types sections, so they are compiled with
DWARF4 or earlier, and they use DW_FORM_ref_addr for DIE references. These
DW_FORM_ref_addr are added by an LTO pass. When we load these binaries into
GDB, we end up with internal errors like:

Dwarf Error: Cannot find DIE at 0x37a8eb8

Or we can run into an internal error where we find a type unit that contains
the offset incorrectly:

../../binutils-gdb/gdb/dwarf2/read.c:7702: internal-error: load_full_comp_unit:
Assertion `! this_cu->is_debug_types' failed.



I found the root cause: for each DWARF there is a "dwarf2_per_bfd" structure
defined in gdb/dwarf2/read.h that has a member:

  /* Table of all the compilation units.  This is used to locate
     the target compilation unit of a particular reference.  */
  std::vector<dwarf2_per_cu_data_up> all_comp_units;

This member gets populated with all units from the .debug_info, and if there
are one or more .debug_types sections, they get appended to this list. The
issue is if we ever need to locate a referenced DIE that uses DW_FORM_ref_addr,
which is an absolute offset in the .debug_info, it will end up binary searching
for the containing compile unit in dwarf2_find_containing_comp_unit(...). This
function uses the dwarf2_per_bfd::all_comp_units vector, which is assumed to be
sorted. But since it has both entries from the .debug_info (which start with
offset zero and increase) and any .debug_types sections (which also start with
a section offset of zero and increase), this list is no longer sorted and a
binary search can fail can cause a the "Dwarf Error: Cannot find DIE at"
errors.

This only happens when you have an attribute that uses DW_FORM_ref_addr. Most
references in DWARF use a compile unit related offset like DW_FORM_ref4, so
this is the main reason this issue has not been seen a lot. Our LTO introduces
these absolute references into the DWARF.

Another thing to note is that any type unit from a .debug_types section, or
ones from DWARF5 that are in the .debug_info section, are never searched for by
DIE offset. Type units are only referenced via their type signature which can
easily map to the right type unit. So "all_comp_units" is being used as storage
for the dwarf2_per_cu_data_up and then being used in a binary search, but any
type units should never need to actually be in this list.

I have a quick fix that adds an extra entry to the "dwarf2_per_bfd" structure
that is the index of the last debug info entry from .debug_info. Then it
modifies dwarf2_find_containing_comp_unit(...) to use this index as a high
index when doing the binary search. I don't know GDB's DWARF parser well enough
to know if this is the correct fix. 

I will attach my quick fix in case this helps, but would appreciate any
guidance or help it getting this fixed the right way.

I have a reproduction case attached as a "b.out" binary. To reproduce the
issue:
$ gdb b.out
(gdb) maintenance print type main
../../binutils-gdb/gdb/dwarf2/read.c:7702: internal-error: load_full_comp_unit:
Assertion `! this_cu->is_debug_types' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
0x50a5fc gdb_internal_backtrace_1
        ../../binutils-gdb/gdb/bt-utils.c:122
0x50a69f _Z22gdb_internal_backtracev
        ../../binutils-gdb/gdb/bt-utils.c:168
0xa401bb internal_vproblem
        ../../binutils-gdb/gdb/utils.c:396
0xa4058a _Z15internal_verrorPKciS0_P13__va_list_tag
        ../../binutils-gdb/gdb/utils.c:476
0xbdf9de _Z14internal_errorPKciS0_z
        ../../binutils-gdb/gdbsupport/errors.cc:55
0x65f816 load_full_comp_unit
        ../../binutils-gdb/gdb/dwarf2/read.c:7702
0x685619 follow_die_offset
        ../../binutils-gdb/gdb/dwarf2/read.c:22307
0x68575e follow_die_ref
        ../../binutils-gdb/gdb/dwarf2/read.c:22340
0x683906 lookup_die_type
        ../../binutils-gdb/gdb/dwarf2/read.c:21460
0x683472 die_type
        ../../binutils-gdb/gdb/dwarf2/read.c:21320
0x6778fc read_subroutine_type
        ../../binutils-gdb/gdb/dwarf2/read.c:16524
0x683a3b read_type_die_1
        ../../binutils-gdb/gdb/dwarf2/read.c:21519
0x6839b5 read_type_die
        ../../binutils-gdb/gdb/dwarf2/read.c:21494
0x66b028 read_func_scope
        ../../binutils-gdb/gdb/dwarf2/read.c:12047
0x662956 process_die
        ../../binutils-gdb/gdb/dwarf2/read.c:8648
0x664980 read_file_scope
        ../../binutils-gdb/gdb/dwarf2/read.c:9634
0x6628d3 process_die
        ../../binutils-gdb/gdb/dwarf2/read.c:8635
0x662057 process_full_comp_unit
        ../../binutils-gdb/gdb/dwarf2/read.c:8404
0x65f6a2 process_queue
        ../../binutils-gdb/gdb/dwarf2/read.c:7650
0x6513f1 dw2_do_instantiate_symtab
        ../../binutils-gdb/gdb/dwarf2/read.c:2063
0x65149a dw2_instantiate_symtab
        ../../binutils-gdb/gdb/dwarf2/read.c:2085
0x656911 dw2_expand_symtabs_matching_one
        ../../binutils-gdb/gdb/dwarf2/read.c:3977
0x67d671
_ZN22cooked_index_functions23expand_symtabs_matchingEP7objfileN3gdb13function_viewIFbPKcbEEEPK16lookup_name_infoNS3_IFbS5_EEENS3_IFbP15compunit_symtabEEE10enum_flagsI24block_search_flag_valuesE11domain_enum13search_domain
        ../../binutils-gdb/gdb/dwarf2/read.c:18739
0x936d8f _ZN7objfile13lookup_symbolE10block_enumPKc11domain_enum
        ../../binutils-gdb/gdb/symfile-debug.c:276
0x9558c5 lookup_symbol_via_quick_fns
        ../../binutils-gdb/gdb/symtab.c:2451
0x955d16 lookup_symbol_in_objfile
        ../../binutils-gdb/gdb/symtab.c:2600
0x955e7d operator()
        ../../binutils-gdb/gdb/symtab.c:2666
0x96166b operator()
        ../../binutils-gdb/gdb/../gdbsupport/function-view.h:304
0x961692 _FUN
        ../../binutils-gdb/gdb/../gdbsupport/function-view.h:298
0x84dd25 _ZNK3gdb13function_viewIFbP7objfileEEclES2_
        ../../binutils-gdb/gdb/../gdbsupport/function-view.h:288
0x90392e svr4_iterate_over_objfiles_in_search_order
        ../../binutils-gdb/gdb/solib-svr4.c:3161
0x49c1b3
_Z45gdbarch_iterate_over_objfiles_in_search_orderP7gdbarchN3gdb13function_viewIFbP7objfileEEES4_
        ../../binutils-gdb/gdb/gdbarch.c:5027
0x956007 lookup_global_or_static_symbol
        ../../binutils-gdb/gdb/symtab.c:2662
0x956170 _Z20lookup_global_symbolPKcPK5block11domain_enum
        ../../binutils-gdb/gdb/symtab.c:2717
0x5c86f9 cp_lookup_bare_symbol
        ../../binutils-gdb/gdb/cp-namespace.c:205
0x5c969a lookup_namespace_scope
        ../../binutils-gdb/gdb/cp-namespace.c:719
0x5c97c5 _Z25cp_lookup_symbol_nonlocalPK13language_defnPKcPK5block11domain_enum
        ../../binutils-gdb/gdb/cp-namespace.c:754
0x5449ad _ZNK14cplus_language22lookup_symbol_nonlocalEPKcPK5block11domain_enum
        ../../binutils-gdb/gdb/c-lang.c:997
0x954eb7 lookup_symbol_aux
        ../../binutils-gdb/gdb/symtab.c:2170
0x954668
_Z25lookup_symbol_in_languagePKcPK5block11domain_enum8languageP20field_of_this_result
        ../../binutils-gdb/gdb/symtab.c:1965
0x9546e2 _Z13lookup_symbolPKcPK5block11domain_enumP20field_of_this_result
        ../../binutils-gdb/gdb/symtab.c:1977
0x5282ca classify_name
        ../../binutils-gdb/gdb/c-exp.y:3046
0x528968 c_yylex
        ../../binutils-gdb/gdb/c-exp.y:3255
0x51ff8b _Z9c_yyparsev
        /home/gclayton/local/gdb/Debug/gdb/c-exp.c.tmp:1991
0x529427 _Z7c_parseP12parser_state
        ../../binutils-gdb/gdb/c-exp.y:3421
0x7930a3 _ZNK13language_defn6parserEP12parser_state
        ../../binutils-gdb/gdb/language.c:623
0x862973 parse_exp_in_context
        ../../binutils-gdb/gdb/parse.c:515
0x862b4f _Z16parse_expressionPKcP23innermost_block_trackerb
        ../../binutils-gdb/gdb/parse.c:551
0x9f16de _Z22maintenance_print_typePKci
        ../../binutils-gdb/gdb/typeprint.c:709
0x55e2a9 do_simple_func
        ../../binutils-gdb/gdb/cli/cli-decode.c:95
0x562ed5 _Z8cmd_funcP16cmd_list_elementPKci
        ../../binutils-gdb/gdb/cli/cli-decode.c:2516
0x9aa0a2 _Z15execute_commandPKci
        ../../binutils-gdb/gdb/top.c:699
0x6d8250 _Z15command_handlerPKc
        ../../binutils-gdb/gdb/event-top.c:598
0x6d86f7 _Z20command_line_handlerOSt10unique_ptrIcN3gdb13xfree_deleterIcEEE
        ../../binutils-gdb/gdb/event-top.c:842
0x9d2fd7 tui_command_line_handler
        ../../binutils-gdb/gdb/tui/tui-interp.c:104
0x6d7a50 gdb_rl_callback_handler
        ../../binutils-gdb/gdb/event-top.c:230
0xaa6f10 rl_callback_read_char
        ../../../binutils-gdb/readline/readline/callback.c:290
0x6d78cd gdb_rl_callback_read_char_wrapper_noexcept
        ../../binutils-gdb/gdb/event-top.c:188
0x6d7954 gdb_rl_callback_read_char_wrapper
        ../../binutils-gdb/gdb/event-top.c:205
0x6d80a1 _Z19stdin_event_handleriPv
        ../../binutils-gdb/gdb/event-top.c:525
0xbe06ef handle_file_event
        ../../binutils-gdb/gdbsupport/event-loop.cc:549
0xbe0c77 gdb_wait_for_event
        ../../binutils-gdb/gdbsupport/event-loop.cc:670
0xbdfb86 _Z16gdb_do_one_eventv
        ../../binutils-gdb/gdbsupport/event-loop.cc:235
0x7e2d47 start_event_loop
        ../../binutils-gdb/gdb/main.c:411
0x7e2e67 captured_command_loop
        ../../binutils-gdb/gdb/main.c:471
0x7e4635 captured_main
        ../../binutils-gdb/gdb/main.c:1329
0x7e469b _Z8gdb_mainP18captured_main_args
        ../../binutils-gdb/gdb/main.c:1344
0x40de2c main
        ../../binutils-gdb/gdb/gdb.c:32
---------------------
../../binutils-gdb/gdb/dwarf2/read.c:7702: internal-error: load_full_comp_unit:
Assertion `! this_cu->is_debug_types' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) y


The .debug_info has been modified to have a DW_FORM_ref_addr on the first
subprogram:

0x0000002a:   DW_TAG_subprogram [7] *
                DW_AT_low_pc [DW_FORM_addr]     (0x0000000000000630)
                DW_AT_high_pc [DW_FORM_data4]   (0x0000007c)
                DW_AT_frame_base [DW_FORM_exprloc]      (DW_OP_reg6 RBP)
                DW_AT_name [DW_FORM_strp]       ( .debug_str[0x000000ca] =
"main")
                DW_AT_decl_file [DW_FORM_data1]
("/home/gclayton/local/debug-types-absolute-refs/main.cpp")
                DW_AT_decl_line [DW_FORM_data1] (24)
                DW_AT_type [DW_FORM_ref_addr]   (0x0000000000000122 "int")
                DW_AT_external [DW_FORM_flag_present]   (true)

which points to a "int" type in another compile unit:

0x00000122:   DW_TAG_base_type [3]  
                DW_AT_name [DW_FORM_strp]       ( .debug_str[0x000000e7] =
"int")
                DW_AT_encoding [DW_FORM_data1]  (DW_ATE_signed)
                DW_AT_byte_size [DW_FORM_data1] (0x04)

This program also has enough types in .debug_types to trigger the bug and cause
the binary search to actually succeed but find the wrong thing: it finds a type
unit.


In case I can't attach the gdb patch I will include it here:

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 84faeb45238..c8c0628e979 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -2703,6 +2703,8 @@ dwarf2_read_gdb_index
   create_cus_from_index (per_bfd, cu_list, cu_list_elements, dwz_list,
                         dwz_list_elements);

+  per_bfd->last_debug_info_unit_idx = per_bfd->all_comp_units.size() - 1;
+
   if (types_list_elements)
     {
       /* We can only handle a single .debug_types when we have an
@@ -7263,6 +7265,9 @@ create_all_comp_units (dwarf2_per_objfile *per_objfile)
   read_comp_units_from_section (per_objfile, &per_objfile->per_bfd->info,
                                &per_objfile->per_bfd->abbrev, 0,
                                types_htab, rcuh_kind::COMPILE);
+
+  per_objfile->per_bfd->last_debug_info_unit_idx =
per_objfile->per_bfd->all_comp_units.size() - 1;
+
   for (dwarf2_section_info &section : per_objfile->per_bfd->types)
     read_comp_units_from_section (per_objfile, &section,
                                  &per_objfile->per_bfd->abbrev, 0,
@@ -7350,7 +7355,7 @@ skip_children (const struct die_reader_specs *reader,
const gdb_byte *info_ptr)
    INFO_PTR should point just after the initial uleb128 of a DIE, and the
    abbrev corresponding to that skipped uleb128 should be passed in
    ABBREV.
-   
+
    If DO_SKIP_CHILDREN is true, or if the DIE has no children, this
    returns a pointer to this DIE's sibling, skipping any children.
    Otherwise, returns a pointer to the DIE's first child.  */
@@ -23490,12 +23495,14 @@ static int
 dwarf2_find_containing_comp_unit
   (sect_offset sect_off,
    unsigned int offset_in_dwz,
-   const std::vector<dwarf2_per_cu_data_up> &all_comp_units)
+   const std::vector<dwarf2_per_cu_data_up> &all_comp_units, int high = -1)
 {
-  int low, high;
+  int low;

   low = 0;
-  high = all_comp_units.size () - 1;
+  if (high == -1)
+    high = all_comp_units.size() - 1;
+
   while (high > low)
     {
       struct dwarf2_per_cu_data *mid_cu;
@@ -23522,7 +23529,8 @@ dwarf2_find_containing_comp_unit (sect_offset sect_off,
                                  dwarf2_per_bfd *per_bfd)
 {
   int low = dwarf2_find_containing_comp_unit
-    (sect_off, offset_in_dwz, per_bfd->all_comp_units);
+    (sect_off, offset_in_dwz, per_bfd->all_comp_units,
+     per_bfd->last_debug_info_unit_idx);
   dwarf2_per_cu_data *this_cu = per_bfd->all_comp_units[low].get ();

   if (this_cu->is_dwz != offset_in_dwz || this_cu->sect_off > sect_off)
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index c2f86a9d367..c6e0376a48c 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -491,6 +491,7 @@ struct dwarf2_per_bfd
      the target compilation unit of a particular reference.  */
   std::vector<dwarf2_per_cu_data_up> all_comp_units;

+  size_t last_debug_info_unit_idx = 0;
   /* Table of struct type_unit_group objects.
      The hash key is the DW_AT_stmt_list value.  */
   htab_up type_unit_groups;

-- 
You are receiving this mail because:
You are on the CC list for the bug.

             reply	other threads:[~2022-08-24  7:12 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-24  7:12 clayborg at gmail dot com [this message]
2022-08-24  7:13 ` [Bug gdb/29518] " clayborg at gmail dot com
2022-08-24  7:29 ` clayborg at gmail dot com

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bug-29518-4717@http.sourceware.org/bugzilla/ \
    --to=sourceware-bugzilla@sourceware.org \
    --cc=gdb-prs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).