[2/3] Handle DWARF 5 separate debug sections

Message ID 20250323-dwz-dwarf-5-v2-v1-2-3c0775ca5514@tromey.com
State New
Headers
Series Support DWARF 5 .debug_sup section |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Tom Tromey March 23, 2025, 7:20 p.m. UTC
  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
---
 gdb/dwarf2/attribute.c                        |   5 +-
 gdb/dwarf2/attribute.h                        |  14 +-
 gdb/dwarf2/cooked-indexer.c                   |   4 +-
 gdb/dwarf2/die.c                              |   3 +
 gdb/dwarf2/dwz.c                              | 185 ++++++++++++++++++++++----
 gdb/dwarf2/dwz.h                              |   4 +-
 gdb/dwarf2/macro.c                            |   1 +
 gdb/dwarf2/read.c                             |  17 ++-
 gdb/dwarf2/read.h                             |   8 +-
 gdb/testsuite/gdb.dwarf2/dwzbuildid.exp       | 159 +---------------------
 gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl       | 184 +++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp      |  17 +++
 gdb/testsuite/gdb.dwarf2/dwznolink.exp        |   2 +-
 gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp |   2 +-
 gdb/testsuite/lib/dwarf.exp                   |  18 +++
 15 files changed, 420 insertions(+), 203 deletions(-)
  

Comments

Alexandra Hájková March 24, 2025, 3:45 p.m. UTC | #1
On Sun, Mar 23, 2025 at 8:23 PM Tom Tromey <tom@tromey.com> wrote:

> 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
>
>
Hi Tom,

I tested this on aarch64, Fedora 40 and  this change causes
gdb.base/bp-cmds-continue-ctrl-c.exp
to fail. I can provide you with the logs if you are interested.
  
Tom Tromey March 24, 2025, 5:16 p.m. UTC | #2
>>>>> "Alexandra" == Alexandra Petlanova Hajkova <ahajkova@redhat.com> writes:

Alexandra> I tested this on aarch64, Fedora 40 and this change causes
Alexandra> gdb.base/bp-cmds-continue-ctrl-c.exp to fail. I can provide
Alexandra> you with the logs if you are interested.

Sure, that may be helpful.
I have no idea why this patch would affect that, unless maybe you were
testing the new mode?

Tom
  
Simon Marchi March 24, 2025, 8:52 p.m. UTC | #3
On 2025-03-23 15:20, Tom Tromey wrote:
> 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

I just had a few minutes to spare, so I just scanned the patch, I'm
happy with it.  More support for standard DWARF is nice.

Acked-By: Simon Marchi <simon.marchi@efficios.com>

Some random comments:

> @@ -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));

modules -> module?

> +  if (*buildid_len != 0)
> +    buildid->reset ((bfd_byte *) xmemdup (contents, *buildid_len,
> +					  *buildid_len));

Could we return a view inside the section, instead of copying the bytes?
It seems like we are just copying the bytes as is.

Simon
  
Tom Tromey March 25, 2025, 1:37 p.m. UTC | #4
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:

>> +  if (*buildid_len != 0)
>> +    buildid->reset ((bfd_byte *) xmemdup (contents, *buildid_len,
>> +					  *buildid_len));

Simon> Could we return a view inside the section, instead of copying the bytes?
Simon> It seems like we are just copying the bytes as is.

In this code, the section isn't mapped but instead just read in and then
discarded.  That's done because it's only needed for a moment while
reading -- once the file is found the .debug_sup contents aren't needed.

Tom
  
Tom Tromey April 3, 2025, 2:39 p.m. UTC | #5
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

>>>>> "Alexandra" == Alexandra Petlanova Hajkova <ahajkova@redhat.com> writes:
Alexandra> I tested this on aarch64, Fedora 40 and this change causes
Alexandra> gdb.base/bp-cmds-continue-ctrl-c.exp to fail. I can provide
Alexandra> you with the logs if you are interested.

Tom> Sure, that may be helpful.
Tom> I have no idea why this patch would affect that, unless maybe you were
Tom> testing the new mode?

Hi, I wanted to land this series, but I need to investigate this first.
Could you send me this log?

thanks,
Tom
  
Tom Tromey April 22, 2025, 2:55 p.m. UTC | #6
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
>>>>> "Alexandra" == Alexandra Petlanova Hajkova <ahajkova@redhat.com> writes:
Alexandra> I tested this on aarch64, Fedora 40 and this change causes
Alexandra> gdb.base/bp-cmds-continue-ctrl-c.exp to fail. I can provide
Alexandra> you with the logs if you are interested.

Tom> Sure, that may be helpful.
Tom> I have no idea why this patch would affect that, unless maybe you were
Tom> testing the new mode?

Tom> Hi, I wanted to land this series, but I need to investigate this first.
Tom> Could you send me this log?

Ping.

Tom
  
Simon Marchi April 22, 2025, 3:10 p.m. UTC | #7
On 4/22/25 10:55 AM, Tom Tromey wrote:
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
> 
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
>>>>>> "Alexandra" == Alexandra Petlanova Hajkova <ahajkova@redhat.com> writes:
> Alexandra> I tested this on aarch64, Fedora 40 and this change causes
> Alexandra> gdb.base/bp-cmds-continue-ctrl-c.exp to fail. I can provide
> Alexandra> you with the logs if you are interested.
> 
> Tom> Sure, that may be helpful.
> Tom> I have no idea why this patch would affect that, unless maybe you were
> Tom> testing the new mode?
> 
> Tom> Hi, I wanted to land this series, but I need to investigate this first.
> Tom> Could you send me this log?
> 
> Ping.
> 
> Tom

FWIW, I think it's pretty likely that it's a flaky test.  I think you
can go ahead and push your series.  If it turns out it's really a bug,
we can always fix it later.

