]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Handle DWARF 5 separate debug sections
authorTom Tromey <tom@tromey.com>
Wed, 19 Mar 2025 20:47:29 +0000 (14:47 -0600)
committerTom Tromey <tom@tromey.com>
Tue, 22 Apr 2025 22:06:51 +0000 (16:06 -0600)
DWARF 5 standardized the .gnu_debugaltlink section that dwz emits in
multi-file mode.  This is handled via some new forms, and a new
.debug_sup section.

This patch adds support for this to gdb.  It is largely
straightforward, I think, though one oddity is that I chose not to
have this code search the system build-id directories for the
supplementary file.  My feeling was that, while it makes sense for a
distro to unify the build-id concept with the hash stored in the
.debug_sup section, there's no intrinsic need to do so.

This in turn means that a few tests -- for example those that test the
index cache -- will not work in this mode.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32808
Acked-By: Simon Marchi <simon.marchi@efficios.com>
15 files changed:
gdb/dwarf2/attribute.c
gdb/dwarf2/attribute.h
gdb/dwarf2/cooked-indexer.c
gdb/dwarf2/die.c
gdb/dwarf2/dwz.c
gdb/dwarf2/dwz.h
gdb/dwarf2/macro.c
gdb/dwarf2/read.c
gdb/dwarf2/read.h
gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dwznolink.exp
gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
gdb/testsuite/lib/dwarf.exp

index 1278f970a7d276eec718c42e45f96b9aba9a8c0b..2d14ebdc6c0d466a178b2a08c7d4e1ae4aa18f99 100644 (file)
@@ -73,7 +73,8 @@ attribute::form_is_string () const
          || form == DW_FORM_strx3
          || form == DW_FORM_strx4
          || form == DW_FORM_GNU_str_index
-         || form == DW_FORM_GNU_strp_alt);
+         || form == DW_FORM_GNU_strp_alt
+         || form == DW_FORM_strp_sup);
 }
 
 /* See attribute.h.  */
@@ -190,6 +191,8 @@ attribute::form_is_unsigned () const
 {
   return (form == DW_FORM_ref_addr
          || form == DW_FORM_GNU_ref_alt
+         || form == DW_FORM_ref_sup4
+         || form == DW_FORM_ref_sup8
          || form == DW_FORM_data2
          || form == DW_FORM_data4
          || form == DW_FORM_data8
index 70edff31b36738d29e48603b4c425856eb75df52..ec4f3d81f769cf982d1652047403ce3089a7b321 100644 (file)
@@ -144,7 +144,9 @@ struct attribute
            || form == DW_FORM_ref4
            || form == DW_FORM_ref8
            || form == DW_FORM_ref_udata
-           || form == DW_FORM_GNU_ref_alt);
+           || form == DW_FORM_GNU_ref_alt
+           || form == DW_FORM_ref_sup4
+           || form == DW_FORM_ref_sup8);
   }
 
   /* Check if the attribute's form is a DW_FORM_block*
@@ -168,6 +170,16 @@ struct attribute
      "reprocessing".  */
   bool form_requires_reprocessing () const;
 
+  /* Check if attribute's form refers to the separate "dwz" file.
+     This is only useful for references to the .debug_info section,
+     not to the supplementary .debug_str section.  */
+  bool form_is_alt () const
+  {
+    return (form == DW_FORM_GNU_ref_alt
+           || form == DW_FORM_ref_sup4
+           || form == DW_FORM_ref_sup8);
+  }
+
   /* Return DIE offset of this attribute.  Return 0 with complaint if
      the attribute is not of the required kind.  */
 
index 1f3a2357958f04e75ba4674c17464ddc14b64dab..b8b66cf0856457c9a78483b4a8ffd1bcd5f23d09 100644 (file)
@@ -222,7 +222,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
        case DW_AT_abstract_origin:
        case DW_AT_extension:
          origin_offset = attr.get_ref_die_offset ();
-         origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
+         origin_is_dwz = attr.form_is_alt ();
          break;
 
        case DW_AT_external:
@@ -423,7 +423,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader,
       if (attr.name == DW_AT_import)
        {
          sect_off = attr.get_ref_die_offset ();
-         is_dwz = (attr.form == DW_FORM_GNU_ref_alt
+         is_dwz = (attr.form_is_alt ()
                    || reader->cu ()->per_cu->is_dwz);
        }
     }
