diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index 44d5c87a5f7..e99acc5e752 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -160,6 +160,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
 	    case DW_AT_specification:
 	    case DW_AT_abstract_origin:
 	    case DW_AT_extension:
+	    case DW_AT_signature:
 	      has_specification_or_origin = true;
 	      break;
 
diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h
index 60ea581cbbe..f0ff356a6d1 100644
--- a/gdb/dwarf2/cooked-index-entry.h
+++ b/gdb/dwarf2/cooked-index-entry.h
@@ -79,6 +79,11 @@ union cooked_index_entry_ref
   parent_map::addr_type deferred;
 };
 
+/* Type that maps DW_AT_signature values for a TU to the name of the
+   primary type within the TU.  */
+
+using signature_to_name_map = std::unordered_map<ULONGEST, const char *>;
+
 /* Return a string representation of FLAGS.  */
 
 std::string to_string (cooked_index_flag flags);
diff --git a/gdb/dwarf2/cooked-index-shard.c b/gdb/dwarf2/cooked-index-shard.c
index 91dc9ab8945..68ecbfa207c 100644
--- a/gdb/dwarf2/cooked-index-shard.c
+++ b/gdb/dwarf2/cooked-index-shard.c
@@ -20,6 +20,7 @@
 #include "dwarf2/cooked-index-shard.h"
 #include "dwarf2/tag.h"
 #include "dwarf2/index-common.h"
+#include "dwarf2/read.h"
 #include "cp-support.h"
 #include "c-lang.h"
 #include "ada-lang.h"
@@ -190,8 +191,30 @@ struct cooked_index_entry_name_ptr_eq
 /* See cooked-index-shard.h.  */
 
 void
-cooked_index_shard::finalize (const parent_map_map *parent_maps)
+cooked_index_shard::finalize (const parent_map_map *parent_maps,
+			      const signature_to_name_map *sig_names)
 {
+  for (const std::pair<cooked_index_entry *, ULONGEST> &entry_to_sig
+	 : m_deferred_names)
+    {
+      ULONGEST signature = entry_to_sig.second;
+      cooked_index_entry *entry = entry_to_sig.first;
+
+      const auto it = sig_names->find (signature);
+      if (it != sig_names->end ())
+	{
+	  /* The previous name was the empty string literal, so
+	     overwriting it doesn't leak.  The new name will point
+	     into the DWARF data.  */
+	  entry->name = it->second;
+	}
+      else
+	complaint (_("Cannot find signatured DIE %s referenced from DIE at %s"
+		     " [in module %s]"),
+		   hex_string (signature), sect_offset_str (entry->die_offset),
+		   entry->per_cu->per_bfd ()->filename ());
+    }
+
   gdb::unordered_set<const cooked_index_entry *,
 		     cooked_index_entry_name_ptr_hash,
 		     cooked_index_entry_name_ptr_eq> seen_names;
diff --git a/gdb/dwarf2/cooked-index-shard.h b/gdb/dwarf2/cooked-index-shard.h
index 84c37958c83..4f25fb594a1 100644
--- a/gdb/dwarf2/cooked-index-shard.h
+++ b/gdb/dwarf2/cooked-index-shard.h
@@ -79,6 +79,16 @@ class cooked_index_shard
      for completion, will be returned.  */
   range find (const std::string &name, bool completing) const;
 
+  /* Record that ENTRY didn't have a name attribute, but did have a
+     DW_AT_signature attribute, SIGNATURE.  ENTRY will have been
+     assigned a fake, empty, name string.  We will patch the name of
+     ENTRY during finalization once the TUs have been parsed, the
+     correct TU will be found using SIGNATURE.  */
+  void add_deferred_name (cooked_index_entry *entry, ULONGEST signature)
+  {
+    m_deferred_names.emplace_back (entry, signature);
+  }
+
 private:
 
   /* Return the entry that is believed to represent the program's
@@ -122,7 +132,8 @@ class cooked_index_shard
      the index has been fully populated.  It enters all the entries
      into the internal table and fixes up all missing parent links.
      This may be invoked in a worker thread.  */
-  void finalize (const parent_map_map *parent_maps);
+  void finalize (const parent_map_map *parent_maps,
+		 const signature_to_name_map *sig_names);
 
   /* Storage for the entries.  */
   auto_obstack m_storage;
@@ -134,6 +145,12 @@ class cooked_index_shard
   addrmap_fixed *m_addrmap = nullptr;
   /* Storage for canonical names.  */
   gdb::string_set m_names;
+
+  /* Entries without a name, but with a signature.  These entries will
+     have been given a fake name, the empty string when they were
+     created, but we need to patch these up with a real name during
+     finalization.  */
+  std::vector<std::pair<cooked_index_entry *, ULONGEST>> m_deferred_names;
 };
 
 using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>;
diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c
index 723e027172e..054b25ba147 100644
--- a/gdb/dwarf2/cooked-index-worker.c
+++ b/gdb/dwarf2/cooked-index-worker.c
@@ -249,6 +249,15 @@ cooked_index_worker::done_reading ()
       m_all_parents_map.add_map (*one_result.get_parent_map ());
   }
 
+  {
+    scoped_time_it time_it ("DWARF add signature name map", m_per_command_time);
+
+    /* Combine all of the signature to name maps.  */
+    for (cooked_index_worker_result &one_result : m_results)
+      for (const auto &[sig, name] : one_result.get_sig_name_map ())
+	m_all_sig_names_map.emplace (sig, name);
+  }
+
   /* Update all the CU inclusion information.  */
   for (auto &item : m_results)
     item.invert_cu_inclusions ();
diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h
index 1799c0b9e08..262b727b983 100644
--- a/gdb/dwarf2/cooked-index-worker.h
+++ b/gdb/dwarf2/cooked-index-worker.h
@@ -83,6 +83,25 @@ class cooked_index_worker_result
     return m_shard->add (name);
   }
 
+  /* Record that ENTRY was missing a name attribute, but did have a
+     DW_AT_signature attribute, SIGNATURE.  When the TUs are
+     processed, which might not happen until after CU processing for
+     skeletonless TUs, we can find a name and supply it at that
+     point.  */
+  void add_deferred_name (cooked_index_entry *entry, ULONGEST signature)
+  {
+    m_shard->add_deferred_name (entry, signature);
+  }
+
+  /* Called when processing TUs to record that the primary type within
+     a type unit with SIGNATURE, was called NAME.  This information
+     will be used during finalization to fix-up the name of any
+     entries passed to add_deferred_name.  */
+  void add_signatured_type_name (ULONGEST signature, const char *name)
+  {
+    m_sig_name_map.emplace (signature, name);
+  }
+
   /* Install the current addrmap into the shard being constructed,
      then transfer ownership of the index to the caller.  */
   cooked_index_shard_up release_shard ()
@@ -153,6 +172,12 @@ class cooked_index_worker_result
      discovered.  */
   void invert_cu_inclusions ();
 
+  /* The signature to name map for this worker.  */
+  const signature_to_name_map &get_sig_name_map () const
+  {
+    return m_sig_name_map;
+  }
+
 private:
   /* The abbrev table cache used by this indexer.  */
   abbrev_table_cache m_abbrev_table_cache;
@@ -188,6 +213,9 @@ class cooked_index_worker_result
   /* Parent map for each CU that is read.  */
   parent_map m_parent_map;
 
+  /* Signature to name map for the primary type in a TU.  */
+  signature_to_name_map m_sig_name_map;
+
   /* A writeable addrmap being constructed by this scanner.  */
   addrmap_mutable m_addrmap;
 
@@ -272,6 +300,12 @@ class cooked_index_worker
     return &m_all_parents_map;
   }
 
+  /* Return the map containing the complete signature to name information.  */
+  const signature_to_name_map &get_sig_name_map () const
+  {
+    return m_all_sig_names_map;
+  }
+
 protected:
 
   /* Let cooked_index call the 'set' and 'write_to_cache' methods.  */
@@ -319,6 +353,10 @@ class cooked_index_worker
      parent relationships.  */
   parent_map_map m_all_parents_map;
 
+  /* Map from signature to name of primary type within a TU.  This is
+     the combined map, built after all the workers have finished.  */
+  signature_to_name_map m_all_sig_names_map;
+
   /* Current state of this object.  */
   cooked_state m_state = cooked_state::INITIAL;
   /* Mutex and condition variable used to synchronize.  */
diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index 167e39ffc89..d91f1267704 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -90,11 +90,12 @@ cooked_index::set_contents ()
     {
       auto this_shard = shard.get ();
       const parent_map_map *parent_maps = m_state->get_parent_map_map ();
-      finalizers.add_task ([this, this_shard, parent_maps] ()
+      const signature_to_name_map *sig_name_map = &m_state->get_sig_name_map ();
+      finalizers.add_task ([this, this_shard, parent_maps, sig_name_map] ()
 	{
 	  scoped_time_it time_it ("DWARF finalize worker",
 				  m_state->m_per_command_time);
-	  this_shard->finalize (parent_maps);
+	  this_shard->finalize (parent_maps, sig_name_map);
 	});
     }
 
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 581c0eb0763..f94a0785f52 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -157,6 +157,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
 				 parent_map::addr_type *maybe_defer,
 				 bool *is_enum_class,
 				 bool *is_inlined,
+				 std::optional<ULONGEST> *signature,
 				 bool for_specification)
 {
   bool is_declaration = false;
@@ -234,6 +235,16 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
 		     attr.get_ref_die_offset () };
 	  break;
 
+	case DW_AT_signature:
+	  /* The DW_AT_signature could also be a direct reference to a
+	     type DIE.  We don't currently try to capture those
+	     signatures as right now we're only capturing this in
+	     order to handle the fact that some TUs are parsed after
+	     the parallel CU parsing.  */
+	  if (attr.form == DW_FORM_ref_sig8)
+	    signature->emplace (attr.as_signature ());
+	  break;
+
 	case DW_AT_external:
 	  if (attr.as_boolean ())
 	    *flags &= ~IS_STATIC;
@@ -380,7 +391,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
 	scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
 			 new_info_ptr, new_abbrev, name, linkage_name,
 			 flags, nullptr, parent_entry, maybe_defer,
-			 is_enum_class, is_inlined, true);
+			 is_enum_class, is_inlined, signature, true);
     }
 
   if (!for_specification)
@@ -547,6 +558,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
       const cooked_index_entry *this_parent_entry = parent_entry;
       bool is_enum_class = false;
       bool is_inlined = false;
+      std::optional<ULONGEST> signature;
 
       /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
 	 its surrounding subroutine.  */
@@ -556,7 +568,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
 	= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
 			   abbrev, &name, &linkage_name, &flags, &sibling,
 			   &this_parent_entry, &defer, &is_enum_class,
-			   &is_inlined, false);
+			   &is_inlined, &signature, false);
       /* A DW_TAG_entry_point inherits its static/extern property from
 	 the enclosing subroutine.  */
       if (abbrev->tag == DW_TAG_entry_point)