Simon
  
Tom de Vries April 28, 2025, 11:35 a.m. UTC | #8
On 4/22/25 17:10, Simon Marchi wrote:
> On 4/22/25 10:55 AM, Tom Tromey wrote:
>>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
>>
>>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
>>>>>>> "Alexandra" == Alexandra Petlanova Hajkova <ahajkova@redhat.com> writes:
>> Alexandra> I tested this on aarch64, Fedora 40 and this change causes
>> Alexandra> gdb.base/bp-cmds-continue-ctrl-c.exp to fail. I can provide
>> Alexandra> you with the logs if you are interested.
>>
>> Tom> Sure, that may be helpful.
>> Tom> I have no idea why this patch would affect that, unless maybe you were
>> Tom> testing the new mode?
>>
>> Tom> Hi, I wanted to land this series, but I need to investigate this first.
>> Tom> Could you send me this log?
>>
>> Ping.
>>
>> Tom
> 
> FWIW, I think it's pretty likely that it's a flaky test.  I think you
> can go ahead and push your series.  If it turns out it's really a bug,
> we can always fix it later.

My guess is that this is PR32167 ( 
https://sourceware.org/bugzilla/show_bug.cgi?id=32167 ).

Thanks,
- Tom
  

Patch

diff --git a/gdb/dwarf2/attribute.c b/gdb/dwarf2/attribute.c
index 49c0bc07d75dd23f5b4f952f4ccb7c46cebc5265..3aa4790d4f7214233d1d9fe52db80f658ac6fcd8 100644
--- a/gdb/dwarf2/attribute.c
+++ b/gdb/dwarf2/attribute.c
@@ -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
diff --git a/gdb/dwarf2/attribute.h b/gdb/dwarf2/attribute.h
index ce6c5639c1ae25644844447399988648842d8f58..b89136678f5fe824c21082e39242fec6d945f169 100644
--- a/gdb/dwarf2/attribute.h
+++ b/gdb/dwarf2/attribute.h
@@ -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.  */
 
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 3b80cd6c50089c82963881845713b93655b25ec4..327db33593d6f3e284ac708f9c55a6aa11ed129d 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -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);
 	}
     }
diff --git a/gdb/dwarf2/die.c b/gdb/dwarf2/die.c
index 9437c2f985871769156c132d96826e9527e4c14b..4cdf535f8ac438a6df1aaa8d03c7010dde3b9a09 100644
--- a/gdb/dwarf2/die.c
+++ b/gdb/dwarf2/die.c
@@ -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 () : "",
diff --git a/gdb/dwarf2/dwz.c b/gdb/dwarf2/dwz.c
index 14cd8e882ba6bc16b57a82aa31296f4025261f15..76274b630e484624a6d1a97b7a5f32feb83019e1 100644
--- a/gdb/dwarf2/dwz.c
+++ b/gdb/dwarf2/dwz.c
@@ -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)));
diff --git a/gdb/dwarf2/dwz.h b/gdb/dwarf2/dwz.h
index 639eea3a41850249a44259e454daa770b246ef2b..381d8dad427aab4a04d2db6608b15862aad8130b 100644
--- a/gdb/dwarf2/dwz.h
+++ b/gdb/dwarf2/dwz.h
@@ -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
diff --git a/gdb/dwarf2/macro.c b/gdb/dwarf2/macro.c
index 2d9d4b7bbcf0e79da203fb23ecc21925f7c5ead3..7924fd2ed5160de72954360e86b560025ab12a86 100644
--- a/gdb/dwarf2/macro.c
+++ b/gdb/dwarf2/macro.c
@@ -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;
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index f4198d71ca28811bd02c5fb87b5206629f118874..e8a8c9d030282033bbcaf9d234188d06dfc32c2c 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -3920,11 +3920,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:
@@ -3937,6 +3939,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:
@@ -5047,7 +5050,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,
@@ -14951,10 +14954,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;
@@ -15006,6 +15011,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
@@ -17288,6 +17294,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 ();
@@ -17494,7 +17501,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
@@ -18278,15 +18285,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
@@ -18497,6 +18503,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.  */
       {
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index ba2dd07a2ee278228fc8ffadd56bbfe290844e74..895ebee633e8c06fce9180b81743ae249072024d 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -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;
diff --git a/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp b/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
index e54508a56e7e616a56d53c2ef17dcc3198fd1cba..6cb5a709855a41b418a0861b045bda23c36ec645 100644
--- a/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
@@ -13,160 +13,5 @@ 
 # 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
index 0000000000000000000000000000000000000000..b4c26a3d60022d21e806dc9400f507ffe9dbe386
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl
@@ -0,0 +1,184 @@ 
+# Copyright 2013-2024 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
index 0000000000000000000000000000000000000000..047626c003dc4824a8b3664b68a0aaff4b434da8
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp
@@ -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
diff --git a/gdb/testsuite/gdb.dwarf2/dwznolink.exp b/gdb/testsuite/gdb.dwarf2/dwznolink.exp
index cbabe8f828d4f2e46004eb41a1e6d091cabd06ae..88fa7c03fe6cb53b7d658f76175a678342454983 100644
--- a/gdb/testsuite/gdb.dwarf2/dwznolink.exp
+++ b/gdb/testsuite/gdb.dwarf2/dwznolink.exp
@@ -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"
diff --git a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
index 54cd199a9473998dfab08a14f27f6e82529a9ea4..1dd98174df81f650e9a276b617c575041d32560a 100644
--- a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
+++ b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
@@ -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. "
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 7dcf13f2ce7bdbdd78c4972ac0e6c7a55958afdd..0b1f01c50b92fae176b97e063ffed7f57f03dc91 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -3006,6 +3006,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]