index 8c3d2caa5d2fa00afd02f04b58efb49b41e33998..7ed18bf5c68940b451306221f2fb3d781b3affbb 100644 (file)
@@ -90,6 +90,8 @@ dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
          gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
          break;
        case DW_FORM_GNU_ref_alt:
+       case DW_FORM_ref_sup4:
+       case DW_FORM_ref_sup8:
          gdb_printf (f, "alt ref address: ");
          gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
          break;
@@ -123,6 +125,7 @@ dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
        case DW_FORM_strx:
        case DW_FORM_GNU_str_index:
        case DW_FORM_GNU_strp_alt:
+       case DW_FORM_strp_sup:
          gdb_printf (f, "string: \"%s\" (%s canonicalized)",
                      die->attrs[i].as_string ()
                      ? die->attrs[i].as_string () : "",
index d5d5112ad4cee8c20d031bf5eeb136c97fbff837..583103b4bae4e121bf4dd5cbe620565a782b5788 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "build-id.h"
 #include "debuginfod-support.h"
+#include "dwarf2/leb.h"
 #include "dwarf2/read.h"
 #include "dwarf2/sect-names.h"
 #include "filenames.h"
@@ -39,12 +40,12 @@ dwz_file::read_string (struct objfile *objfile, LONGEST str_offset)
   gdb_assert (str.readin);
 
   if (str.buffer == NULL)
-    error (_("DW_FORM_GNU_strp_alt used without .debug_str "
+    error (_("supplementary DWARF file missing .debug_str "
             "section [in module %s]"),
           this->filename ());
   if (str_offset >= str.size)
-    error (_("DW_FORM_GNU_strp_alt pointing outside of "
-            ".debug_str section [in module %s]"),
+    error (_("invalid string reference to supplementary DWARF file "
+            "[in module %s]"),
           this->filename ());
   gdb_assert (HOST_CHAR_BIT == 8);
   if (str.buffer[str_offset] == '\0')
@@ -87,6 +88,139 @@ locate_dwz_sections (struct objfile *objfile, bfd *abfd, asection *sectp,
     }
 }
 
+/* Helper that throws an exception when reading the .debug_sup
+   section.  */
+
+static void
+debug_sup_failure (const char *text, bfd *abfd)
+{
+  error (_("%s [in modules %s]"), text, bfd_get_filename (abfd));
+}
+
+/* Look for the .debug_sup section and read it.  If the section does
+   not exist, this returns false.  If the section does exist but fails
+   to parse for some reason, an exception is thrown.  Otherwise, if
+   everything goes well, this returns true and fills in the out
+   parameters.  */
+
+static bool
+get_debug_sup_info (bfd *abfd,
+                   std::string *filename,
+                   size_t *buildid_len,
+                   gdb::unique_xmalloc_ptr<bfd_byte> *buildid)
+{
+  asection *sect = bfd_get_section_by_name (abfd, ".debug_sup");
+  if (sect == nullptr)
+    return false;
+
+  bfd_byte *contents;
+  if (!bfd_malloc_and_get_section (abfd, sect, &contents))
+    debug_sup_failure (_("could not read .debug_sup section"), abfd);
+
+  gdb::unique_xmalloc_ptr<bfd_byte> content_holder (contents);
+  bfd_size_type size = bfd_section_size (sect);
+
+  /* Version of this section.  */
+  if (size < 4)
+    debug_sup_failure (_(".debug_sup section too short"), abfd);
+  unsigned int version = read_2_bytes (abfd, contents);
+  contents += 2;
+  size -= 2;
+  if (version != 5)
+    debug_sup_failure (_(".debug_sup has wrong version number"), abfd);
+
+  /* Skip the is_supplementary value.  We already ensured there were
+     enough bytes, above.  */
+  ++contents;
+  --size;
+
+  /* The spec says that in the supplementary file, this must be \0,
+     but it doesn't seem very important.  */
+  const char *fname = (const char *) contents;
+  size_t len = strlen (fname) + 1;
+  if (filename != nullptr)
+    *filename = fname;
+  contents += len;
+  size -= len;
+
+  if (size == 0)
+    debug_sup_failure (_(".debug_sup section missing ID"), abfd);
+
+  unsigned int bytes_read;
+  *buildid_len = read_unsigned_leb128 (abfd, contents, &bytes_read);
+  contents += bytes_read;
+  size -= bytes_read;
+
+  if (size < *buildid_len)
+    debug_sup_failure (_("extra data after .debug_sup section ID"), abfd);
+
+  if (*buildid_len != 0)
+    buildid->reset ((bfd_byte *) xmemdup (contents, *buildid_len,
+                                         *buildid_len));
+
+  return true;
+}
+
+/* Validate that ABFD matches the given BUILDID.  If DWARF5 is true,
+   then this is done by examining the .debug_sup data.  */
+
+static bool
+verify_id (bfd *abfd, size_t len, const bfd_byte *buildid, bool dwarf5)
+{
+  if (!bfd_check_format (abfd, bfd_object))
+    return false;
+
+  if (dwarf5)
+    {
+      size_t new_len;
+      gdb::unique_xmalloc_ptr<bfd_byte> new_id;
+
+      if (!get_debug_sup_info (abfd, nullptr, &new_len, &new_id))
+       return false;
+      return (len == new_len
+             && memcmp (buildid, new_id.get (), len) == 0);
+    }
+  else
+    return build_id_verify (abfd, len, buildid);
+}
+
+/* Find either the .debug_sup or .gnu_debugaltlink section and return
+   its contents.  Returns true on success and sets out parameters, or
+   false if nothing is found.  */
+
+static bool
+read_alt_info (bfd *abfd, std::string *filename,
+              size_t *buildid_len,
+              gdb::unique_xmalloc_ptr<bfd_byte> *buildid,
+              bool *dwarf5)
+{
+  if (get_debug_sup_info (abfd, filename, buildid_len, buildid))
+    {
+      *dwarf5 = true;
+      return true;
+    }
+
+  bfd_size_type buildid_len_arg;
+  bfd_set_error (bfd_error_no_error);
+  bfd_byte *buildid_out;
+  gdb::unique_xmalloc_ptr<char> new_filename
+    (bfd_get_alt_debug_link_info (abfd, &buildid_len_arg,
+                                 &buildid_out));
+  if (new_filename == nullptr)
+    {
+      if (bfd_get_error () == bfd_error_no_error)
+       return false;
+      error (_("could not read '.gnu_debugaltlink' section: %s"),
+            bfd_errmsg (bfd_get_error ()));
+    }
+  *filename = new_filename.get ();
+
+  *buildid_len = buildid_len_arg;
+  buildid->reset (buildid_out);
+  *dwarf5 = false;
+  return true;
+}
+
 /* Attempt to find a .dwz file (whose full path is represented by
    FILENAME) in all of the specified debug file directories provided.
 
@@ -95,7 +229,7 @@ locate_dwz_sections (struct objfile *objfile, bfd *abfd, asection *sectp,
 
 static gdb_bfd_ref_ptr
 dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
-                           size_t buildid_len)
+                           size_t buildid_len, bool dwarf5)
 {
   /* Let's assume that the path represented by FILENAME has the
      "/.dwz/" subpath in it.  This is what (most) GNU/Linux
@@ -163,7 +297,7 @@ dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
       if (dwz_bfd == nullptr)
        continue;
 
-      if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+      if (!verify_id (dwz_bfd.get (), buildid_len, buildid, dwarf5))
        {
          dwz_bfd.reset (nullptr);
          continue;
@@ -181,9 +315,6 @@ dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
 void
 dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile)
 {
-  bfd_size_type buildid_len_arg;
-  size_t buildid_len;
-  bfd_byte *buildid;
   dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
 
   /* This may query the user via the debuginfod support, so it may
@@ -195,24 +326,17 @@ dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile)
   /* Set this early, so that on error it remains NULL.  */
   per_bfd->dwz_file.emplace (nullptr);
 
-  bfd_set_error (bfd_error_no_error);
-  gdb::unique_xmalloc_ptr<char> data
-    (bfd_get_alt_debug_link_info (per_bfd->obfd,
-                                 &buildid_len_arg, &buildid));
-  if (data == NULL)
+  size_t buildid_len;
+  gdb::unique_xmalloc_ptr<bfd_byte> buildid;
+  std::string filename;
+  bool dwarf5;
+  if (!read_alt_info (per_bfd->obfd, &filename, &buildid_len, &buildid,
+                     &dwarf5))
     {
-      if (bfd_get_error () == bfd_error_no_error)
-       return;
-      error (_("could not read '.gnu_debugaltlink' section: %s"),
-            bfd_errmsg (bfd_get_error ()));
+      /* Nothing found, nothing to do.  */
+      return;
     }
 
-  gdb::unique_xmalloc_ptr<bfd_byte> buildid_holder (buildid);
-
-  buildid_len = (size_t) buildid_len_arg;
-
-  std::string filename = data.get ();
-
   if (!IS_ABSOLUTE_PATH (filename.c_str ()))
     {
       gdb::unique_xmalloc_ptr<char> abs = gdb_realpath (per_bfd->filename ());
@@ -225,25 +349,26 @@ dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile)
   gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename.c_str (), gnutarget));
   if (dwz_bfd != NULL)
     {
-      if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+      if (!verify_id (dwz_bfd.get (), buildid_len, buildid.get (), dwarf5))
        dwz_bfd.reset (nullptr);
     }
 
   if (dwz_bfd == NULL)
-    dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid);
+    dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid.get ());
 
   if (dwz_bfd == nullptr)
     {
       /* If the user has provided us with different
         debug file directories, we can try them in order.  */
-      dwz_bfd = dwz_search_other_debugdirs (filename, buildid, buildid_len);
+      dwz_bfd = dwz_search_other_debugdirs (filename, buildid.get (),
+                                           buildid_len, dwarf5);
     }
 
   if (dwz_bfd == nullptr)
     {
       gdb::unique_xmalloc_ptr<char> alt_filename;
       scoped_fd fd
-       = debuginfod_debuginfo_query (buildid, buildid_len,
+       = debuginfod_debuginfo_query (buildid.get (), buildid_len,
                                      per_bfd->filename (), &alt_filename);
 
       if (fd.get () >= 0)
@@ -254,13 +379,15 @@ dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile)
          if (dwz_bfd == nullptr)
            warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
                     alt_filename.get ());
