[lld] [lld][ELF] Implement merged .debug_names section. (PR #86508)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 25 07:36:31 PDT 2024


https://github.com/cmtice created https://github.com/llvm/llvm-project/pull/86508

Update LLD to be able to generate a single merged .debug_names section (rather than a set of appended sections). Option is controlled by a new flag, --debug-names. Does not handle type units yet (coming soon).

>From 1b844ae7307d4d72a8a9e9b48d7c96618640bb12 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 25 Mar 2024 00:00:27 -0700
Subject: [PATCH] [lld][ELF] Implement merged .debug_names section.

Update LLD to be able to generate a single merged .debug_names section (rather
than a set of appended sections). Option is controlled by a new flag, --debug-names.
Does not handle type units yet (coming soon).
---
 lld/ELF/Config.h                              |   1 +
 lld/ELF/DWARF.cpp                             |   1 +
 lld/ELF/DWARF.h                               |   5 +
 lld/ELF/Driver.cpp                            |   3 +
 lld/ELF/Options.td                            |   4 +
 lld/ELF/SyntheticSections.cpp                 | 761 ++++++++++++++++++
 lld/ELF/SyntheticSections.h                   | 134 +++
 lld/ELF/Writer.cpp                            |   3 +
 lld/docs/ld.lld.1                             |   4 +
 lld/test/ELF/Inputs/debug-names-2.s           | 191 +++++
 .../ELF/Inputs/debug-names-parent-idx-2.s     | 347 ++++++++
 lld/test/ELF/debug-names-bad-aug-string.s     | 189 +++++
 lld/test/ELF/debug-names-bad-die-idx-sizes.s  | 151 ++++
 lld/test/ELF/debug-names-bad-name-count.s     | 154 ++++
 lld/test/ELF/debug-names-bad-offsets-sizes.s  | 153 ++++
 lld/test/ELF/debug-names-bad-version.s        | 173 ++++
 lld/test/ELF/debug-names-invalid-attribute.s  | 179 ++++
 lld/test/ELF/debug-names-parent-idx.s         | 549 +++++++++++++
 lld/test/ELF/debug-names.s                    | 294 +++++++
 19 files changed, 3296 insertions(+)
 create mode 100644 lld/test/ELF/Inputs/debug-names-2.s
 create mode 100644 lld/test/ELF/Inputs/debug-names-parent-idx-2.s
 create mode 100644 lld/test/ELF/debug-names-bad-aug-string.s
 create mode 100644 lld/test/ELF/debug-names-bad-die-idx-sizes.s
 create mode 100644 lld/test/ELF/debug-names-bad-name-count.s
 create mode 100644 lld/test/ELF/debug-names-bad-offsets-sizes.s
 create mode 100644 lld/test/ELF/debug-names-bad-version.s
 create mode 100644 lld/test/ELF/debug-names-invalid-attribute.s
 create mode 100644 lld/test/ELF/debug-names-parent-idx.s
 create mode 100644 lld/test/ELF/debug-names.s

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index bb3608da80b21f..1a7c6dafe17e18 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -228,6 +228,7 @@ struct Config {
   bool cref;
   llvm::SmallVector<std::pair<llvm::GlobPattern, uint64_t>, 0>
       deadRelocInNonAlloc;
+  bool debugNames;
   bool demangle = true;
   bool dependentLibraries;
   bool disableVerify;
diff --git a/lld/ELF/DWARF.cpp b/lld/ELF/DWARF.cpp
index ac28aa8c7ca6e0..03fac65754f020 100644
--- a/lld/ELF/DWARF.cpp
+++ b/lld/ELF/DWARF.cpp
@@ -40,6 +40,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
                 .Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
                 .Case(".debug_line", &lineSection)
                 .Case(".debug_loclists", &loclistsSection)
+                .Case(".debug_names", &debugNamesSection)
                 .Case(".debug_ranges", &rangesSection)
                 .Case(".debug_rnglists", &rnglistsSection)
                 .Case(".debug_str_offsets", &strOffsetsSection)
diff --git a/lld/ELF/DWARF.h b/lld/ELF/DWARF.h
index 1b9a3e3f77943b..e1a830279b54d0 100644
--- a/lld/ELF/DWARF.h
+++ b/lld/ELF/DWARF.h
@@ -68,6 +68,10 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
     return gnuPubtypesSection;
   }
 
+  const LLDDWARFSection &getNamesSection() const override {
+    return debugNamesSection;
+  }
+
   StringRef getFileName() const override { return ""; }
   StringRef getAbbrevSection() const override { return abbrevSection; }
   StringRef getStrSection() const override { return strSection; }
@@ -95,6 +99,7 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
   LLDDWARFSection strOffsetsSection;
   LLDDWARFSection lineSection;
   LLDDWARFSection addrSection;
+  LLDDWARFSection debugNamesSection;
   StringRef abbrevSection;
   StringRef strSection;
   StringRef lineStrSection;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7257ebd0fac994..9a300a54e122bd 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -441,6 +441,8 @@ static void checkOptions() {
       error("-r and -pie may not be used together");
     if (config->exportDynamic)
       error("-r and --export-dynamic may not be used together");
+    if (config->debugNames)
+      error("-r and --debug-names may not be used together");
   }
 
   if (config->executeOnly) {
@@ -1231,6 +1233,7 @@ static void readConfigs(opt::InputArgList &args) {
   config->cref = args.hasArg(OPT_cref);
   config->optimizeBBJumps =
       args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false);
+  config->debugNames = args.hasFlag(OPT_debug_names, OPT_no_debug_names, false);
   config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
   config->dependencyFile = args.getLastArgValue(OPT_dependency_file);
   config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index c5e95d0d25c1ae..d470646ed0eeef 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -153,6 +153,10 @@ def : Flag<["--"], "no-color-diagnostics">, Alias<color_diagnostics>, AliasArgs<
 def cref: FF<"cref">,
   HelpText<"Output cross reference table. If -Map is specified, print to the map file">;
 
+defm debug_names: BB<"debug-names",
+    "Generate a merged .debug_names section",
+    "Do not generate a merged .debug_names section (default)">;
+
 defm demangle: B<"demangle",
     "Demangle symbol names (default)",
     "Do not demangle symbol names">;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index f924756ddddfcd..6b84f4d806f356 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "SyntheticSections.h"
+
 #include "Config.h"
 #include "DWARF.h"
 #include "EhFrame.h"
@@ -34,7 +35,9 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
+#include "llvm/Support/DJB.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/LEB128.h"
 #include "llvm/Support/Parallel.h"
@@ -2690,6 +2693,759 @@ static uint32_t computeGdbHash(StringRef s) {
   return h;
 }
 
+DebugNamesSection::DebugNamesSection()
+    : SyntheticSection(0, SHT_PROGBITS, 1, ".debug_names") {}
+
+template <class ELFT, class RelTy>
+void DebugNamesSection::getNameRelocsImpl(
+    InputSection *sec, ArrayRef<RelTy> rels,
+    llvm::DenseMap<uint32_t, uint32_t> &relocs) {
+  for (auto &rel : rels) {
+    Symbol &sym = sec->getFile<ELFT>()->getRelocTargetSym(rel);
+    relocs[rel.r_offset] = sym.getVA(getAddend<ELFT>(rel));
+  }
+}
+
+template <class ELFT>
+void DebugNamesSection::getNameRelocs(
+    InputSectionBase *base, llvm::DenseMap<uint32_t, uint32_t> &relocs) {
+  auto *sec = cast<InputSection>(base);
+  const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+  if (rels.areRelocsRel())
+    getNameRelocsImpl<ELFT>(sec, rels.rels, relocs);
+  else
+    getNameRelocsImpl<ELFT>(sec, rels.relas, relocs);
+}
+
+void DebugNamesSection::writeTo(uint8_t *buf) { invokeELFT(writeToImpl, buf); }
+
+template <class ELFT> void DebugNamesSection::writeToImpl(uint8_t *buf) {
+  SmallVector<uint32_t, 0> mergedCuOffsets;
+  SmallVector<uint32_t, 0> mergedTuOffsets;
+  llvm::DenseMap<uint32_t, uint32_t> strOffsets;
+  SmallVector<llvm::DenseMap<uint32_t, uint32_t>, 0> chunksRelocs;
+  chunksRelocs.reserve(numChunks);
+
+  for (size_t i = 0; i < numChunks; ++i) {
+    DebugNamesOutputChunk &chunk = outputChunks[i];
+    InputSectionBase *base = inputDebugNamesSections[i];
+    llvm::DenseMap<uint32_t, uint32_t> relocs;
+    getNameRelocs<ELFT>(base, relocs);
+    chunksRelocs.push_back(std::move(relocs));
+
+    // Update CuOffsets list with new data
+    for (uint32_t cuOffset : chunk.compilationUnits)
+      mergedCuOffsets.push_back(chunk.sec->outSecOff + cuOffset);
+
+    // TODO: Update TuOffsets list with new data
+  }
+
+  // Update the entries with the relocated string offsets.
+  for (auto &stringEntry : mergedEntries) {
+    uint32_t oldOffset = stringEntry.stringOffsetOffset;
+    uint32_t idx = stringEntry.chunkIdx;
+    stringEntry.relocatedEntryOffset = chunksRelocs[idx][oldOffset];
+  }
+
+  // Write out bytes for merged section.
+
+  // Write the header.
+  endian::write32<ELFT::TargetEndianness>(buf + 0, mergedHdr.UnitLength);
+  endian::write16<ELFT::TargetEndianness>(buf + 4, mergedHdr.Version);
+  endian::write32<ELFT::TargetEndianness>(buf + 8, mergedHdr.CompUnitCount);
+  endian::write32<ELFT::TargetEndianness>(buf + 12,
+                                          mergedHdr.LocalTypeUnitCount);
+  endian::write32<ELFT::TargetEndianness>(buf + 16,
+                                          mergedHdr.ForeignTypeUnitCount);
+  endian::write32<ELFT::TargetEndianness>(buf + 20, mergedHdr.BucketCount);
+  endian::write32<ELFT::TargetEndianness>(buf + 24, mergedHdr.NameCount);
+  endian::write32<ELFT::TargetEndianness>(buf + 28, mergedHdr.AbbrevTableSize);
+  endian::write32<ELFT::TargetEndianness>(buf + 32,
+                                          mergedHdr.AugmentationStringSize);
+  buf += 36;
+  memcpy(buf, mergedHdr.AugmentationString.c_str(), 8);
+  buf += 8;
+
+  // Write the CU list.
+  for (uint32_t offset : mergedCuOffsets) {
+    endian::write32<ELFT::TargetEndianness>(buf + 0, offset);
+    buf += 4;
+  }
+
+  // Write the local TU list.
+  // TODO: Fix this, once we get everything working without TUs.
+  if (mergedHdr.LocalTypeUnitCount != 0)
+    warn(".debug_names: type units are not handled in merged index");
+
+  // Write the foreign TU list.
+  // Currently LLVM does not emit foreign type units, so there should
+  // be nothing here to emit.
+  // TODO: Fix this, once we get everything working wtihout TUs.
+  if (mergedHdr.ForeignTypeUnitCount != 0)
+    warn(".debug_names: type units are not handled in merged index");
+
+  // Write the hash table.
+  // ... Write the buckets
+  uint32_t idx = 1;
+  for (const auto &bucket : bucketList) {
+    if (!bucket.empty())
+      endian::write32<ELFT::TargetEndianness>(buf + 0, idx);
+    idx += bucket.size();
+    buf += 4;
+  }
+
+  // ...Write the hashes
+  for (const auto &bucket : bucketList) {
+    for (const auto &entry : bucket) {
+      uint32_t hashValue = entry->hashValue;
+      endian::write32<ELFT::TargetEndianness>(buf + 0, hashValue);
+      buf += 4;
+    }
+  }
+
+  // Write the string offsets.
+  for (const auto &entry : mergedEntries) {
+    endian::write32<ELFT::TargetEndianness>(buf + 0,
+                                            entry.relocatedEntryOffset);
+    buf += 4;
+  }
+
+  // Write the entry offsets.
+  for (const auto &entry : mergedEntries) {
+    endian::write32<ELFT::TargetEndianness>(buf + 0, entry.entryOffset);
+    buf += 4;
+  }
+
+  // Write the abbrev table.
+  for (const auto *abbrev : mergedAbbrevTable) {
+    size_t uleb_size = encodeULEB128(abbrev->code, buf);
+    buf += uleb_size;
+    uleb_size = encodeULEB128(abbrev->tag, buf);
+    buf += uleb_size;
+    for (auto attr : abbrev->attributes) {
+      uleb_size = encodeULEB128(attr.Index, buf);
+      buf += uleb_size;
+      uleb_size = encodeULEB128(attr.Form, buf);
+      buf += uleb_size;
+    }
+    endian::write16<ELFT::TargetEndianness>(buf + 0, 0); // attribute sentinels.
+    buf += 2;
+  }
+  *buf++ = 0; // abbrev table sentinel
+
+  // Write the entry pool.
+  for (const auto &stringEntry : mergedEntries) {
+    // Write all the entries for the string.
+    size_t uleb_size;
+    for (const auto &entry : stringEntry.indexEntries) {
+      uleb_size = encodeULEB128(entry->abbrevCode, buf);
+      buf += uleb_size;
+      for (const auto &value : entry->attrValues) {
+        if (value.attrSize > 0) {
+          endian::write32<ELFT::TargetEndianness>(buf + 0, value.attrValue);
+          buf += value.attrSize;
+        }
+      }
+    }
+    *buf++ = 0; // Entry sentinel
+  }
+}
+
+bool DebugNamesSection::isNeeded() const { return numChunks > 0; }
+
+template <class ELFT>
+static void
+readCompileUnitOffsets(struct DebugNamesSection::DebugNamesSectionData &secData,
+                       DebugNamesSection::DebugNamesInputChunk &inputChunk,
+                       DebugNamesSection::DebugNamesOutputChunk &outputChunk,
+                       llvm::DWARFDataExtractor &namesExtractor) {
+  uint64_t offset = secData.locs.CUsBase;
+  uint64_t *offsetPtr = &offset;
+  for (size_t i = 0; i < secData.hdr.CompUnitCount; ++i) {
+    llvm::Error err = llvm::Error::success();
+    uint32_t value = namesExtractor.getU32(offsetPtr, &err);
+    if (err)
+      errorOrWarn(toString(inputChunk.namesSection->sec) +
+                  ": error reading CU offsets: " + toString(std::move(err)));
+    else
+      outputChunk.compilationUnits.push_back(value);
+  }
+}
+
+template <class ELFT>
+static void
+readEntryOffsets(struct DebugNamesSection::DebugNamesSectionData &secData,
+                 DebugNamesSection::DebugNamesInputChunk &chunk,
+                 llvm::DWARFDataExtractor &namesExtractor) {
+  secData.entryOffsets = std::make_unique<uint32_t[]>(secData.hdr.NameCount);
+  if (secData.locs.EntryOffsetsBase >= namesExtractor.getData().size())
+    errorOrWarn(toString(chunk.namesSection->sec) +
+                ": invalid entry offsets input");
+
+  uint64_t offset = secData.locs.EntryOffsetsBase;
+  uint64_t *offsetPtr = &offset;
+  for (size_t i = 0; i < secData.hdr.NameCount; ++i) {
+    llvm::Error err = llvm::Error::success();
+    uint32_t value = namesExtractor.getU32(offsetPtr, &err);
+    if (err)
+      errorOrWarn(toString(chunk.namesSection->sec) +
+                  ": error reading entry offsets: " + toString(std::move(err)));
+    else
+      secData.entryOffsets.get()[i] = value;
+  }
+}
+
+template <class ELFT>
+static void readAttributeValues(
+    SmallVector<DebugNamesSection::AttrValueData, 3> &values,
+    DebugNamesSection::DebugNamesInputChunk &chunk, uint64_t &offset,
+    struct DebugNamesSection::DebugNamesSectionData &secData,
+    int32_t &parentOffset, llvm::DWARFDataExtractor &namesExtractor,
+    const llvm::DWARFDebugNames::Abbrev &abbrev) {
+  const LLDDWARFSection &namesSection = *chunk.namesSection;
+  uint64_t *offsetPtr = &offset;
+  DebugNamesSection::AttrValueData cuOrTuAttr = {0, 0};
+  for (auto attr : abbrev.Attributes) {
+    llvm::Error err = llvm::Error::success();
+    DebugNamesSection::AttrValueData newAttr;
+    uint32_t value;
+    switch (attr.Form) {
+    case DW_FORM_flag_present: {
+      // Currently only DW_IDX_parent attributes (in .debug_names) can
+      // have this form. This form does not have a real value (nothing is
+      // emitted for it).
+      break;
+    }
+    case DW_FORM_data1:
+    case DW_FORM_ref1: {
+      newAttr.attrValue = namesExtractor.getU8(offsetPtr, &err);
+      newAttr.attrSize = 1;
+      break;
+    }
+    case DW_FORM_data2:
+    case DW_FORM_ref2: {
+      value = namesExtractor.getU16(offsetPtr, &err);
+      newAttr.attrValue = value;
+      newAttr.attrSize = 2;
+      break;
+    }
+    case DW_FORM_data4:
+    case DW_FORM_ref4: {
+      value = namesExtractor.getU32(offsetPtr, &err);
+      newAttr.attrValue = value;
+      newAttr.attrSize = 4;
+      if (attr.Index == dwarf::DW_IDX_parent)
+        parentOffset = value + secData.locs.EntriesBase;
+      break;
+    }
+    case DW_FORM_data8:
+    case DW_FORM_ref8:
+    case DW_FORM_ref_sig8: {
+      value = namesExtractor.getU64(offsetPtr, &err);
+      newAttr.attrValue = value;
+      newAttr.attrSize = 8;
+      break;
+    }
+    default: {
+      errorOrWarn(toString(namesSection.sec) +
+                  Twine(": unrecognized form encoding ") + Twine(attr.Form) +
+                  " in .debug_names abbrev table");
+      break;
+    }
+    }
+    if (err)
+      errorOrWarn(
+          toString(namesSection.sec) +
+          ": error while reading attributes: " + toString(std::move(err)));
+    if (attr.Index == DW_IDX_compile_unit || attr.Index == DW_IDX_type_unit)
+      // Save it to put it at the end of the attributes list.
+      cuOrTuAttr = newAttr;
+    else if (attr.Form != DW_FORM_flag_present)
+      values.push_back(newAttr);
+  }
+
+  // Make sure the CU or TU index attribute is the last one in the list.
+  values.push_back(cuOrTuAttr);
+}
+
+template <class ELFT>
+static void readEntry(DebugNamesSection::NamedEntry &stringEntry,
+                      DebugNamesSection::DebugNamesInputChunk &chunk,
+                      DebugNamesSection::DebugNamesSectionData &secData,
+                      llvm::DWARFDataExtractor &namesExtractor,
+                      const llvm::DWARFDebugNames::NameIndex &ni) {
+  std::unique_ptr<LLDDWARFSection> &namesSection = chunk.namesSection;
+  uint64_t offset = stringEntry.entryOffset;
+  // Each entry ends with a zero 'sentinel' value
+  while (offset < namesSection->Data.size() &&
+         namesSection->Data[offset] != 0) {
+    // Read & store all entries (for the same string)
+    auto entry = std::make_unique<DebugNamesSection::IndexEntry>();
+    entry->poolOffset = offset;
+    llvm::Error err = llvm::Error::success();
+    uint64_t ulebValue = namesExtractor.getULEB128(&offset, &err);
+    if (err)
+      errorOrWarn(toString(chunk.namesSection->sec) +
+                  ": error reading entry: " + toString(std::move(err)));
+    entry->abbrevCode = static_cast<uint32_t>(ulebValue);
+    const auto &abbrevs = ni.getAbbrevs();
+    auto abbrevIt = abbrevs.find_as(entry->abbrevCode);
+    if (abbrevIt != abbrevs.end()) {
+      const llvm::DWARFDebugNames::Abbrev &abbrev = *abbrevIt;
+      readAttributeValues<ELFT>(entry->attrValues, chunk, offset, secData,
+                                entry->parentOffset, namesExtractor, abbrev);
+      stringEntry.indexEntries.push_back(std::move(entry));
+    }
+  }
+  if (offset >= namesSection->Data.size())
+    errorOrWarn(toString(chunk.namesSection->sec) +
+                ": encountered unexpected end of section while reading entry");
+}
+
+template <class ELFT>
+static void
+readEntries(struct DebugNamesSection::DebugNamesSectionData &secData,
+            DebugNamesSection::DebugNamesInputChunk &chunk,
+            llvm::DWARFDataExtractor &namesExtractor,
+            llvm::DataExtractor &strExtractor,
+            const llvm::DWARFDebugNames::NameIndex &ni) {
+  // Temporary map from entry offsets to address (pointer) of entry with that
+  // offset; used to find parent entries quickly.
+  DenseMap<uint32_t, DebugNamesSection::IndexEntry *> offsetMap;
+  // Reserve sizes for this chunk's hashes & namedEntries.
+  chunk.hashValues.reserve(secData.hdr.NameCount);
+  secData.namedEntries.reserve(secData.hdr.NameCount);
+  // Calculate the Entry Offsets, create initial records.
+  for (uint32_t i = 0; i < secData.hdr.NameCount; ++i) {
+    // Get string value
+    DebugNamesSection::NamedEntry stringEntry;
+    stringEntry.entryOffset =
+        secData.locs.EntriesBase + secData.entryOffsets[i];
+    uint64_t strOffsetOffset =
+        secData.locs.StringOffsetsBase + i * secData.dwarfSize;
+    stringEntry.stringOffsetOffset = strOffsetOffset;
+    uint64_t strOffset =
+        namesExtractor.getRelocatedValue(secData.dwarfSize, &strOffsetOffset);
+    StringRef name = strExtractor.getCStrRef(&strOffset);
+    stringEntry.name = name.data();
+    // Calculate hash
+    stringEntry.hashValue = caseFoldingDjbHash(name);
+    chunk.hashValues.push_back(stringEntry.hashValue);
+    // Read String Entry
+    readEntry<ELFT>(stringEntry, chunk, secData, namesExtractor, ni);
+    // Add index entries & offsets to our temporary map
+    for (const auto &entry : stringEntry.indexEntries)
+      offsetMap[entry->poolOffset] = entry.get();
+    secData.namedEntries.push_back(std::move(stringEntry));
+  }
+  // Find/assign pointers to any 'real' parent entries (needed to find correct
+  // parent entry offsets in merged data).
+  for (auto &stringEntry : secData.namedEntries)
+    for (auto &entry : stringEntry.indexEntries)
+      if (entry->parentOffset != -1)
+        entry->parentEntry = offsetMap[entry->parentOffset];
+}
+
+static uint16_t computeDebugNamesHeaderSize() {
+  // Size of the .debug_names section header, in bytes, for DWARF32:
+  uint16_t size = /* unit length */ 4 +
+                  /* version */ 2 +
+                  /* padding */ 2 +
+                  /* CU count */ 4 +
+                  /* TU count */ 4 +
+                  /* Foreign TU count */ 4 +
+                  /* Bucket Count */ 4 +
+                  /* Name Count */ 4 +
+                  /* Abbrev table size */ 4 +
+                  /* Augmentation string size */ 4 +
+                  /* augmentation string */ 8;
+  return size;
+}
+
+template <class ELFT>
+static void collectDebugNamesSectionData(
+    DebugNamesSection::DebugNamesInputChunk &chunk,
+    DebugNamesSection::DebugNamesOutputChunk &outputChunk,
+    llvm::DWARFDataExtractor &namesExtractor,
+    llvm::DataExtractor &strExtractor) {
+  for (const auto &ni : *chunk.debugNamesData) {
+    DebugNamesSection::DebugNamesSectionData secData;
+    secData.hdr = ni.getHeader();
+    if (secData.hdr.Format != DwarfFormat::DWARF32)
+      errorOrWarn(toString(chunk.namesSection->sec) + ": unsupported DWARF64");
+    secData.dwarfSize = dwarf::getDwarfOffsetByteSize(DwarfFormat::DWARF32);
+    secData.hdrSize = computeDebugNamesHeaderSize();
+    if (secData.hdr.Version != 5)
+      errorOrWarn(toString(chunk.namesSection->sec) + ": unsupported version");
+    findDebugNamesOffsets(secData.locs, secData.hdrSize, secData.hdr.Format,
+                          secData.hdr);
+    readCompileUnitOffsets<ELFT>(secData, chunk, outputChunk, namesExtractor);
+    readEntryOffsets<ELFT>(secData, chunk, namesExtractor);
+    readEntries<ELFT>(secData, chunk, namesExtractor, strExtractor, ni);
+    chunk.sectionsData.push_back(std::move(secData));
+  }
+}
+
+void DebugNamesSection::collectMergedCounts(
+    MutableArrayRef<DebugNamesInputChunk> &inputChunks) {
+  SmallVector<uint32_t, 0> tmpMergedCuOffsets;
+
+  mergedHdr.CompUnitCount = 0;
+  mergedHdr.LocalTypeUnitCount = 0;
+  mergedHdr.ForeignTypeUnitCount = 0;
+  mergedHdr.Version = 5;
+  mergedHdr.Format = DwarfFormat::DWARF32;
+  mergedHdr.AugmentationStringSize = 0;
+
+  for (size_t i = 0; i < numChunks; ++i) {
+    DebugNamesInputChunk &inputChunk = inputChunks[i];
+    DebugNamesOutputChunk &outputChunk = outputChunks[i];
+    inputChunk.baseCuOffsetIdx = tmpMergedCuOffsets.size();
+    for (uint32_t cuOffset : outputChunk.compilationUnits)
+      tmpMergedCuOffsets.push_back(outputChunk.sec->outSecOff + cuOffset);
+    for (const DebugNamesSectionData &data : inputChunk.sectionsData) {
+      mergedHdr.CompUnitCount += data.hdr.CompUnitCount;
+      mergedHdr.LocalTypeUnitCount += data.hdr.LocalTypeUnitCount;
+      mergedHdr.ForeignTypeUnitCount += data.hdr.ForeignTypeUnitCount;
+      // Set & check the Augmentation String.
+      if (mergedHdr.AugmentationStringSize == 0) {
+        mergedHdr.AugmentationStringSize = data.hdr.AugmentationStringSize;
+        mergedHdr.AugmentationString = data.hdr.AugmentationString;
+      } else if ((mergedHdr.AugmentationStringSize !=
+                  data.hdr.AugmentationStringSize) ||
+                 (mergedHdr.AugmentationString !=
+                  data.hdr.AugmentationString)) {
+        // There are conflicting augmentation strings, so it's best for the
+        // merged index to not use an augmentation string.
+        StringRef emptyString = "        ";
+        mergedHdr.AugmentationStringSize = 8;
+        mergedHdr.AugmentationString = emptyString;
+      }
+    }
+  }
+}
+
+void DebugNamesSection::Abbrev::Profile(llvm::FoldingSetNodeID &id) const {
+  id.AddInteger(tag);
+  for (const DWARFDebugNames::AttributeEncoding &attr : attributes) {
+    id.AddInteger(attr.Index);
+    id.AddInteger(attr.Form);
+  }
+}
+
+std::pair<uint8_t, dwarf::Form> DebugNamesSection::getMergedCuSizeData() {
+  // Once we've merged all the CU offsets into a single list, the original
+  // DWARF form/size (often 1 byte) may be too small to hold indices to all
+  // the offsets. Here we calculate what the right form/size needs to be for
+  // the merged index.
+  uint8_t size;
+  dwarf::Form form;
+  // TODO: Investigate possibly using llvm::DIEInteger::BestForm here
+  if (mergedHdr.CompUnitCount > 0xffffffff) {
+    form = DW_FORM_data8;
+    size = 8;
+  } else if (mergedHdr.CompUnitCount > 0xffff) {
+    form = DW_FORM_data4;
+    size = 4;
+  } else if (mergedHdr.CompUnitCount > 0xff) {
+    form = DW_FORM_data2;
+    size = 2;
+  } else {
+    form = DW_FORM_data1;
+    size = 1;
+  }
+
+  return {size, form};
+}
+
+void DebugNamesSection::getMergedAbbrevTable(
+    MutableArrayRef<DebugNamesInputChunk> &inputChunks) {
+  MapVector<uint64_t, Abbrev> abbrevMap;
+  FoldingSet<DebugNamesSection::Abbrev> AbbreviationsSet;
+
+  // Need to determine what size form is needed for the DW_IDX_compile_unit
+  // attributes in the merged index. Will need to update the abbrevs to use
+  // the right form.
+  dwarf::Form compileUnitAttrForm = getMergedCuSizeData().second;
+
+  for (DebugNamesInputChunk &chunk : inputChunks) {
+    for (const auto &ni : *chunk.debugNamesData) {
+      const auto &abbrevs = ni.getAbbrevs();
+      for (const DWARFDebugNames::Abbrev &abbrev : abbrevs) {
+        // Create canonicalized abbrev.
+        DebugNamesSection::Abbrev newAbbrev;
+        DWARFDebugNames::AttributeEncoding cuOrTuAttr(DW_IDX_compile_unit,
+                                                      compileUnitAttrForm);
+        newAbbrev.code = abbrev.Code;
+        newAbbrev.tag = abbrev.Tag;
+        for (const auto attr : abbrev.Attributes) {
+          DWARFDebugNames::AttributeEncoding newAttr(attr.Index, attr.Form);
+          if (attr.Index == DW_IDX_compile_unit ||
+              attr.Index == DW_IDX_type_unit)
+            // Save it, to put it at the end.
+            cuOrTuAttr.Index = newAttr.Index;
+          else
+            newAbbrev.attributes.push_back(newAttr);
+        }
+        // Put the CU/TU index at the end of the attributes list.
+        newAbbrev.attributes.push_back(cuOrTuAttr);
+
+        // Next, check our abbreviations set to see if we've already seen the
+        // identical abbreviation.
+        uint32_t oldCode = newAbbrev.code;
+        uint32_t newCode;
+        FoldingSetNodeID id;
+        newAbbrev.Profile(id);
+        void *insertPos;
+        if (Abbrev *Existing =
+                AbbreviationsSet.FindNodeOrInsertPos(id, insertPos)) {
+          // Found it; we've already seen an identical abbreviation.
+          newCode = Existing->code;
+        } else {
+          // Didn't find it.
+          Abbrev *newAbbrev2 = new (Alloc) Abbrev(std::move(newAbbrev));
+          AbbreviationsSet.InsertNode(newAbbrev2, insertPos);
+          newCode = mergedAbbrevTable.size() + 1;
+          newAbbrev2->code = newCode;
+          mergedAbbrevTable.push_back(newAbbrev2);
+        }
+        chunk.abbrevCodeMap[oldCode] = newCode;
+      }
+    }
+  }
+
+  // Calculate the merged abbrev table size.
+  mergedHdr.AbbrevTableSize = 0;
+  for (Abbrev *a : mergedAbbrevTable) {
+    mergedHdr.AbbrevTableSize += getULEB128Size(a->code);
+    mergedHdr.AbbrevTableSize += getULEB128Size(a->tag);
+    for (const auto &attr : a->attributes) {
+      mergedHdr.AbbrevTableSize += getULEB128Size(attr.Index);
+      mergedHdr.AbbrevTableSize += getULEB128Size(attr.Form);
+    }
+    mergedHdr.AbbrevTableSize += 2; // attribute index & form sentinels
+  }
+  ++mergedHdr.AbbrevTableSize; // abbrev table sentinel
+}
+
+void DebugNamesSection::getMergedSymbols(
+    MutableArrayRef<DebugNamesInputChunk> &inputChunks) {
+  // The number of symbols (& abbrevs) we will handle is very large; will use
+  // multi-threading to speed it up.
+  constexpr size_t numShards = 32;
+  const size_t concurrency =
+      llvm::bit_floor(std::min<size_t>(config->threadCount, numShards));
+  const size_t shift = 32 - llvm::countr_zero(numShards);
+
+  struct ShardData {
+    // Map to uniquify symbols by name
+    MapVector<CachedHashStringRef, NamedEntry> nameMap;
+  };
+
+  // Need to determine what size is needed for the DW_IDX_compile_unit
+  // attributes in the merged index. Will need to update the indexEntries
+  // to use the right size.
+  uint8_t compileUnitAttrSize = getMergedCuSizeData().first;
+
+  auto shardsPtr = std::make_unique<ShardData[]>(numShards);
+  MutableArrayRef<ShardData> shards(shardsPtr.get(), numShards);
+
+  parallelFor(0, concurrency, [&](size_t threadId) {
+    for (size_t i = 0; i < numChunks; ++i) {
+      DebugNamesInputChunk &chunk = inputChunks[i];
+      for (auto &secData : chunk.sectionsData) {
+        // Deduplicate the NamedEntry records (based on the string/name),
+        // using a map from string/name to NamedEntry records.
+        // Note there is a twist: If there is already a record for the current
+        // 'string' in the nameMap, we append all the indexEntries from the
+        // current record to the record that's in the nameMap. I.e. we
+        // deduplicate the *strings* but we keep all the IndexEntry records
+        // (moving them to the appropriate 'kept' NamedEntry record).
+        for (auto &stringEntry : secData.namedEntries) {
+          size_t shardId = stringEntry.hashValue >> shift;
+          if ((shardId & (concurrency - 1)) != threadId)
+            continue;
+
+          auto &shard = shards[shardId];
+          stringEntry.chunkIdx = i;
+          for (auto &entry : stringEntry.indexEntries) {
+            // The DW_IDX_compile_unit is always the last attribute (we set it
+            // up that way when we read/created the attributes). We need to
+            // update the index value to use the correct merged offset, and we
+            // need to fix the size of the index attribute.
+            uint8_t endPos = entry->attrValues.size() - 1;
+            entry->attrValues[endPos].attrValue += chunk.baseCuOffsetIdx;
+            entry->attrValues[endPos].attrSize = compileUnitAttrSize;
+            // Update the entry's abbrev code to match the merged
+            // abbreviations.
+            entry->abbrevCode = chunk.abbrevCodeMap[entry->abbrevCode];
+          }
+
+          CachedHashStringRef cachedHashName(stringEntry.name);
+          auto [it, inserted] =
+              shard.nameMap.try_emplace(cachedHashName, std::move(stringEntry));
+          if (!inserted) {
+            // Found the string already; don't add to map, but append
+            // entry/entries for it to existing map entry.
+            NamedEntry &found = it->second;
+            // Append the entries...
+            for (auto &entry : stringEntry.indexEntries)
+              found.indexEntries.push_back(std::move(entry));
+          }
+        }
+      }
+    }
+  });
+
+  // Combined the shared symbols into mergedEntries
+  for (auto &shard : shards)
+    for (auto &mapEntry : shard.nameMap)
+      mergedEntries.push_back(std::move(mapEntry.second));
+  mergedHdr.NameCount = mergedEntries.size();
+}
+
+void DebugNamesSection::computeUniqueHashes(
+    MutableArrayRef<DebugNamesInputChunk> &chunks) {
+  SmallVector<uint32_t, 0> uniques;
+  for (const auto &chunk : chunks)
+    uniques.append(chunk.hashValues);
+  mergedHdr.BucketCount = dwarf::getDebugNamesBucketAndHashCount(uniques).first;
+}
+
+void DebugNamesSection::generateBuckets() {
+  bucketList.resize(mergedHdr.BucketCount);
+  for (auto &entry : mergedEntries) {
+    uint32_t bucketIdx = entry.hashValue % mergedHdr.BucketCount;
+    bucketList[bucketIdx].push_back(&entry);
+  }
+
+  // Sort the contents of the buckets by hash value so that the hash collisions
+  // end up together.
+  for (auto &bucket : bucketList)
+    llvm::stable_sort(bucket, [](NamedEntry *lhs, NamedEntry *rhs) {
+      return lhs->hashValue < rhs->hashValue;
+    });
+}
+
+void DebugNamesSection::calculateEntriesSizeAndOffsets() {
+  uint32_t offset = 0;
+  for (DebugNamesSection::NamedEntry &stringEntry : mergedEntries) {
+    stringEntry.entryOffset = offset;
+    for (auto &entry : stringEntry.indexEntries) {
+      uint32_t entrySize = 0;
+      entry->poolOffset = offset;
+      uint32_t ulebSize = getULEB128Size(entry->abbrevCode);
+      entrySize += ulebSize;
+      for (const auto &attr : entry->attrValues)
+        entrySize += attr.attrSize;
+      offset += entrySize;
+    }
+    // Add in sentinel size
+    ++offset;
+  }
+  mergedTotalEntriesSize = offset;
+}
+
+void DebugNamesSection::updateParentIndexEntries() {
+  for (DebugNamesSection::NamedEntry &stringEntry : mergedEntries) {
+    for (auto &childEntry : stringEntry.indexEntries) {
+      if (!childEntry->parentEntry)
+        continue;
+
+      // Abbrevs are indexed starting at 1; vector starts at 0. (abbrevCode
+      // corresponds to position in the merged table vector).
+      const Abbrev *abbrev = mergedAbbrevTable[childEntry->abbrevCode - 1];
+
+      // Found the abbrev. Find the index for the DW_IDX_parent attribute
+      // (in the abbrev) and update that value in the entry with the
+      // correct parent offset (in the merged entry pool).
+      for (size_t idx = 0, size = abbrev->attributes.size(); idx < size;
+           ++idx) {
+        auto attr = abbrev->attributes[idx];
+        if (attr.Index == DW_IDX_parent && attr.Form == DW_FORM_ref4)
+          childEntry->attrValues[idx].attrValue =
+              childEntry->parentEntry->poolOffset;
+      }
+    }
+  }
+}
+
+uint64_t DebugNamesSection::calculateMergedSectionSize() {
+  uint32_t hdrSize = computeDebugNamesHeaderSize();
+  findDebugNamesOffsets(mergedOffsets, hdrSize, mergedHdr.Format, mergedHdr);
+  // Add in the size for all the Entries, and make it 4-byte aligned.
+  mergedHdr.UnitLength =
+      alignTo(mergedOffsets.EntriesBase + mergedTotalEntriesSize, 4);
+  // Add in the first 4 bytes, whichs print out the length of the section.
+  return mergedHdr.UnitLength + 4;
+}
+
+template <class ELFT> DebugNamesSection *DebugNamesSection::create() {
+  llvm::TimeTraceScope timeScope("Create merged .debug_names");
+  SetVector<InputFile *> files;
+  SmallVector<InputSectionBase *, 0> sections;
+  for (InputSectionBase *s : ctx.inputSections) {
+    InputSection *isec = dyn_cast<InputSection>(s);
+    if (!isec)
+      continue;
+    // Mark original sections as dead, but save links to them for calculating
+    // relocations later.
+    if (s->name == ".debug_names") {
+      s->markDead();
+      sections.push_back(s);
+      files.insert(isec->file);
+    }
+  }
+  auto inputChunksPtr = std::make_unique<DebugNamesInputChunk[]>(files.size());
+  MutableArrayRef<DebugNamesInputChunk> inputChunks(inputChunksPtr.get(),
+                                                    files.size());
+  auto outputChunks = std::make_unique<DebugNamesOutputChunk[]>(files.size());
+  parallelFor(0, files.size(), [&](size_t i) {
+    ObjFile<ELFT> *file = cast<ObjFile<ELFT>>(files[i]);
+    auto dwarfCtx = std::make_unique<DWARFContext>(
+        std::make_unique<LLDDwarfObj<ELFT>>(file));
+    auto &dobj =
+        static_cast<const LLDDwarfObj<ELFT> &>(dwarfCtx->getDWARFObj());
+
+    // Extract llvm::DWARFDebugNames data from the .debug_names section. The
+    // .debug_names section needs the .debug_str section, to get the actual
+    // symbol names.
+    const StringRef &strSection = dobj.getStrSection();
+    const LLDDWARFSection &namesSection = dobj.getNamesSection();
+    llvm::DWARFDataExtractor namesExtractor(dobj, namesSection, config->isLE,
+                                            config->wordsize);
+    llvm::DataExtractor strExtractor(strSection, config->isLE,
+                                     config->wordsize);
+    inputChunks[i].debugNamesData =
+        std::make_unique<DWARFDebugNames>(namesExtractor, strExtractor);
+    inputChunks[i].namesSection =
+        std::make_unique<LLDDWARFSection>(namesSection);
+    if (llvm::Error E = inputChunks[i].debugNamesData->extract()) {
+      // Report an error here. We were unable to extract the data.
+      errorOrWarn(toString(dobj.getNamesSection().sec) + ": " +
+                  toString(std::move(E)));
+    }
+    outputChunks[i].sec = dobj.getInfoSection();
+    collectDebugNamesSectionData<ELFT>(inputChunks[i], outputChunks[i],
+                                       namesExtractor, strExtractor);
+  });
+
+  auto *ret = make<DebugNamesSection>();
+  ret->addSections(sections);
+  ret->outputChunks = std::move(outputChunks);
+  ret->numChunks = files.size();
+  ret->collectMergedCounts(inputChunks);
+  ret->getMergedAbbrevTable(inputChunks);
+  ret->getMergedSymbols(inputChunks);
+  ret->computeUniqueHashes(inputChunks);
+  inputChunksPtr.reset();
+  ret->generateBuckets();
+  ret->calculateEntriesSizeAndOffsets();
+  ret->updateParentIndexEntries();
+  ret->sectionSize = ret->calculateMergedSectionSize();
+  return ret;
+}
+
 GdbIndexSection::GdbIndexSection()
     : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index") {}
 
@@ -3965,6 +4721,11 @@ InStruct elf::in;
 std::vector<Partition> elf::partitions;
 Partition *elf::mainPart;
 
+template DebugNamesSection *DebugNamesSection::create<ELF32LE>();
+template DebugNamesSection *DebugNamesSection::create<ELF32BE>();
+template DebugNamesSection *DebugNamesSection::create<ELF64LE>();
+template DebugNamesSection *DebugNamesSection::create<ELF64BE>();
+
 template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
 template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
 template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index fa21b80a5a5ea0..df13e6c0baca2a 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -21,12 +21,16 @@
 #define LLD_ELF_SYNTHETIC_SECTIONS_H
 
 #include "Config.h"
+#include "DWARF.h"
 #include "InputSection.h"
 #include "Symbols.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
 #include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Allocator.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/Parallel.h"
@@ -788,6 +792,136 @@ class RelroPaddingSection final : public SyntheticSection {
   void writeTo(uint8_t *buf) override {}
 };
 
+class DebugNamesSection final : public SyntheticSection {
+  // N.B. Everything in this class assumes that we are using DWARF32.
+  // If we move to DWARF64, most of this data will need to be re-sized,
+  // and the code that handles or manipulates it will need to be updated
+  // accordingly.
+
+public:
+  DebugNamesSection();
+  template <typename ELFT> static DebugNamesSection *create();
+  void writeTo(uint8_t *buf) override;
+  size_t getSize() const override { return sectionSize; }
+  bool isNeeded() const override;
+
+  void addSections(SmallVector<InputSectionBase *, 0> sec_list) {
+    inputDebugNamesSections = sec_list;
+  }
+
+  template <class ELFT> void writeToImpl(uint8_t *buf);
+
+  template <class ELFT, class RelTy>
+  void getNameRelocsImpl(InputSection *sec, ArrayRef<RelTy> rels,
+                         llvm::DenseMap<uint32_t, uint32_t> &relocs);
+
+  template <class ELFT>
+  void getNameRelocs(InputSectionBase *base,
+                     llvm::DenseMap<uint32_t, uint32_t> &relocs);
+
+  template <class ELFT>
+  void endianWrite(uint8_t size, uint8_t *buf_start, uint32_t offset,
+                   uint32_t data);
+
+  struct Abbrev : public llvm::FoldingSetNode {
+    uint32_t code;
+    uint32_t tag;
+    SmallVector<llvm::DWARFDebugNames::AttributeEncoding, 2> attributes;
+
+    void Profile(llvm::FoldingSetNodeID &id) const;
+  };
+
+  struct AttrValueData {
+    uint32_t attrValue;
+    uint8_t attrSize;
+  };
+
+  struct IndexEntry {
+    uint32_t abbrevCode;
+    uint32_t poolOffset;
+    union {
+      int32_t parentOffset = -1;
+      IndexEntry *parentEntry;
+    };
+    SmallVector<AttrValueData, 3> attrValues;
+  };
+
+  struct NamedEntry {
+    const char *name;
+    uint32_t hashValue;
+    uint32_t stringOffsetOffset;
+    uint32_t entryOffset;
+    uint32_t relocatedEntryOffset;
+    // The index of the chunk that 'name' points into, for looking up
+    // relocation data for this string.
+    uint32_t chunkIdx;
+    SmallVector<std::unique_ptr<IndexEntry>, 0> indexEntries;
+  };
+
+  struct SectionOffsetLocs {
+    uint64_t stringOffsetsBase;
+    uint64_t entryOffsetsBase;
+    uint64_t entriesBase;
+  };
+
+  struct DebugNamesSectionData {
+    llvm::DWARFDebugNames::Header hdr;
+    llvm::DWARFDebugNames::DWARFDebugNamesOffsets locs;
+    SmallVector<uint32_t, 0> tuOffsets;
+    SmallVector<Abbrev, 0> abbrevTable;
+    std::unique_ptr<uint32_t[]> entryOffsets;
+    SmallVector<NamedEntry, 0> namedEntries;
+    uint16_t dwarfSize;
+    uint16_t hdrSize;
+  };
+
+  // Per-file data used, while reading in the data, to generate the merged
+  // section information.
+  struct DebugNamesInputChunk {
+    uint32_t baseCuOffsetIdx;
+    std::unique_ptr<llvm::DWARFDebugNames> debugNamesData;
+    std::unique_ptr<LLDDWARFSection> namesSection;
+    SmallVector<DebugNamesSectionData, 0> sectionsData;
+    SmallVector<uint32_t, 0> hashValues;
+    llvm::DenseMap<uint32_t, uint32_t> abbrevCodeMap;
+  };
+
+  // Per-file data needed for correctly writing out the .debug_names section.
+  struct DebugNamesOutputChunk {
+    // Pointer to .debug_info section for this chunk/file, used for
+    // calculating correct relocated CU offsets in the merged index.
+    InputSection *sec;
+    SmallVector<uint32_t, 0> compilationUnits;
+    SmallVector<uint32_t, 0> typeUnits;
+  };
+
+  void collectMergedCounts(MutableArrayRef<DebugNamesInputChunk> &inputChunks);
+  std::pair<uint8_t, llvm::dwarf::Form> getMergedCuSizeData();
+  void getMergedAbbrevTable(MutableArrayRef<DebugNamesInputChunk> &inputChunks);
+  void getMergedSymbols(MutableArrayRef<DebugNamesInputChunk> &inputChunks);
+  void computeUniqueHashes(MutableArrayRef<DebugNamesInputChunk> &inputChunks);
+  void generateBuckets();
+  void calculateEntriesSizeAndOffsets();
+  void updateParentIndexEntries();
+  uint64_t calculateMergedSectionSize();
+
+  llvm::BumpPtrAllocator Alloc;
+
+private:
+  size_t sectionSize;
+  uint32_t mergedTotalEntriesSize;
+  uint32_t numChunks;
+  llvm::DWARFDebugNames::DWARFDebugNamesOffsets mergedOffsets;
+  std::unique_ptr<DebugNamesOutputChunk[]> outputChunks;
+  // Pointers to the original .debug_names sections; used for find the correct'
+  // string relocation values when writing out the merged index.
+  SmallVector<InputSectionBase *, 0> inputDebugNamesSections;
+  llvm::DWARFDebugNames::Header mergedHdr;
+  SmallVector<Abbrev *, 0> mergedAbbrevTable;
+  SmallVector<NamedEntry, 0> mergedEntries;
+  SmallVector<SmallVector<NamedEntry *, 0>, 0> bucketList;
+};
+
 class GdbIndexSection final : public SyntheticSection {
 public:
   struct AddressEntry {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 40d617b7fdf324..95267523ef5966 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -543,6 +543,9 @@ template <class ELFT> void elf::createSyntheticSections() {
   if (config->gdbIndex)
     add(*GdbIndexSection::create<ELFT>());
 
+  if (config->debugNames)
+    add(*DebugNamesSection::create<ELFT>());
+
   // We always need to add rel[a].plt to output if it has entries.
   // Even for static linking it can contain R_[*]_IRELATIVE relocations.
   in.relaPlt = std::make_unique<RelocationSection<ELFT>>(
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 65e50e349c8cc0..ba091a9844fe10 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -172,6 +172,10 @@ This is like a generalized
 Output cross reference table. If
 .Fl Map
 is specified, print to the map file.
+.It Fl -debug-names
+Generate a merged
+.Li .debug_names
+section.
 .It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar expression
 Define a symbol alias.
 .Ar expression
diff --git a/lld/test/ELF/Inputs/debug-names-2.s b/lld/test/ELF/Inputs/debug-names-2.s
new file mode 100644
index 00000000000000..586788653dcc72
--- /dev/null
+++ b/lld/test/ELF/Inputs/debug-names-2.s
@@ -0,0 +1,191 @@
+#-- input file: debug-names-2.cpp
+## Generated with:
+##
+## - clang++ -g -O0 -gpubnames -fdebug-compilation-dir='debug-names-test' \
+##     -S debug-names-2.cpp -o debug-names-2.s
+##
+## debug-names-2.cpp contents:
+##
+## struct t1 { };
+## int main() {
+##   t1 v1;
+## }
+##
+	.text
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	xorl	%eax, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	main, .Lfunc_end0-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x3d DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x1b DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.long	62                              # DW_AT_type
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x32:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	127
+	.byte	5                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	3                               # DW_AT_decl_line
+	.long	66                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x3e:0x4 DW_TAG_base_type
+	.byte	4                               # DW_AT_name
+	.byte	5                               # DW_AT_encoding
+	.byte	4                               # DW_AT_byte_size
+	.byte	5                               # Abbrev [5] 0x42:0x6 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	6                               # DW_AT_name
+	.byte	1                               # DW_AT_byte_size
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	32                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names-2.cpp"             # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=122
+.Linfo_string3:
+	.asciz	"main"                          # string offset=139
+.Linfo_string4:
+	.asciz	"int"                           # string offset=144
+.Linfo_string5:
+	.asciz	"v1"                            # string offset=148
+.Linfo_string6:
+	.asciz	"t1"                            # string offset=151
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5863786                         # Hash in Bucket 1
+	.long	2090499946                      # Hash in Bucket 1
+	.long	193495088                       # Hash in Bucket 2
+	.long	.Linfo_string6                  # String in Bucket 1: t1
+	.long	.Linfo_string3                  # String in Bucket 1: main
+	.long	.Linfo_string4                  # String in Bucket 2: int
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	3                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames2:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	66                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: t1
+.Lnames0:
+.L2:
+	.byte	2                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: main
+.Lnames1:
+.L0:
+	.byte	3                               # Abbreviation code
+	.long	62                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/Inputs/debug-names-parent-idx-2.s b/lld/test/ELF/Inputs/debug-names-parent-idx-2.s
new file mode 100644
index 00000000000000..7eedaaee549450
--- /dev/null
+++ b/lld/test/ELF/Inputs/debug-names-parent-idx-2.s
@@ -0,0 +1,347 @@
+#-- input file: debug-names-parent-idx-2.cpp
+## Generated with:
+##
+## - clang++ -g -O0 -gpubnames -fdebug-compilation-dir='parent-idx-test' \
+##     -S debug-names-parent-idx-2.cpp -o debug-names-parent-idx-2.s
+##
+## foo.h contents:
+##
+## int foo();
+##
+## struct foo {
+##   int x;
+##   char y;
+##   struct foo *foo_ptr;
+## };
+##
+## namespace parent_test {
+##   int foo();
+## }
+##
+## debug-names-parent-index-2.cpp contents:
+##
+## #include "foo.h"
+## int foo () {
+##   struct foo struct2;
+##   struct2.x = 1024;
+##   struct2.y = 'r';
+##   struct2.foo_ptr = nullptr;
+##   return struct2.x * (int) struct2.y;
+## }
+##
+## namespace parent_test {
+## int foo () {
+##   return 25;
+## }
+## }
+##
+	.text
+	.globl	_Z3foov                         # -- Begin function _Z3foov
+	.p2align	4, 0x90
+	.type	_Z3foov, at function
+_Z3foov:                                # @_Z3foov
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	movl	$1024, -16(%rbp)                # imm = 0x400
+	movb	$114, -12(%rbp)
+	movq	$0, -8(%rbp)
+	movl	-16(%rbp), %eax
+	movsbl	-12(%rbp), %ecx
+	imull	%ecx, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z3foov, .Lfunc_end0-_Z3foov
+	.cfi_endproc
+                                        # -- End function
+	.globl	_ZN11parent_test3fooEv          # -- Begin function _ZN11parent_test3fooEv
+	.p2align	4, 0x90
+	.type	_ZN11parent_test3fooEv, at function
+_ZN11parent_test3fooEv:                 # @_ZN11parent_test3fooEv
+.Lfunc_begin1:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp2:
+	movl	$25, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp3:
+.Lfunc_end1:
+	.size	_ZN11parent_test3fooEv, .Lfunc_end1-_ZN11parent_test3fooEv
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x76 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x4 DW_TAG_base_type
+	.byte	3                               # DW_AT_name
+	.byte	5                               # DW_AT_encoding
+	.byte	4                               # DW_AT_byte_size
+	.byte	3                               # Abbrev [3] 0x27:0x1c DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	5                               # DW_AT_linkage_name
+	.byte	6                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	4                               # DW_AT_decl_line
+	.long	35                              # DW_AT_type
+                                        # DW_AT_external
+	.byte	4                               # Abbrev [4] 0x37:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	112
+	.byte	8                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	5                               # DW_AT_decl_line
+	.long	86                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	5                               # Abbrev [5] 0x43:0x13 DW_TAG_namespace
+	.byte	4                               # DW_AT_name
+	.byte	6                               # Abbrev [6] 0x45:0x10 DW_TAG_subprogram
+	.byte	1                               # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin1       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	7                               # DW_AT_linkage_name
+	.byte	6                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	16                              # DW_AT_decl_line
+	.long	35                              # DW_AT_type
+                                        # DW_AT_external
+	.byte	0                               # End Of Children Mark
+	.byte	7                               # Abbrev [7] 0x56:0x22 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	6                               # DW_AT_name
+	.byte	16                              # DW_AT_byte_size
+	.byte	1                               # DW_AT_decl_file
+	.byte	4                               # DW_AT_decl_line
+	.byte	8                               # Abbrev [8] 0x5c:0x9 DW_TAG_member
+	.byte	9                               # DW_AT_name
+	.long	35                              # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	5                               # DW_AT_decl_line
+	.byte	0                               # DW_AT_data_member_location
+	.byte	8                               # Abbrev [8] 0x65:0x9 DW_TAG_member
+	.byte	10                              # DW_AT_name
+	.long	120                             # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	6                               # DW_AT_decl_line
+	.byte	4                               # DW_AT_data_member_location
+	.byte	8                               # Abbrev [8] 0x6e:0x9 DW_TAG_member
+	.byte	12                              # DW_AT_name
+	.long	124                             # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.byte	8                               # DW_AT_data_member_location
+	.byte	0                               # End Of Children Mark
+	.byte	2                               # Abbrev [2] 0x78:0x4 DW_TAG_base_type
+	.byte	11                              # DW_AT_name
+	.byte	6                               # DW_AT_encoding
+	.byte	1                               # DW_AT_byte_size
+	.byte	9                               # Abbrev [9] 0x7c:0x5 DW_TAG_pointer_type
+	.long	86                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	56                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names-parent-idx-2.cpp"  # string offset=104
+.Linfo_string2:
+	.asciz	"parent-idx-test"               # string offset=133
+.Linfo_string3:
+	.asciz	"int"                           # string offset=149
+.Linfo_string4:
+	.asciz	"foo"                           # string offset=153
+.Linfo_string5:
+	.asciz	"_Z3foov"                       # string offset=157
+.Linfo_string6:
+	.asciz	"parent_test"                   # string offset=165
+.Linfo_string7:
+	.asciz	"_ZN11parent_test3fooEv"        # string offset=177
+.Linfo_string8:
+	.asciz	"struct2"                       # string offset=200
+.Linfo_string9:
+	.asciz	"x"                             # string offset=208
+.Linfo_string10:
+	.asciz	"y"                             # string offset=210
+.Linfo_string11:
+	.asciz	"char"                          # string offset=212
+.Linfo_string12:
+	.asciz	"foo_ptr"                       # string offset=217
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+	.quad	.Lfunc_begin1
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	6                               # Header: bucket count
+	.long	6                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5                               # Bucket 3
+	.long	0                               # Bucket 4
+	.long	6                               # Bucket 5
+	.long	-1451972055                     # Hash in Bucket 1
+	.long	-1257882357                     # Hash in Bucket 1
+	.long	175265198                       # Hash in Bucket 2
+	.long	193495088                       # Hash in Bucket 2
+	.long	193491849                       # Hash in Bucket 3
+	.long	2090147939                      # Hash in Bucket 5
+	.long	.Linfo_string7                  # String in Bucket 1: _ZN11parent_test3fooEv
+	.long	.Linfo_string5                  # String in Bucket 1: _Z3foov
+	.long	.Linfo_string6                  # String in Bucket 2: parent_test
+	.long	.Linfo_string3                  # String in Bucket 2: int
+	.long	.Linfo_string4                  # String in Bucket 3: foo
+	.long	.Linfo_string11                 # String in Bucket 5: char
+	.long	.Lnames4-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames3-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 3
+	.long	.Lnames5-.Lnames_entries0       # Offset in Bucket 5
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	19                              # DW_FORM_ref4
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	3                               # Abbrev code
+	.byte	57                              # DW_TAG_namespace
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	4                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	5                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames4:
+.L3:
+	.byte	1                               # Abbreviation code
+	.long	69                              # DW_IDX_die_offset
+	.long	.L5-.Lnames_entries0            # DW_IDX_parent
+	.byte	0                               # End of list: _ZN11parent_test3fooEv
+.Lnames2:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	39                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z3foov
+.Lnames3:
+.L5:
+	.byte	3                               # Abbreviation code
+	.long	67                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: parent_test
+.Lnames0:
+.L2:
+	.byte	4                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+.Lnames1:
+	.byte	2                               # Abbreviation code
+	.long	39                              # DW_IDX_die_offset
+	.byte	1                               # DW_IDX_parent
+                                        # Abbreviation code
+	.long	69                              # DW_IDX_die_offset
+	.long	.L5-.Lnames_entries0            # DW_IDX_parent
+.L4:
+	.byte	5                               # Abbreviation code
+	.long	86                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: foo
+.Lnames5:
+.L1:
+	.byte	4                               # Abbreviation code
+	.long	120                             # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: char
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-bad-aug-string.s b/lld/test/ELF/debug-names-bad-aug-string.s
new file mode 100644
index 00000000000000..edf79f0fde5773
--- /dev/null
+++ b/lld/test/ELF/debug-names-bad-aug-string.s
@@ -0,0 +1,189 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/debug-names-2.s -o %t2
+# RUN: ld.lld --debug-names %t1.o %t2.o -o %t
+
+# RUN: llvm-dwarfdump -debug-names %t | FileCheck %s --check-prefix=DWARF
+
+# DWARF:      .debug_names contents:
+# DWARF:      Name Index @ 0x0 {
+# DWARF-NEXT:   Header {
+# DWARF-NEXT:     Length: 0xCC
+# DWARF-NEXT:     Format: DWARF32
+# DWARF-NEXT:     Version: 5
+# DWARF-NEXT:     CU count: 2
+# DWARF-NEXT:     Local TU count: 0
+# DWARF-NEXT:     Foreign TU count: 0
+# DWARF-NEXT:     Bucket count: 5
+# DWARF-NEXT:     Name count: 5
+# DWARF-NEXT:     Abbreviations table size: 0x1F
+# DWARF-NEXT:     Augmentation: '        '
+# DWARF:        Compilation Unit offsets [
+# DWARF-NEXT:     CU[0]: 0x00000000
+# DWARF-NEXT:     CU[1]: 0x00000041
+
+##
+## This file was generated by copying debug-names.s and manually
+## editing the 'Header: augmentation string' in the .debug_names section.
+##
+	.text
+	.globl	_Z2f12t1                        # -- Begin function _Z2f12t1
+	.p2align	4, 0x90
+	.type	_Z2f12t1, at function
+_Z2f12t1:                               # @_Z2f12t1
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z2f12t1, .Lfunc_end0-_Z2f12t1
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x35 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x17 DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_linkage_name
+	.byte	4                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x2f:0xa DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	127
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.long	58                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x3a:0x6 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	5                               # DW_AT_name
+	.byte	1                               # DW_AT_byte_size
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	28                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names.cpp"               # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=120
+.Linfo_string3:
+	.asciz	"f1"                            # string offset=137
+.Linfo_string4:
+	.asciz	"_Z2f12t1"                      # string offset=140
+.Linfo_string5:
+	.asciz	"t1"                            # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"JUNK1234"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5863324                         # Hash in Bucket 1
+	.long	5863786                         # Hash in Bucket 1
+	.long	1398125246                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: f1
+	.long	.Linfo_string5                  # String in Bucket 1: t1
+	.long	.Linfo_string4                  # String in Bucket 2: _Z2f12t1
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: f1
+.Lnames2:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	58                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: t1
+.Lnames1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z2f12t1
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-bad-die-idx-sizes.s b/lld/test/ELF/debug-names-bad-die-idx-sizes.s
new file mode 100644
index 00000000000000..2da278a8dd5b7c
--- /dev/null
+++ b/lld/test/ELF/debug-names-bad-die-idx-sizes.s
@@ -0,0 +1,151 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+#
+# RUN: not ld.lld --debug-names %t1.o -o /dev/null 2>&1 | FileCheck %s
+#
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+
+## This file was generated by first compiling main.cpp: 
+## clang++ -g -O0 -S -gpubnames -fdebug-compilation-dir='debug-names-test' \
+##      main.cpp
+##
+## Then manually edit the .debug_names section. In the entries, change the
+## size of the DW_IDX_die_offset from '.long' to '.byte'.
+##
+## main.cpp:
+## int main (int argc, char **argv) { }
+##
+	.text
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movq	%rsi, -16(%rbp)
+.Ltmp0:
+	xorl	%eax, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	main, .Lfunc_end0-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	36                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)" # string offset=0
+.Linfo_string1:
+	.asciz	"main.cpp"                      # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=113
+.Linfo_string3:
+	.asciz	"main"                          # string offset=130
+.Linfo_string4:
+	.asciz	"int"                           # string offset=135
+.Linfo_string5:
+	.asciz	"argc"                          # string offset=139
+.Linfo_string6:
+	.asciz	"argv"                          # string offset=144
+.Linfo_string7:
+	.asciz	"char"                          # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	2                               # Bucket 2
+	.long	2090499946                      # Hash in Bucket 1
+	.long	193495088                       # Hash in Bucket 2
+	.long	2090147939                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: main
+	.long	.Linfo_string4                  # String in Bucket 2: int
+	.long	.Linfo_string7                  # String in Bucket 2: char
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.byte	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: main
+.Lnames1:
+.L0:
+	.byte	2                               # Abbreviation code
+	.byte	73                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+.Lnames2:
+.L2:
+	.byte	2                               # Abbreviation code
+	.byte	87                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: char
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-bad-name-count.s b/lld/test/ELF/debug-names-bad-name-count.s
new file mode 100644
index 00000000000000..4acfa182b11a91
--- /dev/null
+++ b/lld/test/ELF/debug-names-bad-name-count.s
@@ -0,0 +1,154 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+#
+# RUN: not ld.lld --debug-names %t1.o -o /dev/null 2>&1 | FileCheck %s
+#
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+
+## This file was generated by first compiling main.cpp: 
+## clang++ -g -O0 -S -gpubnames -fdebug-compilation-dir='debug-names-test' \
+##      main.cpp
+##
+## Then manually edit .debug_names section: change value for
+## 'Header: name count' from 3 to 4.
+##
+## main.cpp:
+## int main (int argc, char **argv) { }
+##
+	.text
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movq	%rsi, -16(%rbp)
+.Ltmp0:
+	xorl	%eax, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	main, .Lfunc_end0-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	36                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)" # string offset=0
+.Linfo_string1:
+	.asciz	"main.cpp"                      # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=113
+.Linfo_string3:
+	.asciz	"main"                          # string offset=130
+.Linfo_string4:
+	.asciz	"int"                           # string offset=135
+.Linfo_string5:
+	.asciz	"argc"                          # string offset=139
+.Linfo_string6:
+	.asciz	"argv"                          # string offset=144
+.Linfo_string7:
+	.asciz	"char"                          # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	4                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	2                               # Bucket 2
+	.long	2090499946                      # Hash in Bucket 1
+	.long	193495088                       # Hash in Bucket 2
+	.long	2090147939                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: main
+	.long	.Linfo_string4                  # String in Bucket 2: int
+	.long	.Linfo_string7                  # String in Bucket 2: char
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: main
+.Lnames1:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	73                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+.Lnames2:
+.L2:
+	.byte	2                               # Abbreviation code
+	.long	87                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: char
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-bad-offsets-sizes.s b/lld/test/ELF/debug-names-bad-offsets-sizes.s
new file mode 100644
index 00000000000000..e11d19a9bf579c
--- /dev/null
+++ b/lld/test/ELF/debug-names-bad-offsets-sizes.s
@@ -0,0 +1,153 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+#
+# RUN: not ld.lld --debug-names %t1.o -o /dev/null 2>&1 | FileCheck %s
+#
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+# CHECK: error: {{.*}}:(.debug_names): encountered unexpected end of section while reading entry
+
+## This file was generated by first compiling main.cpp: 
+## clang++ -g -O0 -S -gpubnames -fdebug-compilation-dir='debug-names-test' \
+##      main.cpp
+##
+## Then manually edit .debug_names section. Change the sizes of
+## 'Offset in Bucket' values from '.long' to '.byte'.
+##
+## main.cpp:
+## int main (int argc, char **argv) { }
+##
+	.text
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	movq	%rsi, -16(%rbp)
+.Ltmp0:
+	xorl	%eax, %eax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	main, .Lfunc_end0-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	36                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)" # string offset=0
+.Linfo_string1:
+	.asciz	"main.cpp"                      # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=113
+.Linfo_string3:
+	.asciz	"main"                          # string offset=130
+.Linfo_string4:
+	.asciz	"int"                           # string offset=135
+.Linfo_string5:
+	.asciz	"argc"                          # string offset=139
+.Linfo_string6:
+	.asciz	"argv"                          # string offset=144
+.Linfo_string7:
+	.asciz	"char"                          # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	2                               # Bucket 2
+	.long	2090499946                      # Hash in Bucket 1
+	.long	193495088                       # Hash in Bucket 2
+	.long	2090147939                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: main
+	.long	.Linfo_string4                  # String in Bucket 2: int
+	.long	.Linfo_string7                  # String in Bucket 2: char
+	.byte	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.byte	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+	.byte	.Lnames2-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: main
+.Lnames1:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	73                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+.Lnames2:
+.L2:
+	.byte	2                               # Abbreviation code
+	.long	87                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: char
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git d33d5630b281debe6eabd67e323bcf767340fb6a)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-bad-version.s b/lld/test/ELF/debug-names-bad-version.s
new file mode 100644
index 00000000000000..f3914670af94a5
--- /dev/null
+++ b/lld/test/ELF/debug-names-bad-version.s
@@ -0,0 +1,173 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/debug-names-2.s -o %t2
+#
+# RUN: not ld.lld --debug-names %t1.o %t2.o -o /dev/null 2>&1 | FileCheck %s
+#
+# CHECK: error: {{.*}}:(.debug_names): unsupported version
+##
+## This file was generated by copying debug-names.s and manually
+## editing the 'Header: version' in the .debug_names section (changed it from
+## 5 to 4).
+	
+	.text
+	.globl	_Z2f12t1                        # -- Begin function _Z2f12t1
+	.p2align	4, 0x90
+	.type	_Z2f12t1, at function
+_Z2f12t1:                               # @_Z2f12t1
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z2f12t1, .Lfunc_end0-_Z2f12t1
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x35 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x17 DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_linkage_name
+	.byte	4                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x2f:0xa DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	127
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.long	58                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x3a:0x6 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	5                               # DW_AT_name
+	.byte	1                               # DW_AT_byte_size
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	28                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names.cpp"               # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=120
+.Linfo_string3:
+	.asciz	"f1"                            # string offset=137
+.Linfo_string4:
+	.asciz	"_Z2f12t1"                      # string offset=140
+.Linfo_string5:
+	.asciz	"t1"                            # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	4                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5863324                         # Hash in Bucket 1
+	.long	5863786                         # Hash in Bucket 1
+	.long	1398125246                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: f1
+	.long	.Linfo_string5                  # String in Bucket 1: t1
+	.long	.Linfo_string4                  # String in Bucket 2: _Z2f12t1
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: f1
+.Lnames2:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	58                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: t1
+.Lnames1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z2f12t1
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-invalid-attribute.s b/lld/test/ELF/debug-names-invalid-attribute.s
new file mode 100644
index 00000000000000..9b1a915d81f882
--- /dev/null
+++ b/lld/test/ELF/debug-names-invalid-attribute.s
@@ -0,0 +1,179 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+#
+# RUN: not ld.lld --debug-names %t1.o  -o /dev/null 2>&1 | FileCheck %s
+#
+# CHECK: error: {{.*}}:(.debug_names): unrecognized form encoding 16 in .debug_names abbrev table
+##
+## Generated by copying debug-names.s and manually editing it to make some
+## of the abbrev attributes invalid.
+	
+	.text
+	.globl	_Z2f12t1                        # -- Begin function _Z2f12t1
+	.p2align	4, 0x90
+	.type	_Z2f12t1, at function
+_Z2f12t1:                               # @_Z2f12t1
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z2f12t1, .Lfunc_end0-_Z2f12t1
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x35 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x17 DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_linkage_name
+	.byte	4                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x2f:0xa DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	127
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.long	58                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x3a:0x6 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	5                               # DW_AT_name
+	.byte	1                               # DW_AT_byte_size
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	28                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names.cpp"               # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=120
+.Linfo_string3:
+	.asciz	"f1"                            # string offset=137
+.Linfo_string4:
+	.asciz	"_Z2f12t1"                      # string offset=140
+.Linfo_string5:
+	.asciz	"t1"                            # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5863324                         # Hash in Bucket 1
+	.long	5863786                         # Hash in Bucket 1
+	.long	1398125246                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: f1
+	.long	.Linfo_string5                  # String in Bucket 1: t1
+	.long	.Linfo_string4                  # String in Bucket 2: _Z2f12t1
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	3                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	16                              # DW_FORM_ref_addr
+	.byte	7                               # DW_IDX_unknown
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: f1
+.Lnames2:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	58                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: t1
+.Lnames1:
+	.byte	3                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z2f12t1
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names-parent-idx.s b/lld/test/ELF/debug-names-parent-idx.s
new file mode 100644
index 00000000000000..a4e8682cd9ee58
--- /dev/null
+++ b/lld/test/ELF/debug-names-parent-idx.s
@@ -0,0 +1,549 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/debug-names-parent-idx-2.s -o %t2.o
+# RUN: ld.lld --debug-names %t1.o %t2.o -o %t
+
+# RUN: llvm-dwarfdump -debug-names %t | FileCheck %s --check-prefix=DWARF
+
+# DWARF:      .debug_names contents:
+# DWARF:      Name Index @ 0x0 {
+# DWARF-NEXT:   Header {
+# DWARF-NEXT:     Length: 0x15C
+# DWARF-NEXT:     Format: DWARF32
+# DWARF-NEXT:     Version: 5
+# DWARF-NEXT:     CU count: 2
+# DWARF-NEXT:     Local TU count: 0
+# DWARF-NEXT:     Foreign TU count: 0
+# DWARF-NEXT:     Bucket count: 9
+# DWARF-NEXT:     Name count: 9
+# DWARF-NEXT:     Abbreviations table size: 0x33
+# DWARF-NEXT:     Augmentation: 'LLVM0700'
+# DWARF:        Compilation Unit offsets [
+# DWARF-NEXT:     CU[0]: 0x00000000
+# DWARF-NEXT:     CU[1]: 0x000000cc
+# DWARF:        Abbreviations [
+# DWARF-NEXT:     Abbreviation 0x1 {
+# DWARF-NEXT:       Tag: DW_TAG_base_type
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x2 {
+# DWARF-NEXT:       Tag: DW_TAG_subprogram
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x3 {
+# DWARF-NEXT:       Tag: DW_TAG_structure_type
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x4 {
+# DWARF-NEXT:       Tag: DW_TAG_subprogram
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x5 {
+# DWARF-NEXT:       Tag: DW_TAG_namespace
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:            String: 0x00000093 "bar"
+# DWARF-NEXT:       Entry @ 0xf7 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF:            String: 0x000000a9 "int"
+# DWARF-NEXT:       Entry @ 0xfe {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_base_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x0000008d
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0x104 {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_base_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x000000ad "foo"
+# DWARF-NEXT:       Entry @ 0x10b {
+# DWARF-NEXT:         Abbrev: 0x3
+# DWARF-NEXT:         Tag: DW_TAG_structure_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000096
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0x111 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000027
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0x117 {
+# DWARF-NEXT:         Abbrev: 0x4
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000045
+# DWARF-NEXT:         DW_IDX_parent: Entry @ 0x128
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0x121 {
+# DWARF-NEXT:         Abbrev: 0x3
+# DWARF-NEXT:         Tag: DW_TAG_structure_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000056
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x00000196 "parent_test"
+# DWARF-NEXT:       Entry @ 0x128 {
+# DWARF-NEXT:         Abbrev: 0x5
+# DWARF-NEXT:         Tag: DW_TAG_namespace
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000043
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x00000097 "_Z3barR3fooi"
+# DWARF-NEXT:       Entry @ 0x12f {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF:            String: 0x000000a4 "main"
+# DWARF-NEXT:       Entry @ 0x136 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000046
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF:            String: 0x000000b5 "char"
+# DWARF-NEXT:       Entry @ 0x13d {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_base_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x000000b8
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0x143 {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_base_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000078
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x000001a2 "_ZN11parent_test3fooEv"
+# DWARF-NEXT:       Entry @ 0x14a {
+# DWARF-NEXT:         Abbrev: 0x4
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000045
+# DWARF-NEXT:         DW_IDX_parent: Entry @ 0x128
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x0000018e "_Z3foov"
+# DWARF-NEXT:       Entry @ 0x155 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000027
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+	
+#-- input file: debug-names-parent-idx.cpp
+## Generated with:
+##
+## - clang++ -g -O0 -gpubnames -fdebug-compilation-dir='parent-idx-test' \
+##     -S debug-names-parent-idx.cpp -o debug-names-parent-idx.s
+##
+## foo.h contents:
+##
+## int foo();
+##
+## struct foo {
+##   int x;
+##   char y;
+##   struct foo *foo_ptr;
+## };
+##
+## namespace parent_test {
+##   int foo();
+## }
+##
+##  debug-names-parent-idx.cpp contents:
+##
+## #include "foo.h"
+## void bar (struct foo &foo, int junk) {
+##   foo.x = foo.x * junk;
+## }
+## int main (int argc, char** argv) {
+##   struct foo my_struct;
+##   my_struct.x = 10;
+##   my_struct.y = 'q';
+##   my_struct.foo_ptr = nullptr;
+##   int junk = foo();
+##   bar(my_struct, junk);
+##   int junk2 = parent_test::foo();
+##   return 0;
+## }
+##
+	.text
+	.globl	_Z3barR3fooi                    # -- Begin function _Z3barR3fooi
+	.p2align	4, 0x90
+	.type	_Z3barR3fooi, at function
+_Z3barR3fooi:                           # @_Z3barR3fooi
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movq	%rdi, -8(%rbp)
+	movl	%esi, -12(%rbp)
+.Ltmp0:
+	movq	-8(%rbp), %rax
+	movl	(%rax), %ecx
+	imull	-12(%rbp), %ecx
+	movq	-8(%rbp), %rax
+	movl	%ecx, (%rax)
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z3barR3fooi, .Lfunc_end0-_Z3barR3fooi
+	.cfi_endproc
+                                        # -- End function
+	.globl	main                            # -- Begin function main
+	.p2align	4, 0x90
+	.type	main, at function
+main:                                   # @main
+.Lfunc_begin1:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq	$48, %rsp
+	movl	$0, -4(%rbp)
+	movl	%edi, -8(%rbp)
+	movq	%rsi, -16(%rbp)
+.Ltmp2:
+	movl	$10, -32(%rbp)
+	movb	$113, -28(%rbp)
+	movq	$0, -24(%rbp)
+	callq	_Z3foov at PLT
+	movl	%eax, -36(%rbp)
+	movl	-36(%rbp), %esi
+	leaq	-32(%rbp), %rdi
+	callq	_Z3barR3fooi
+	callq	_ZN11parent_test3fooEv at PLT
+	movl	%eax, -40(%rbp)
+	xorl	%eax, %eax
+	addq	$48, %rsp
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp3:
+.Lfunc_end1:
+	.size	main, .Lfunc_end1-main
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0xc0 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x23 DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_linkage_name
+	.byte	4                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	3                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x2f:0xb DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	120
+	.byte	7                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	3                               # DW_AT_decl_line
+	.long	145                             # DW_AT_type
+	.byte	3                               # Abbrev [3] 0x3a:0xb DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	116
+	.byte	12                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	3                               # DW_AT_decl_line
+	.long	141                             # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x46:0x47 DW_TAG_subprogram
+	.byte	1                               # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin1       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	5                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.long	141                             # DW_AT_type
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x55:0xb DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	120
+	.byte	13                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.long	141                             # DW_AT_type
+	.byte	3                               # Abbrev [3] 0x60:0xb DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	112
+	.byte	14                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.long	193                             # DW_AT_type
+	.byte	5                               # Abbrev [5] 0x6b:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	96
+	.byte	15                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	9                               # DW_AT_decl_line
+	.long	150                             # DW_AT_type
+	.byte	5                               # Abbrev [5] 0x76:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	92
+	.byte	12                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	14                              # DW_AT_decl_line
+	.long	141                             # DW_AT_type
+	.byte	5                               # Abbrev [5] 0x81:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	88
+	.byte	16                              # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	16                              # DW_AT_decl_line
+	.long	141                             # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	6                               # Abbrev [6] 0x8d:0x4 DW_TAG_base_type
+	.byte	6                               # DW_AT_name
+	.byte	5                               # DW_AT_encoding
+	.byte	4                               # DW_AT_byte_size
+	.byte	7                               # Abbrev [7] 0x91:0x5 DW_TAG_reference_type
+	.long	150                             # DW_AT_type
+	.byte	8                               # Abbrev [8] 0x96:0x22 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	7                               # DW_AT_name
+	.byte	16                              # DW_AT_byte_size
+	.byte	1                               # DW_AT_decl_file
+	.byte	4                               # DW_AT_decl_line
+	.byte	9                               # Abbrev [9] 0x9c:0x9 DW_TAG_member
+	.byte	8                               # DW_AT_name
+	.long	141                             # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	5                               # DW_AT_decl_line
+	.byte	0                               # DW_AT_data_member_location
+	.byte	9                               # Abbrev [9] 0xa5:0x9 DW_TAG_member
+	.byte	9                               # DW_AT_name
+	.long	184                             # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	6                               # DW_AT_decl_line
+	.byte	4                               # DW_AT_data_member_location
+	.byte	9                               # Abbrev [9] 0xae:0x9 DW_TAG_member
+	.byte	11                              # DW_AT_name
+	.long	188                             # DW_AT_type
+	.byte	1                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.byte	8                               # DW_AT_data_member_location
+	.byte	0                               # End Of Children Mark
+	.byte	6                               # Abbrev [6] 0xb8:0x4 DW_TAG_base_type
+	.byte	10                              # DW_AT_name
+	.byte	6                               # DW_AT_encoding
+	.byte	1                               # DW_AT_byte_size
+	.byte	10                              # Abbrev [10] 0xbc:0x5 DW_TAG_pointer_type
+	.long	150                             # DW_AT_type
+	.byte	10                              # Abbrev [10] 0xc1:0x5 DW_TAG_pointer_type
+	.long	198                             # DW_AT_type
+	.byte	10                              # Abbrev [10] 0xc6:0x5 DW_TAG_pointer_type
+	.long	184                             # DW_AT_type
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	72                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names-parent-idx.cpp"    # string offset=104
+.Linfo_string2:
+	.asciz	"parent-idx-test"               # string offset=131
+.Linfo_string3:
+	.asciz	"bar"                           # string offset=147
+.Linfo_string4:
+	.asciz	"_Z3barR3fooi"                  # string offset=151
+.Linfo_string5:
+	.asciz	"main"                          # string offset=164
+.Linfo_string6:
+	.asciz	"int"                           # string offset=169
+.Linfo_string7:
+	.asciz	"foo"                           # string offset=173
+.Linfo_string8:
+	.asciz	"x"                             # string offset=177
+.Linfo_string9:
+	.asciz	"y"                             # string offset=179
+.Linfo_string10:
+	.asciz	"char"                          # string offset=181
+.Linfo_string11:
+	.asciz	"foo_ptr"                       # string offset=186
+.Linfo_string12:
+	.asciz	"junk"                          # string offset=194
+.Linfo_string13:
+	.asciz	"argc"                          # string offset=199
+.Linfo_string14:
+	.asciz	"argv"                          # string offset=204
+.Linfo_string15:
+	.asciz	"my_struct"                     # string offset=209
+.Linfo_string16:
+	.asciz	"junk2"                         # string offset=219
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+	.quad	.Lfunc_begin1
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	6                               # Header: bucket count
+	.long	6                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	1                               # Bucket 0
+	.long	0                               # Bucket 1
+	.long	2                               # Bucket 2
+	.long	4                               # Bucket 3
+	.long	5                               # Bucket 4
+	.long	6                               # Bucket 5
+	.long	193487034                       # Hash in Bucket 0
+	.long	193495088                       # Hash in Bucket 2
+	.long	1358986904                      # Hash in Bucket 2
+	.long	193491849                       # Hash in Bucket 3
+	.long	2090499946                      # Hash in Bucket 4
+	.long	2090147939                      # Hash in Bucket 5
+	.long	.Linfo_string3                  # String in Bucket 0: bar
+	.long	.Linfo_string6                  # String in Bucket 2: int
+	.long	.Linfo_string4                  # String in Bucket 2: _Z3barR3fooi
+	.long	.Linfo_string7                  # String in Bucket 3: foo
+	.long	.Linfo_string5                  # String in Bucket 4: main
+	.long	.Linfo_string10                 # String in Bucket 5: char
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 0
+	.long	.Lnames3-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+	.long	.Lnames4-.Lnames_entries0       # Offset in Bucket 3
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 4
+	.long	.Lnames5-.Lnames_entries0       # Offset in Bucket 5
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	36                              # DW_TAG_base_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	3                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L2:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: bar
+.Lnames3:
+.L1:
+	.byte	2                               # Abbreviation code
+	.long	141                             # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: int
+.Lnames1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z3barR3fooi
+.Lnames4:
+.L4:
+	.byte	3                               # Abbreviation code
+	.long	150                             # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: foo
+.Lnames2:
+.L0:
+	.byte	1                               # Abbreviation code
+	.long	70                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: main
+.Lnames5:
+.L3:
+	.byte	2                               # Abbreviation code
+	.long	184                             # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: char
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.addrsig_sym _Z3barR3fooi
+	.addrsig_sym _Z3foov
+	.addrsig_sym _ZN11parent_test3fooEv
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lld/test/ELF/debug-names.s b/lld/test/ELF/debug-names.s
new file mode 100644
index 00000000000000..ce013303f65f67
--- /dev/null
+++ b/lld/test/ELF/debug-names.s
@@ -0,0 +1,294 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/debug-names-2.s -o %t2.o
+# RUN: ld.lld --debug-names %t1.o %t2.o -o %t
+
+# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=DISASM
+# RUN: llvm-dwarfdump -debug-names %t | FileCheck %s --check-prefix=DWARF
+# RUN: llvm-readelf -SW %t | FileCheck %s --check-prefix=READELF
+	
+# DISASM:       Disassembly of section .text:
+# DISASM-EMPTY:
+# DISASM:       <_Z2f12t1>:
+# DISASM-CHECK:   201180: 55       pushq %rbp
+# DISASM-CHECK:   201181: 48 89 e5 movq  %rsp, %rbp
+# DISASM-CHECK:   201184: 5d       popq  %rbp
+# DISASM-CHECK:   201185: c3       retq
+# DISASM-CHECK:   201186: cc       int3
+# DISASM-CHECK:   201187: cc       int3
+# DISASM-CHECK:   201188: cc       int3
+# DISASM-CHECK:   201189: cc       int3
+# DISASM-CHECK:   20118a: cc       int3
+# DISASM-CHECK:   20118b: cc       int3
+# DISASM-CHECK:   20118c: cc       int3
+# DISASM-CHECK:   20118d: cc       int3
+# DISASM-CHECK:   20118e: cc       int3
+# DISASM-CHECK:   20118f: cc       int3
+# DISASM:       <main>:
+# DISASM-CHECK:   201190: 55       pushq %rbp
+# DISASM-CHECK:   201191: 48 89 e5 movq  %rsp, %rbp
+# DISASM-CHECK:   201194: 31 c0    xorl  %eax, %eax
+# DISASM-CHECK:   201196: 5d       popq  %rbp
+# DISASM-CHECK:   201197: c3       retq
+
+# DWARF:      .debug_names contents:
+# DWARF:      Name Index @ 0x0 {
+# DWARF-NEXT:   Header {
+# DWARF-NEXT:     Length: 0xCC
+# DWARF-NEXT:     Format: DWARF32
+# DWARF-NEXT:     Version: 5
+# DWARF-NEXT:     CU count: 2
+# DWARF-NEXT:     Local TU count: 0
+# DWARF-NEXT:     Foreign TU count: 0
+# DWARF-NEXT:     Bucket count: 5
+# DWARF-NEXT:     Name count: 5
+# DWARF-NEXT:     Abbreviations table size: 0x1F
+# DWARF-NEXT:     Augmentation: 'LLVM0700'
+# DWARF:        Compilation Unit offsets [
+# DWARF-NEXT:     CU[0]: 0x00000000
+# DWARF-NEXT:     CU[1]: 0x00000041
+# DWARF:          Abbreviations [
+# DWARF-NEXT:     Abbreviation 0x1 {
+# DWARF:            Tag: DW_TAG_structure_type
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x2 {
+# DWARF-NEXT:       Tag: DW_TAG_subprogram
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:          Abbreviation 0x3 {
+# DWARF-NEXT:       Tag: DW_TAG_base_type
+# DWARF-NEXT:       DW_IDX_die_offset: DW_FORM_ref4
+# DWARF-NEXT:       DW_IDX_parent: DW_FORM_flag_present
+# DWARF-NEXT:       DW_IDX_compile_unit: DW_FORM_data1
+# DWARF:        Bucket 0 [
+# DWARF:        Bucket 1 [
+# DWARF:            String: 0x00000089 "f1"
+# DWARF-NEXT:       Entry @ 0xa3 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF:            String: 0x00000095 "t1"
+# DWARF-NEXT:       Entry @ 0xaa {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_structure_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x0000003a
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF-NEXT:       }
+# DWARF-NEXT:       Entry @ 0xb0 {
+# DWARF-NEXT:         Abbrev: 0x1
+# DWARF-NEXT:         Tag: DW_TAG_structure_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000042
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:            String: 0x00000130 "int"
+# DWARF-NEXT:       Entry @ 0xb7 {
+# DWARF-NEXT:         Abbrev: 0x3
+# DWARF-NEXT:         Tag: DW_TAG_base_type
+# DWARF-NEXT:         DW_IDX_die_offset: 0x0000003e
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+# DWARF:        Bucket 2 [
+# DWARF:        Bucket 3 [
+# DWARF:            String: 0x0000008c "_Z2f12t1"
+# DWARF-NEXT:       Entry @ 0xbe {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x00
+# DWARF:        Bucket 4 [
+# DWARF:            String: 0x0000012b "main"
+# DWARF-NEXT:       Entry @ 0xc5 {
+# DWARF-NEXT:         Abbrev: 0x2
+# DWARF-NEXT:         Tag: DW_TAG_subprogram
+# DWARF-NEXT:         DW_IDX_die_offset: 0x00000023
+# DWARF-NEXT:         DW_IDX_parent: <parent not indexed>
+# DWARF-NEXT:         DW_IDX_compile_unit: 0x01
+
+# READELF: .debug_names PROGBITS 0000000000000000 0003eb 0000d0
+
+# RUN: ld.lld --debug-names --no-debug-names %t1.o %t2.o -o %t
+# RUN: llvm-readelf -SW %t | FileCheck %s --check-prefix=NO_DBG_NAMES
+	
+
+# NO_DBG_NAMES: .debug_names  PROGBITS  0000000000000000 00037c 000110
+	
+#-- input file: debug-names.cpp
+## Generated with:
+##
+## - clang++ -g -O0 -gpubnames -fdebug-compilation-dir='debug-names-test' \
+##     -S debug-names.cpp -o debug-names.s
+##
+## debug-names.cpp contents:
+##
+## struct t1 { };
+## void f1(t1) { }
+##
+##
+	.text
+	.globl	_Z2f12t1                        # -- Begin function _Z2f12t1
+	.p2align	4, 0x90
+	.type	_Z2f12t1, at function
+_Z2f12t1:                               # @_Z2f12t1
+.Lfunc_begin0:
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z2f12t1, .Lfunc_end0-_Z2f12t1
+	.cfi_endproc
+                                        # -- End function
+	.section	.debug_abbrev,"", at progbits
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"", at progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x35 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x17 DW_TAG_subprogram
+	.byte	0                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	3                               # DW_AT_linkage_name
+	.byte	4                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	3                               # Abbrev [3] 0x2f:0xa DW_TAG_formal_parameter
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	127
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.long	58                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	4                               # Abbrev [4] 0x3a:0x6 DW_TAG_structure_type
+	.byte	5                               # DW_AT_calling_convention
+	.byte	5                               # DW_AT_name
+	.byte	1                               # DW_AT_byte_size
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"", at progbits
+	.long	28                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS", at progbits,1
+.Linfo_string0:
+	.asciz	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)" # string offset=0
+.Linfo_string1:
+	.asciz	"debug-names.cpp"               # string offset=104
+.Linfo_string2:
+	.asciz	"debug-names-test"              # string offset=120
+.Linfo_string3:
+	.asciz	"f1"                            # string offset=137
+.Linfo_string4:
+	.asciz	"_Z2f12t1"                      # string offset=140
+.Linfo_string5:
+	.asciz	"t1"                            # string offset=149
+.Laddr_table_base0:
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0     # Header: unit length
+.Lnames_start0:
+	.short	5                               # Header: version
+	.short	0                               # Header: padding
+	.long	1                               # Header: compilation unit count
+	.long	0                               # Header: local type unit count
+	.long	0                               # Header: foreign type unit count
+	.long	3                               # Header: bucket count
+	.long	3                               # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	8                               # Header: augmentation string size
+	.ascii	"LLVM0700"                      # Header: augmentation string
+	.long	.Lcu_begin0                     # Compilation unit 0
+	.long	0                               # Bucket 0
+	.long	1                               # Bucket 1
+	.long	3                               # Bucket 2
+	.long	5863324                         # Hash in Bucket 1
+	.long	5863786                         # Hash in Bucket 1
+	.long	1398125246                      # Hash in Bucket 2
+	.long	.Linfo_string3                  # String in Bucket 1: f1
+	.long	.Linfo_string5                  # String in Bucket 1: t1
+	.long	.Linfo_string4                  # String in Bucket 2: _Z2f12t1
+	.long	.Lnames0-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames2-.Lnames_entries0       # Offset in Bucket 1
+	.long	.Lnames1-.Lnames_entries0       # Offset in Bucket 2
+.Lnames_abbrev_start0:
+	.byte	1                               # Abbrev code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	2                               # Abbrev code
+	.byte	19                              # DW_TAG_structure_type
+	.byte	3                               # DW_IDX_die_offset
+	.byte	19                              # DW_FORM_ref4
+	.byte	4                               # DW_IDX_parent
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev
+	.byte	0                               # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames0:
+.L1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: f1
+.Lnames2:
+.L0:
+	.byte	2                               # Abbreviation code
+	.long	58                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: t1
+.Lnames1:
+	.byte	1                               # Abbreviation code
+	.long	35                              # DW_IDX_die_offset
+	.byte	0                               # DW_IDX_parent
+                                        # End of list: _Z2f12t1
+	.p2align	2, 0x0
+.Lnames_end0:
+	.ident	"clang version 19.0.0git (git at github.com:llvm/llvm-project.git 4df364bc93af49ae413ec1ae8328f34ac70730c4)"
+	.section	".note.GNU-stack","", at progbits
+	.addrsig
+	.section	.debug_line,"", at progbits
+.Lline_table_start0:



More information about the llvm-commits mailing list