@@ -611,6 +623,17 @@ cooked_indexer::index_dies (cutu_reader *reader,
       cooked_index_entry *this_entry = nullptr;
       /* Always use the reader's CU for the entry CU.  */
       dwarf2_per_cu *cu_for_entry = reader->cu ()->per_cu;
+
+      /* If scan_attributes failed to find a name then we might still
+	 want to create an entry if we think we might later find a
+	 name via a signature.  If this is the case then we set NAME
+	 to be the empty string to avoid having to handle NULL.  */
+      bool found_name = name != nullptr;
+      if (!found_name && signature.has_value ())
+	name = "";
+
+      /* Check NAME here, not FOUND_NAME as NAME might have been
+	 updated above.  */
       if (name != nullptr)
 	{
 	  if (defer != 0)
@@ -624,6 +647,11 @@ cooked_indexer::index_dies (cutu_reader *reader,
 	      = m_index_storage->add (this_die, abbrev->tag, flags,
 				      m_language, name,
 				      this_parent_entry, cu_for_entry);
+
+	  /* Record that this entry doesn't have a valid name and
+	     needs patching.  */
+	  if (signature.has_value () && !found_name)
+	    m_index_storage->add_deferred_name (this_entry, signature.value ());
 	}
       else if (this_parent_entry != nullptr)
 	{
@@ -637,6 +665,18 @@ cooked_indexer::index_dies (cutu_reader *reader,
 	  m_die_range_map->add_entry (addr, addr, this_parent_entry);
 	}
 
+      /* If THIS_DIE is the primary type within a TU, and has a valid
+	 name, then add an entry mapping the signature to the name.  */
+      if (found_name
+	  && cu_for_entry->is_debug_types ()
+	  && this_die == (reader->cu ()->header.sect_off
+			  + to_underlying (reader->cu ()->header.type_offset_in_tu)))
+	{
+	  const signatured_type *st = cu_for_entry->as_signatured_type ();
+	  if (st != nullptr)
+	    m_index_storage->add_signatured_type_name (st->signature, name);
+	}
+
       if (linkage_name != nullptr)
 	{
 	  /* We only want this to be "main" if it has a linkage name
diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h
index 563742c90f6..393ef9fca5a 100644
--- a/gdb/dwarf2/cooked-indexer.h
+++ b/gdb/dwarf2/cooked-indexer.h
@@ -86,6 +86,7 @@ class cooked_indexer
 				   parent_map::addr_type *maybe_defer,
 				   bool *is_enum_class,
 				   bool *is_inlined,
+				   std::optional<ULONGEST> *signature,
 				   bool for_specification);
 
   /* Handle DW_TAG_imported_unit, by scanning the DIE to find
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp
new file mode 100644
index 00000000000..e52f1e3b21a
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp
@@ -0,0 +1,160 @@
+# Copyright 2026 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/>.
+
+# The test sets up a type DIE without a DW_AT_name, the type DIE links
+# to a type within a TU via DW_AT_signature.  Normally GDB would
+# use the DW_AT_signature to find the name of the type DIE.
+#
+# However, in this test there is no type DIE with the specified
+# signature!
+#
+# This test checks that GDB can emit a complaint when this situation is
+# encountered.  The test also checks that trying to find a method
+# within the type using the qualified name will fail.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c .S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+proc run_test { version } {
+    # Create the DWARF.
+    set asm_file [standard_output_file $::srcfile2]
+    Dwarf::assemble {
+	filename $asm_file
+	add_dummy_cus 0
+    } {
+	upvar version version
+
+	get_func_info foo
+	get_func_info main
+
+	declare_labels method_decl
+
+	tu { version $version } 0xdeadbeefdeadbeef the_type {
+	    DW_TAG_type_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+	    } {
+		the_type: DW_TAG_class_type {
+		    DW_AT_name the_type
+		    DW_AT_byte_size 1 sdata
+		}
+	    }
+	}
+
+	cu { version $version } {
+	    compile_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+	    } {
+		DW_TAG_class_type {
+		    DW_AT_declaration 1 flag
+		    DW_AT_signature 0xdeadbeef01234567 ref_sig8
+		} {
+		    method_decl: DW_TAG_subprogram {
+			DW_AT_name method
+			DW_AT_linkage_name _ZN8the_type6methodEv
+			DW_AT_declaration 1 flag
+		    }
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_specification %$method_decl
+		    DW_AT_low_pc $foo_start DW_FORM_addr
+		    DW_AT_high_pc $foo_end DW_FORM_addr
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_name main
+		    DW_AT_low_pc $main_start DW_FORM_addr
+		    DW_AT_high_pc $main_end DW_FORM_addr
+		}
+	    }
+	}
+    }
+
+    set testfile_name ${::testfile}-${version}
+    set binfile_name [standard_output_file $testfile_name]
+    if { [prepare_for_testing "failed to prepare" $testfile_name \
+	      [list $asm_file $::srcfile] {nodebug}] } {
+	return
+    }
+
+    set expect_complaint true
+    # If we have a .gdb_index already then the part of this test that
+    # checks for a warning when loading the executable is not going to
+    # work as the warning will have already been generated when the
+    # index was added.
+    if {[get_index_type $testfile_name] == "gdb"} {
+	set expect_complaint false
+    }
+
+    # Restart without the executable so we can check for warnings when
+    # the executable is loaded.
+    clean_restart
+
+    set host_binfile [gdb_remote_download host $binfile_name]
+    if { [is_remote host] } {
+	# For some remote host boards gdb_remote_download returns an
+	# absolute path, but for others it returns a relative path.
+	# The path reported in the complaint message is always
+	# absolute.  Handle this with an optional prefix pattern.
+	set binfile_re "\[^\r\n\]*[string_to_regexp $host_binfile]"
+    } elseif {[section_get $binfile_name ".gnu_debuglink"] ne ""} {
+	set binfile_re "\[^\r\n\]+[string_to_regexp ${testfile_name}.debug]"
+    } else {
+	set binfile_re [string_to_regexp $binfile_name]
+    }
+
+    # Load the executable and check we see the expected complaint from
+    # the DWARF indexer.
+    gdb_test_no_output "maint set dwarf synchronous on"
+    gdb_test_no_output "set complaints 100"
+    set saw_complaint false
+
+    gdb_test_multiple "file $host_binfile" "file command" -lbl {
+	-re "\r\nDuring symbol reading: (?:DWARF Error: )?Cannot find signatured DIE 0xdeadbeef01234567 referenced from DIE at $::hex \\\[in module $binfile_re\\\](?=\r\n)" {
+	    # The 'DWARF Error' part only occurs when using the
+	    # 'readnow' board, this is the error when triggered from
+	    # the full symbol reader rather than the indexer.
+	    set saw_complaint true
+	    exp_continue
+	}
+
+	-re -wrap "" {
+	    gdb_assert { $saw_complaint == $expect_complaint } $gdb_test_name
+	}
+    }
+
+    if { ![readnow] } {
+	# Check that GDB is unable to find this via the index.  If we
+	# load the full symbols then GDB is able to resolve this print
+	# via the linkage name.
+	#
+	# It is unfortunate that there is a difference in behaviour
+	# depending on whether GDB is using the index or not, but
+	# remember, the DWARF is corrupted at this point, so it's
+	# probably OK if things don't fully work, so long as GDB
+	# doesn't crash.
+	gdb_test "print the_type::method" \
+	    "^There is no field named method"
+    }
+}
+
+foreach_with_prefix version { 4 5 } {
+    run_test $version
+}
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp
new file mode 100644
index 00000000000..0b89c811976
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp
@@ -0,0 +1,139 @@
+# Copyright 2026 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/>.
+
+# Like sig-type-unnamed-class.exp, but with split DWARF.  The type
+# unit in the DWO file is skeletonless, which is the norm when using
+# split DWARF.
+#
+# Check that the cooked index correctly resolves the parent of a member
+# function when the class declaration stub in the DWO compile unit has
+# DW_AT_signature but no DW_AT_name, and the referenced type unit is
+# skeletonless.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c -dw.S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+
+proc run_test { version } {
+    set asm_file [standard_output_file $::srcfile2]
+
+    # Setup some state based on DWARF version.
+    if { $version == 5 } {
+	set dwo_cu_opts [list fission 1 version 5 dwo_id 0xF00D]
+	set skel_cu_opts [list version 5 dwo_id 0xF00D]
+	set dwo_name_attr DW_AT_dwo_name
+    } else {
+	set dwo_cu_opts [list fission 1 version 4]
+	set skel_cu_opts [list version 4]
+	set dwo_name_attr DW_AT_GNU_dwo_name
+    }
+
+    Dwarf::assemble $asm_file {
+	upvar version version
+	upvar dwo_cu_opts dwo_cu_opts
+	upvar skel_cu_opts skel_cu_opts
+	upvar dwo_name_attr dwo_name_attr
+
+	declare_labels method_decl
+
+	get_func_info foo
+
+	# Type unit in the DWO file.  This is skeletonless: there is no
+	# corresponding skeleton TU in the main file.
+	tu {
+	    fission 1
+	    version $version
+	} 0xdeadbeef01234567 the_type {
+	    type_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+	    } {
+		the_type: DW_TAG_class_type {
+		    DW_AT_name the_type
+		    DW_AT_byte_size 1 DW_FORM_sdata
+		}
+	    }
+	}
+
+	set debug_addr_base [debug_addr_label]
+
+	# Compile unit in the DWO file.  Contains a class declaration
+	# stub with DW_AT_signature but no DW_AT_name, replicating what
+	# Clang emits with -fdebug-types-section.  The class name must
+	# be resolved by following the signature to the type unit.
+	cu $dwo_cu_opts {
+	    compile_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+		DW_AT_name ${::srcfile}
+		DW_AT_comp_dir .
+		if { $version == 4 } {
+		    DW_AT_GNU_dwo_id 0xF00D DW_FORM_data8
+		}
+	    } {
+		DW_TAG_class_type {
+		    DW_AT_declaration 1 DW_FORM_flag
+		    DW_AT_signature 0xdeadbeef01234567 DW_FORM_ref_sig8
+		} {
+		    method_decl: DW_TAG_subprogram {
+			DW_AT_name method
+			DW_AT_linkage_name _ZN8the_type6methodEv
+			DW_AT_declaration 1 DW_FORM_flag
+		    }
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_specification :$method_decl
+		    DW_AT_low_pc $foo_start DW_FORM_GNU_addr_index
+		    DW_AT_high_pc $foo_end DW_FORM_GNU_addr_index
+		}
+	    }
+	}
+
+	# Skeleton CU in the main file.
+	cu $skel_cu_opts {
+	    compile_unit {
+		$dwo_name_attr ${::gdb_test_file_name}-dw.dwo DW_FORM_strp
+		DW_AT_comp_dir .
+		DW_AT_GNU_addr_base $debug_addr_base
+		if { $version == 4 } {
+		    DW_AT_GNU_dwo_id 0xF00D DW_FORM_data8
+		}
+	    } {}
+	}
+    }
+
+    set obj [standard_output_file "${::testfile}-dw.o"]
+    if {[build_executable_and_dwo_files "${::testfile}.exp" "${::binfile}" \
+	     {nodebug} \
+	     [list $asm_file {nodebug split-dwo} $obj] \
+	     [list $::srcfile {nodebug}]]} {
+	return
+    }
+
+    clean_restart ${::testfile}
+
+    # Check that GDB was able to find the parent for "method", and as
+    # a result, can correctly find this field of "the_type".
+    gdb_test "print the_type::method" \
+	[string_to_regexp " <the_type::method()>"]
+}
+
+foreach_with_prefix version { 4 5 } {
+    run_test $version
+}
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp
new file mode 100644
index 00000000000..6f39e2c823d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp
@@ -0,0 +1,106 @@
+# Copyright 2026 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/>.
+
+# Check that the cooked index correctly resolves the parent of a member
+# function when the class declaration stub in the compile unit has
+# DW_AT_signature but no DW_AT_name.
+#
+# This replicates what Clang emits with -fdebug-types-section.  The
+# compile unit contains an unnamed DW_TAG_class_type declaration with
+# only DW_AT_declaration, DW_AT_signature, and child member function
+# declarations.
+#
+# The class name must be resolved by following the signature to the
+# type unit.  Without this, the member function definitions (which use
+# DW_AT_specification to point at the child declarations) end up with
+# no parent, and qualified lookup fails.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c .S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+proc run_test { version } {
+    # Create the DWARF.
+    set asm_file [standard_output_file $::srcfile2]
+    Dwarf::assemble {
+	filename $asm_file
+	add_dummy_cus 0
+    } {
+	upvar version version
+
+	get_func_info foo
+	get_func_info main
+
+	declare_labels method_decl
+
+	tu { version $version } 0xdeadbeef01234567 the_type {
+	    DW_TAG_type_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+	    } {
+		the_type: DW_TAG_class_type {
+		    DW_AT_name the_type
+		    DW_AT_byte_size 1 sdata
+		}
+	    }
+	}
+
+	cu { version $version } {
+	    compile_unit {
+		DW_AT_language @DW_LANG_C_plus_plus
+	    } {
+		DW_TAG_class_type {
+		    DW_AT_declaration 1 flag
+		    DW_AT_signature 0xdeadbeef01234567 ref_sig8
+		} {
+		    method_decl: DW_TAG_subprogram {
+			DW_AT_name method
+			DW_AT_linkage_name _ZN8the_type6methodEv
+			DW_AT_declaration 1 flag
+		    }
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_specification %$method_decl
+		    DW_AT_low_pc $foo_start DW_FORM_addr
+		    DW_AT_high_pc $foo_end DW_FORM_addr
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_name main
+		    DW_AT_low_pc $main_start DW_FORM_addr
+		    DW_AT_high_pc $main_end DW_FORM_addr
+		}
+	    }
+	}
+    }
+
+    if { [prepare_for_testing "failed to prepare" ${::testfile}-${version} \
+	      [list $asm_file $::srcfile] {nodebug}] } {
+	return
+    }
+
+    # Check that GDB was able to find the parent for "method", and as
+    # a result, can correctly find this field of "the_type".
+    gdb_test "print the_type::method" \
+	[string_to_regexp " <the_type::method()>"]
+}
+
+foreach_with_prefix version { 4 5 } {
+    run_test $version
+}