-         else if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+         else if (!verify_id (dwz_bfd.get (), buildid_len, buildid.get (),
+                              dwarf5))
            dwz_bfd.reset (nullptr);
        }
     }
 
   if (dwz_bfd == NULL)
-    error (_("could not find '.gnu_debugaltlink' file for %s"),
+    error (_("could not find supplementary DWARF file (%s) for %s"),
+          filename.c_str (),
           per_bfd->filename ());
 
   dwz_file_up result (new dwz_file (std::move (dwz_bfd)));
index 06ce45045de42cf7aef39991a3d9b013dd8551ea..372f7e60979da8d877f5277ecb267266aaf248cd 100644 (file)
@@ -34,8 +34,8 @@ struct dwz_file
   /* Open the separate '.dwz' debug file, if needed.  This will set
      the appropriate field in the per-BFD structure.  If the DWZ file
      exists, the relevant sections are read in as well.  Throws an
-     error if the .gnu_debugaltlink section exists but the file cannot
-     be found.  */
+     exception if the .gnu_debugaltlink or .debug_sup section exists
+     but is invalid or if the file cannot be found.  */
   static void read_dwz_file (dwarf2_per_objfile *per_objfile);
 
   const char *filename () const
index 9b8f093b7352ccdc53e35e03ef1b8b381dea5450..1dc3a9ec9b3e544a4aabb640964bbd0e88e870cd 100644 (file)
@@ -259,6 +259,7 @@ skip_form_bytes (bfd *abfd, const gdb_byte *bytes, const gdb_byte *buffer_end,
     case DW_FORM_sec_offset:
     case DW_FORM_strp:
     case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup:
       bytes += offset_size;
       break;
 
index 01b3a9024d8344ed16f8b58224a5e98967af6a9e..7b019f930affba6164ddcb5b6418d7c25e4d7a90 100644 (file)
@@ -3896,11 +3896,13 @@ cutu_reader::skip_one_attribute (dwarf_form form, const gdb_byte *info_ptr)
     case DW_FORM_data4:
     case DW_FORM_ref4:
     case DW_FORM_strx4:
+    case DW_FORM_ref_sup4:
       return info_ptr + 4;
 
     case DW_FORM_data8:
     case DW_FORM_ref8:
     case DW_FORM_ref_sig8:
+    case DW_FORM_ref_sup8:
       return info_ptr + 8;
 
     case DW_FORM_data16:
@@ -3913,6 +3915,7 @@ cutu_reader::skip_one_attribute (dwarf_form form, const gdb_byte *info_ptr)
     case DW_FORM_sec_offset:
     case DW_FORM_strp:
     case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup:
       return info_ptr + m_cu->header.offset_size;
 
     case DW_FORM_exprloc:
@@ -5023,7 +5026,7 @@ process_imported_unit_die (struct die_info *die, struct dwarf2_cu *cu)
   if (attr != NULL)
     {
       sect_offset sect_off = attr->get_ref_die_offset ();
-      bool is_dwz = (attr->form == DW_FORM_GNU_ref_alt || cu->per_cu->is_dwz);
+      bool is_dwz = attr->form_is_alt () || cu->per_cu->is_dwz;
       dwarf2_per_objfile *per_objfile = cu->per_objfile;
       dwarf2_per_cu *per_cu
        = dwarf2_find_containing_comp_unit (sect_off, is_dwz,
@@ -14914,10 +14917,12 @@ cutu_reader::read_attribute_value (attribute *attr, unsigned form,
       info_ptr += 2;
       break;
     case DW_FORM_data4:
+    case DW_FORM_ref_sup4:
       attr->set_unsigned (read_4_bytes (m_abfd, info_ptr));
       info_ptr += 4;
       break;
     case DW_FORM_data8:
+    case DW_FORM_ref_sup8:
       attr->set_unsigned (read_8_bytes (m_abfd, info_ptr));
       info_ptr += 8;
       break;
@@ -14969,6 +14974,7 @@ cutu_reader::read_attribute_value (attribute *attr, unsigned form,
        }
       [[fallthrough]];
     case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup:
       {
        dwz_file *dwz = per_objfile->per_bfd->get_dwz_file (true);
        LONGEST str_offset
@@ -17251,6 +17257,7 @@ dwarf2_const_value_attr (const struct attribute *attr, struct type *type,
     case DW_FORM_strx:
     case DW_FORM_GNU_str_index:
     case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup:
       /* The string is already allocated on the objfile obstack, point
         directly to it.  */
       *bytes = (const gdb_byte *) attr->as_string ();
@@ -17457,7 +17464,7 @@ lookup_die_type (struct die_info *die, const struct attribute *attr,
 
   /* First see if we have it cached.  */
 
-  if (attr->form == DW_FORM_GNU_ref_alt)
+  if (attr->form_is_alt ())
     {
       sect_offset sect_off = attr->get_ref_die_offset ();
       dwarf2_per_cu *per_cu
@@ -18241,15 +18248,14 @@ follow_die_ref (struct die_info *src_die, const struct attribute *attr,
   struct dwarf2_cu *cu = *ref_cu;
   struct die_info *die;
 
-  if (attr->form != DW_FORM_GNU_ref_alt && src_die->sect_off == sect_off)
+  if (!attr->form_is_alt () && src_die->sect_off == sect_off)
     {
       /* Self-reference, we're done.  */
       return src_die;
     }
 
   die = follow_die_offset (sect_off,
-                          (attr->form == DW_FORM_GNU_ref_alt
-                           || cu->per_cu->is_dwz),
+                          attr->form_is_alt () || cu->per_cu->is_dwz,
                           ref_cu);
   if (!die)
     error (_(DWARF_ERROR_PREFIX
@@ -18460,6 +18466,7 @@ dwarf2_fetch_constant_bytes (sect_offset sect_off,
     case DW_FORM_strx:
     case DW_FORM_GNU_str_index:
     case DW_FORM_GNU_strp_alt:
+    case DW_FORM_strp_sup:
       /* The string is already allocated on the objfile obstack, point
         directly to it.  */
       {
index a9a2aa42aa1dbf53b5fbb42599fb854f91f9c882..f3e043c0ffa6a64a78f8045a2f5733a11466b0f5 100644 (file)
@@ -533,9 +533,9 @@ struct dwarf2_per_bfd
   }
 
   /* Return the separate '.dwz' debug file.  If there is no
-     .gnu_debugaltlink section in the file, then the result depends on
-     REQUIRE: if REQUIRE is true, error out; if REQUIRE is false,
-     return nullptr.  */
+     .gnu_debugaltlink or .debug_sup section in the file, then the
+     result depends on REQUIRE: if REQUIRE is true, error out; if
+     REQUIRE is false, return nullptr.  */
   struct dwz_file *get_dwz_file (bool require = false)
   {
     gdb_assert (!require || this->dwz_file.has_value ());
@@ -546,7 +546,7 @@ struct dwarf2_per_bfd
       {
        result = this->dwz_file->get ();
        if (require && result == nullptr)
-         error (_("could not read '.gnu_debugaltlink' section"));
+         error (_("could not find supplementary DWARF file"));
       }
 
     return result;
index 055e69c0d02452c2be838f1c55e48262ce474291..080e99969f7cd84a0011b4d89c73765f437fb5b6 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-load_lib dwarf.exp
-
-# This test can only be run on targets which support DWARF-2 and use gas.
-require dwarf2_support
-
-# No remote host testing either.
-require {!is_remote host}
-
-
-# Lots of source files since we test a few cases and make new files
-# for each.
-# The tests are:
-#     ok - the main file refers to a dwz and the buildids match
-#     mismatch - the buildids do not match
-#     fallback - the buildids do not match but a match is found via buildid
-standard_testfile main.c \
-    dwzbuildid-ok-base.S dwzbuildid-ok-sep.S \
-    dwzbuildid-mismatch-base.S dwzbuildid-mismatch-sep.S \
-    dwzbuildid-fallback-base.S dwzbuildid-fallback-sep.S \
-    dwzbuildid-fallback-ok.S
-    
-# Write some assembly that just has a .gnu_debugaltlink section.
-proc write_just_debugaltlink {filename dwzname buildid} {
-    set asm_file [standard_output_file $filename]
-
-    Dwarf::assemble $asm_file {
-       upvar dwzname dwzname
-       upvar buildid buildid
-
-       gnu_debugaltlink $dwzname $buildid
-
-       # Only the DWARF reader checks .gnu_debugaltlink, so make sure
-       # there is a bit of DWARF in here.
-       cu { label cu_start } {
-           compile_unit {{language @DW_LANG_C}} {
-           }
-       }
-       aranges {} cu_start {
-           arange {} 0 0
-       }
-    }
-}
-
-# Write some DWARF that also sets the buildid.
-proc write_dwarf_file {filename buildid {value 99}} {
-    set asm_file [standard_output_file $filename]
-
-    Dwarf::assemble $asm_file {
-       declare_labels int_label int_label2
-
-       upvar buildid buildid
-       upvar value value
-
-       build_id $buildid
-
-       cu { label cu_start } {
-           compile_unit {{language @DW_LANG_C}} {
-               int_label2: base_type {
-                   {name int}
-                   {byte_size 4 sdata}
-                   {encoding @DW_ATE_signed}
-               }
-
-               constant {
-                   {name the_int}
-                   {type :$int_label2}
-                   {const_value $value data1}
-               }
-           }
-       }
-
-       aranges {} cu_start {
-           arange {} 0 0
-       }
-    }
-}
-
-if  { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
-          object {nodebug}] != "" } {
-    return -1
-}
-
-# The values don't really matter, just whether they are equal.
-set ok_prefix 01
-set ok_suffix 02030405060708091011121314151617181920
-set ok_suffix2 020304050607080910111213141516171819ff
-set ok_buildid ${ok_prefix}${ok_suffix}
-set ok_buildid2 ${ok_prefix}${ok_suffix2}
-set bad_buildid [string repeat ff 20]
-
-set debugdir [standard_output_file {}]
-set basedir $debugdir/.build-id
-file mkdir $basedir $basedir/$ok_prefix
-
-# Test where the separate debuginfo's buildid matches.
-write_just_debugaltlink $srcfile2 ${binfile}3.o $ok_buildid
-write_dwarf_file $srcfile3 $ok_buildid
-
-# Test where the separate debuginfo's buildid does not match.
-write_just_debugaltlink $srcfile4 ${binfile}5.o $ok_buildid
-write_dwarf_file $srcfile5 $bad_buildid
-
-# Test where the separate debuginfo's buildid does not match, but then
-# we find a match in the .build-id directory.
-write_just_debugaltlink $srcfile6 ${binfile}7.o $ok_buildid2
-# Use 77 as the value so that if we load the bad debuginfo, we will
-# see the wrong result.
-write_dwarf_file $srcfile7 $bad_buildid 77
-write_dwarf_file $srcfile8 $ok_buildid2
-
-# Compile everything.
-for {set i 2} {$i <= 8} {incr i} {
-    if {[gdb_compile [standard_output_file [set srcfile$i]] \
-            ${binfile}$i.o object nodebug] != ""} {
-       return -1
-    }
-}
-
-# Copy a file into the .build-id place for the "fallback" test.
-file copy -force -- ${binfile}8.o $basedir/$ok_prefix/$ok_suffix2.debug
-
-proc do_test {} {
-    clean_restart
-
-    gdb_test_no_output "set debug-file-directory $::debugdir" \
-       "set debug-file-directory"
-
-    gdb_load ${::binfile}-${::testname}
-
-    if {![runto_main]} {
-       return
-    }
-
-    if {$::testname == "mismatch"} {
-       gdb_test "print the_int" \
-           "(No symbol table is loaded|No symbol \"the_int\" in current context).*"
-    } else {
-       gdb_test "print the_int" " = 99"
-    }
-}
-
-foreach_with_prefix testname { ok mismatch fallback } {
-    if { $testname == "ok" } {
-       set objs [list ${binfile}1.o ${binfile}2.o]
-    } elseif { $testname == "mismatch" } {
-       set objs [list ${binfile}1.o ${binfile}4.o]
-    } elseif { $testname == "fallback" } {
-       set objs [list ${binfile}1.o ${binfile}6.o]
-    }
-
-    if {[gdb_compile $objs ${binfile}-$testname executable {quiet}] != ""} {
-       unsupported "compilation failed"
-       continue
-    }
-
-    do_test
-}
+set scenario gnu
+source $srcdir/$subdir/dwzbuildid.tcl
diff --git a/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl b/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl
new file mode 100644 (file)
index 0000000..a9077eb
--- /dev/null
@@ -0,0 +1,184 @@
+# Copyright 2013-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+# No remote host testing either.
+require {!is_remote host}
+
+
+# Lots of source files since we test a few cases and make new files
+# for each.
+# The tests are:
+#     ok - the main file refers to a dwz and the buildids match
+#     mismatch - the buildids do not match
+#     fallback - the buildids do not match but a match is found via buildid
+standard_testfile main.c \
+    dwzbuildid-ok-base.S dwzbuildid-ok-sep.S \
+    dwzbuildid-mismatch-base.S dwzbuildid-mismatch-sep.S \
+    dwzbuildid-fallback-base.S dwzbuildid-fallback-sep.S \
+    dwzbuildid-fallback-ok.S
+
+# Write some assembly that just has a .gnu_debugaltlink section.
+proc write_just_debugaltlink {filename dwzname buildid} {
+    set asm_file [standard_output_file $filename]
+
+    Dwarf::assemble $asm_file {
+       upvar dwzname dwzname
+       upvar buildid buildid
+
+       if {$::scenario == "gnu"} {
+           gnu_debugaltlink $dwzname $buildid
+       } else {
+           debug_sup 0 $dwzname $buildid
+       }
+
+       # Only the DWARF reader checks .gnu_debugaltlink, so make sure
+       # there is a bit of DWARF in here.
+       cu { label cu_start } {
+           compile_unit {{language @DW_LANG_C}} {
+           }
+       }
+       aranges {} cu_start {
+           arange {} 0 0
+       }
+    }
+}
+
+# Write some DWARF that also sets the buildid.
+proc write_dwarf_file {filename buildid {value 99}} {
+    set asm_file [standard_output_file $filename]
+
+    Dwarf::assemble $asm_file {
+       declare_labels int_label int_label2
+
+       upvar buildid buildid
+       upvar value value
+
+       if {$::scenario == "gnu"} {
+           build_id $buildid
+       } else {
+           debug_sup 1 "" $buildid
+       }
+
+       cu { label cu_start } {
+           compile_unit {{language @DW_LANG_C}} {
+               int_label2: base_type {
+                   {name int}
+                   {byte_size 4 sdata}
+                   {encoding @DW_ATE_signed}
+               }
+
+               constant {
+                   {name the_int}
+                   {type :$int_label2}
+                   {const_value $value data1}
+               }
+           }
+       }
+
+       aranges {} cu_start {
+           arange {} 0 0
+       }
+    }
+}
+
+if  { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
+          object {nodebug}] != "" } {
+    return -1
+}
+
+# The values don't really matter, just whether they are equal.
+set ok_prefix 01
+set ok_suffix 02030405060708091011121314151617181920
+set ok_suffix2 020304050607080910111213141516171819ff
+set ok_buildid ${ok_prefix}${ok_suffix}
+set ok_buildid2 ${ok_prefix}${ok_suffix2}
+set bad_buildid [string repeat ff 20]
+
+set debugdir [standard_output_file {}]
+set basedir $debugdir/.build-id
+file mkdir $basedir $basedir/$ok_prefix
+
+# Test where the separate debuginfo's buildid matches.
+write_just_debugaltlink $srcfile2 ${binfile}3.o $ok_buildid
+write_dwarf_file $srcfile3 $ok_buildid
+
+# Test where the separate debuginfo's buildid does not match.
+write_just_debugaltlink $srcfile4 ${binfile}5.o $ok_buildid
+write_dwarf_file $srcfile5 $bad_buildid
+
+# Test where the separate debuginfo's buildid does not match, but then
+# we find a match in the .build-id directory.
+write_just_debugaltlink $srcfile6 ${binfile}7.o $ok_buildid2
+# Use 77 as the value so that if we load the bad debuginfo, we will
+# see the wrong result.
+write_dwarf_file $srcfile7 $bad_buildid 77
+write_dwarf_file $srcfile8 $ok_buildid2
+
+# Compile everything.
+for {set i 2} {$i <= 8} {incr i} {
+    if {[gdb_compile [standard_output_file [set srcfile$i]] \
+            ${binfile}$i.o object nodebug] != ""} {
+       return -1
+    }
+}
+
+# Copy a file into the .build-id place for the "fallback" test.
+file copy -force -- ${binfile}8.o $basedir/$ok_prefix/$ok_suffix2.debug
+
+proc do_test {} {
+    clean_restart
+
+    gdb_test_no_output "set debug-file-directory $::debugdir" \
+       "set debug-file-directory"
+
+    gdb_load ${::binfile}-${::testname}
+
+    if {![runto_main]} {
+       return
+    }
+
+    if {$::testname == "mismatch"} {
+       gdb_test "print the_int" \
+           "(No symbol table is loaded|No symbol \"the_int\" in current context).*"
+    } else {
+       gdb_test "print the_int" " = 99"
+    }
+}
+
+set tests {ok mismatch}
+if {$scenario == "gnu"} {
+    lappend tests fallback
+}
+foreach_with_prefix testname $tests {
+    if { $testname == "ok" } {
+       set objs [list ${binfile}1.o ${binfile}2.o]
+    } elseif { $testname == "mismatch" } {
+       set objs [list ${binfile}1.o ${binfile}4.o]
+    } elseif { $testname == "fallback" } {
+       set objs [list ${binfile}1.o ${binfile}6.o]
+    }
+
+    if {[gdb_compile $objs ${binfile}-$testname executable {quiet}] != ""} {
+       unsupported "compilation failed"
+       continue
+    }
+
+    do_test
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp b/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp
new file mode 100644 (file)
index 0000000..047626c
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set scenario dwarf5
+source $srcdir/$subdir/dwzbuildid.tcl
index 91fe369c2b6272e51585cf5e21ecb89f03322ebe..0c486eadd4956ce2b59fb375e54a1c1564a5e1a2 100644 (file)
@@ -49,5 +49,5 @@ if {[build_executable $testfile.exp $testfile \
 
 clean_restart
 gdb_test "file -readnow $binfile" \
-    "could not read '.gnu_debugaltlink' section" \
+    "could not find supplementary DWARF file" \
     "file $testfile"
index 7475d7ad95293bea54a1e3df3b885d8b61599552..05e625f53d4aed061f9264042bc31d874ed7085d 100644 (file)
@@ -38,7 +38,7 @@ if { [build_executable $testfile.exp $testfile [list $srcfile $asm_file]] } {
 clean_restart
 gdb_test_no_output "maint set dwarf synchronous on"
 
-set msg "\r\nwarning: could not find '\.gnu_debugaltlink' file for \[^\r\n\]*"
+set msg "\r\nwarning: could not find supplementary DWARF file \[^\r\n\]*"
 gdb_test "file $binfile" "$msg" "file command"
 
 set question "Load new symbol table from .*\? .y or n. "
index 46b39a13313213cc22f75620e52749ffc85c566d..7e8778aa1ad7a4389a81d8ecf9c592ec0e2991f2 100644 (file)
@@ -3068,6 +3068,24 @@ namespace eval Dwarf {
        }
     }
 
+    # Emit a .debug_sup section with the given file name and build-id.
+    # The buildid should be represented as a hexadecimal string, like
+    # "ffeeddcc".
+    proc debug_sup {is_sup filename buildid} {
+       _defer_output .debug_sup {
+           # The version.
+           _op .2byte 0x5
+           # Supplementary marker.
+           _op .byte $is_sup
+           _op .ascii [_quote $filename]
+           set len [expr {[string length $buildid] / 2}]
+           _op .uleb128 $len
+           foreach {a b} [split $buildid {}] {
+               _op .byte 0x$a$b
+           }
+       }
+    }
+
     proc _note {type name hexdata} {
        set namelen [expr [string length $name] + 1]
        set datalen [expr [string length $hexdata] / 2]