[llvm] [llvm-dwp] Replace MCStreamer with direct ELF writer for zero-copy output (PR #192112)

Farid Zakaria via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 16 10:25:30 PDT 2026


https://github.com/fzakaria updated https://github.com/llvm/llvm-project/pull/192112

>From 9168bf155205262addd2e3b1759be9a20bc077bb Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at meta.com>
Date: Thu, 16 Apr 2026 09:20:40 -0700
Subject: [PATCH 1/2] [llvm-dwp] Replace MCStreamer with direct ELF writer for
 zero-copy output

Replace the MCStreamer-based output pipeline with a lightweight direct
ELF writer (DWPWriter). Section data is stored as zero-copy StringRef
chunks pointing to the mmap'd input files, and written as a minimal
ELF64 relocatable object directly to disk.

The MCStreamer pipeline copies all section data into 16KB MCDataFragment
blocks, accumulates them in memory, then writes everything out during
MCAssembler::Finish(). For a 3.3GB DWP file, this means ~3.3GB of heap
allocation and two full copies of the data. The new DWPWriter avoids
this:
- emitBytes() stores a StringRef chunk (zero-copy, no allocation)
- emitIntValue() writes to a small per-section buffer (index tables)
- writeELF() streams chunks directly from input mmap to output file

For single-input DWP files, string deduplication is also skipped since
all strings are already unique.

This also removes all MC library dependencies from llvm-dwp
(AllTargetsCodeGens, AllTargetsDescs, AllTargetsInfos, MC,
TargetParser), significantly reducing the binary size.

Benchmarked on a 3.3GB production DWP file (unicorn
inrack_aggregator_server, 8638 CUs, ~981MB .debug_str.dwo):

  Before: 23.6s wall (19.6s user, 3.9s sys)
  After:   6.0s wall  (3.0s user, 2.9s sys)

  3.9x wall time improvement, 6x fewer page faults (178K vs ~1M).

The benchmark represents a single-input DWP repackaging workflow.
Multi-input workloads (merging many .dwo files) also benefit from the
zero-copy pass-through of bulk debug sections (loclists, rnglists,
abbrev, line, info) but will still perform string deduplication.
---
 llvm/include/llvm/DWP/DWP.h           | 123 +++++---
 llvm/include/llvm/DWP/DWPStringPool.h |  41 +--
 llvm/lib/DWP/CMakeLists.txt           |   1 -
 llvm/lib/DWP/DWP.cpp                  | 424 +++++++++++++++++++-------
 llvm/tools/llvm-dwp/CMakeLists.txt    |   5 -
 llvm/tools/llvm-dwp/llvm-dwp.cpp      |  98 +-----
 6 files changed, 417 insertions(+), 275 deletions(-)

diff --git a/llvm/include/llvm/DWP/DWP.h b/llvm/include/llvm/DWP/DWP.h
index 67c73afbfe5ac..af4463ae81377 100644
--- a/llvm/include/llvm/DWP/DWP.h
+++ b/llvm/include/llvm/DWP/DWP.h
@@ -4,11 +4,10 @@
 #include "DWPStringPool.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
-#include "llvm/MC/MCSection.h"
-#include "llvm/MC/MCStreamer.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
@@ -16,6 +15,8 @@
 #include <vector>
 
 namespace llvm {
+class raw_pwrite_stream;
+
 enum OnCuIndexOverflow {
   HardStop,
   SoftStop,
@@ -28,6 +29,88 @@ enum Dwarf64StrOffsetsPromotion {
   Always,   ///< Always emit .debug_str_offsets talbes as DWARF64 for testing.
 };
 
+/// Section identifiers for DWP output.
+enum DWPSectionId : unsigned {
+  DS_Info,
+  DS_Types,
+  DS_Abbrev,
+  DS_Line,
+  DS_Loc,
+  DS_Loclists,
+  DS_Rnglists,
+  DS_Macro,
+  DS_Str,
+  DS_StrOffsets,
+  DS_CUIndex,
+  DS_TUIndex,
+  DS_NumSections
+};
+
+/// Direct ELF writer for DWP output, bypassing MCStreamer.
+///
+/// Section data is stored as zero-copy StringRef chunks pointing to the
+/// mmap'd input files, plus an inline buffer for constructed data
+/// (emitIntValue). This avoids copying gigabytes of debug section data
+/// through the MC infrastructure (MCContext, MCAssembler, MCDataFragment
+/// allocation, layout, etc.).
+class LLVM_ABI DWPWriter {
+  /// Per-section storage: zero-copy chunks + inline buffer for small writes.
+  struct SectionData {
+    SmallVector<StringRef, 4> Chunks; // zero-copy refs to input data
+    SmallVector<char, 0> Buffer;      // for emitIntValue / constructed data
+
+    uint64_t totalSize() const {
+      uint64_t Size = 0;
+      for (auto &C : Chunks)
+        Size += C.size();
+      Size += Buffer.size();
+      return Size;
+    }
+
+    bool empty() const { return Chunks.empty() && Buffer.empty(); }
+
+    void writeTo(raw_ostream &OS) const {
+      for (auto &C : Chunks)
+        OS.write(C.data(), C.size());
+      if (!Buffer.empty())
+        OS.write(Buffer.data(), Buffer.size());
+    }
+  };
+
+  SectionData Sections[DS_NumSections];
+  DWPSectionId CurrentSection = DS_Info;
+  uint16_t ELFMachine = 0;
+  uint8_t ELFOSABI = 0;
+
+public:
+  DWPWriter() = default;
+
+  void setMachine(uint16_t Machine) { ELFMachine = Machine; }
+  void setOSABI(uint8_t OSABI) { ELFOSABI = OSABI; }
+
+  SmallVectorImpl<char> &getSectionBuffer(DWPSectionId Id) {
+    return Sections[Id].Buffer;
+  }
+
+  void switchSection(DWPSectionId Id) { CurrentSection = Id; }
+
+  /// Zero-copy: stores a reference to the input data without copying.
+  void emitBytes(StringRef Data) {
+    if (!Data.empty())
+      Sections[CurrentSection].Chunks.push_back(Data);
+  }
+
+  void emitIntValue(uint64_t Value, unsigned Size) {
+    auto &Buf = Sections[CurrentSection].Buffer;
+    for (unsigned I = 0; I < Size; ++I) {
+      Buf.push_back(static_cast<char>(Value & 0xff));
+      Value >>= 8;
+    }
+  }
+
+  Error writeELF(raw_pwrite_stream &OS);
+};
+
 struct UnitIndexEntry {
   DWARFUnitIndex::Entry::SectionContribution Contributions[8];
   std::string Name;
@@ -73,45 +156,15 @@ struct CompileUnitIdentifiers {
   const char *DWOName = "";
 };
 
-LLVM_ABI Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
+LLVM_ABI Error write(DWPWriter &Out, ArrayRef<std::string> Inputs,
                      OnCuIndexOverflow OverflowOptValue,
-                     Dwarf64StrOffsetsPromotion StrOffsetsOptValue);
+                     Dwarf64StrOffsetsPromotion StrOffsetsOptValue,
+                     raw_pwrite_stream *OS = nullptr);
 
 typedef std::vector<std::pair<DWARFSectionKind, uint32_t>> SectionLengths;
 
-LLVM_ABI Error handleSection(
-    const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections,
-    const MCSection *StrSection, const MCSection *StrOffsetSection,
-    const MCSection *TypesSection, const MCSection *CUIndexSection,
-    const MCSection *TUIndexSection, const MCSection *InfoSection,
-    const object::SectionRef &Section, MCStreamer &Out,
-    std::deque<SmallString<32>> &UncompressedSections,
-    uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry,
-    StringRef &CurStrSection, StringRef &CurStrOffsetSection,
-    std::vector<StringRef> &CurTypesSection,
-    std::vector<StringRef> &CurInfoSection, StringRef &AbbrevSection,
-    StringRef &CurCUIndexSection, StringRef &CurTUIndexSection,
-    SectionLengths &SectionLength);
-
 LLVM_ABI Expected<InfoSectionUnitHeader>
 parseInfoSectionUnitHeader(StringRef Info);
 
-LLVM_ABI void
-writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings,
-                       MCSection *StrOffsetSection, StringRef CurStrSection,
-                       StringRef CurStrOffsetSection, uint16_t Version,
-                       SectionLengths &SectionLength,
-                       const Dwarf64StrOffsetsPromotion StrOffsetsOptValue);
-
-LLVM_ABI Error
-buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
-                    const CompileUnitIdentifiers &ID, StringRef DWPName);
-
-LLVM_ABI void
-writeIndex(MCStreamer &Out, MCSection *Section,
-           ArrayRef<unsigned> ContributionOffsets,
-           const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
-           uint32_t IndexVersion);
-
 } // namespace llvm
 #endif // LLVM_DWP_DWP_H
diff --git a/llvm/include/llvm/DWP/DWPStringPool.h b/llvm/include/llvm/DWP/DWPStringPool.h
index d1486ff7872e1..7122e84cdfff5 100644
--- a/llvm/include/llvm/DWP/DWPStringPool.h
+++ b/llvm/include/llvm/DWP/DWPStringPool.h
@@ -2,49 +2,28 @@
 #define LLVM_DWP_DWPSTRINGPOOL_H
 
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/MC/MCSection.h"
-#include "llvm/MC/MCStreamer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
 #include <cassert>
 
 namespace llvm {
 class DWPStringPool {
-
-  struct CStrDenseMapInfo {
-    static inline const char *getEmptyKey() {
-      return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0));
-    }
-    static inline const char *getTombstoneKey() {
-      return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1));
-    }
-    static unsigned getHashValue(const char *Val) {
-      assert(Val != getEmptyKey() && "Cannot hash the empty key!");
-      assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
-      return (unsigned)hash_value(StringRef(Val));
-    }
-    static bool isEqual(const char *LHS, const char *RHS) {
-      if (RHS == getEmptyKey())
-        return LHS == getEmptyKey();
-      if (RHS == getTombstoneKey())
-        return LHS == getTombstoneKey();
-      return strcmp(LHS, RHS) == 0;
-    }
-  };
-
-  MCStreamer &Out;
-  MCSection *Sec;
-  DenseMap<const char *, uint64_t, CStrDenseMapInfo> Pool;
+  SmallVectorImpl<char> &Buffer;
+  // Use StringRef keys instead of const char* to avoid redundant strlen
+  // on every hash computation and strcmp on every probe comparison.
+  DenseMap<StringRef, uint64_t> Pool;
   uint64_t Offset = 0;
 
 public:
-  DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {}
+  DWPStringPool(SmallVectorImpl<char> &Buffer) : Buffer(Buffer) {}
 
   uint64_t getOffset(const char *Str, unsigned Length) {
     assert(strlen(Str) + 1 == Length && "Ensure length hint is correct");
 
-    auto Pair = Pool.insert(std::make_pair(Str, Offset));
+    StringRef Key(Str, Length - 1);
+    auto Pair = Pool.insert(std::make_pair(Key, Offset));
     if (Pair.second) {
-      Out.switchSection(Sec);
-      Out.emitBytes(StringRef(Str, Length));
+      Buffer.insert(Buffer.end(), Str, Str + Length);
       Offset += Length;
     }
 
diff --git a/llvm/lib/DWP/CMakeLists.txt b/llvm/lib/DWP/CMakeLists.txt
index 777de1978dae3..56199dec94108 100644
--- a/llvm/lib/DWP/CMakeLists.txt
+++ b/llvm/lib/DWP/CMakeLists.txt
@@ -10,7 +10,6 @@ add_llvm_component_library(LLVMDWP
 
   LINK_COMPONENTS
   DebugInfoDWARF
-  MC
   Object
   Support
   )
diff --git a/llvm/lib/DWP/DWP.cpp b/llvm/lib/DWP/DWP.cpp
index ea029e97464bc..91afb930c9309 100644
--- a/llvm/lib/DWP/DWP.cpp
+++ b/llvm/lib/DWP/DWP.cpp
@@ -11,20 +11,20 @@
 //
 //===----------------------------------------------------------------------===//
 #include "llvm/DWP/DWP.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/DWP/DWPError.h"
-#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCObjectFileInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.h"
 #include "llvm/Object/Decompressor.h"
 #include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/MathExtras.h"
 #include <limits>
 
 using namespace llvm;
 using namespace llvm::object;
 
-static mc::RegisterMCTargetOptionsFlags MCTargetOptionsFlags;
-
 // Returns the size of debug_str_offsets section headers in bytes.
 static uint64_t debugStrOffsetsHeaderSize(DataExtractor StrOffsetsData,
                                           uint16_t DwarfVersion) {
@@ -200,12 +200,12 @@ static Error sectionOverflowErrorOrWarning(uint32_t PrevOffset,
 }
 
 static Error addAllTypesFromDWP(
-    MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
-    const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types,
+    DWPWriter &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+    const DWARFUnitIndex &TUIndex, DWPSectionId OutputSection, StringRef Types,
     const UnitIndexEntry &TUEntry, uint32_t &TypesOffset,
     unsigned TypesContributionIndex, OnCuIndexOverflow OverflowOptValue,
     bool &AnySectionOverflow) {
-  Out.switchSection(OutputTypes);
+  Out.switchSection(OutputSection);
   for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) {
     auto *I = E.getContributions();
     if (!I)
@@ -249,12 +249,12 @@ static Error addAllTypesFromDWP(
 }
 
 static Error addAllTypesFromTypesSection(
-    MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
-    MCSection *OutputTypes, const std::vector<StringRef> &TypesSections,
+    DWPWriter &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+    DWPSectionId OutputSection, const std::vector<StringRef> &TypesSections,
     const UnitIndexEntry &CUEntry, uint32_t &TypesOffset,
     OnCuIndexOverflow OverflowOptValue, bool &AnySectionOverflow) {
   for (StringRef Types : TypesSections) {
-    Out.switchSection(OutputTypes);
+    Out.switchSection(OutputSection);
     uint64_t Offset = 0;
     DataExtractor Data(Types, true, 0);
     while (Data.isValidOffset(Offset)) {
@@ -351,6 +351,32 @@ handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections,
   return Error::success();
 }
 
+static Error buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
+                                 const CompileUnitIdentifiers &ID,
+                                 StringRef DWPName) {
+  return make_error<DWPError>(
+      std::string("duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " +
+      buildDWODescription(PrevE.second.Name, PrevE.second.DWPName,
+                          PrevE.second.DWOName) +
+      " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName));
+}
+
+// Create a mask so we don't trigger a emitIntValue() assert below if the
+// NewOffset is over 4GB.
+static void writeNewOffsetsTo(DWPWriter &Out, DataExtractor &Data,
+                              DenseMap<uint64_t, uint64_t> &OffsetRemapping,
+                              uint64_t &Offset, const uint64_t Size,
+                              uint32_t OldOffsetSize, uint32_t NewOffsetSize) {
+  const uint64_t NewOffsetMask = NewOffsetSize == 8 ? UINT64_MAX : UINT32_MAX;
+  while (Offset < Size) {
+    const uint64_t OldOffset = Data.getUnsigned(&Offset, OldOffsetSize);
+    const uint64_t NewOffset = OffsetRemapping[OldOffset];
+    // Truncate the string offset like the old llvm-dwp would have if we aren't
+    // promoting the .debug_str_offsets to DWARF64.
+    Out.emitIntValue(NewOffset & NewOffsetMask, NewOffsetSize);
+  }
+}
+
 namespace llvm {
 // Parse and return the header of an info section compile/type unit.
 Expected<InfoSectionUnitHeader> parseInfoSectionUnitHeader(StringRef Info) {
@@ -412,33 +438,31 @@ Expected<InfoSectionUnitHeader> parseInfoSectionUnitHeader(StringRef Info) {
   return Header;
 }
 
-static void writeNewOffsetsTo(MCStreamer &Out, DataExtractor &Data,
-                              DenseMap<uint64_t, uint64_t> &OffsetRemapping,
-                              uint64_t &Offset, const uint64_t Size,
-                              uint32_t OldOffsetSize, uint32_t NewOffsetSize) {
-  // Create a mask so we don't trigger a emitIntValue() assert below if the
-  // NewOffset is over 4GB.
-  const uint64_t NewOffsetMask = NewOffsetSize == 8 ? UINT64_MAX : UINT32_MAX;
-  while (Offset < Size) {
-    const uint64_t OldOffset = Data.getUnsigned(&Offset, OldOffsetSize);
-    const uint64_t NewOffset = OffsetRemapping[OldOffset];
-    // Truncate the string offset like the old llvm-dwp would have if we aren't
-    // promoting the .debug_str_offsets to DWARF64.
-    Out.emitIntValue(NewOffset & NewOffsetMask, NewOffsetSize);
-  }
-}
-
-void writeStringsAndOffsets(
-    MCStreamer &Out, DWPStringPool &Strings, MCSection *StrOffsetSection,
-    StringRef CurStrSection, StringRef CurStrOffsetSection, uint16_t Version,
-    SectionLengths &SectionLength,
-    const Dwarf64StrOffsetsPromotion StrOffsetsOptValue) {
+static void
+writeStringsAndOffsets(DWPWriter &Out, DWPStringPool &Strings,
+                       StringRef CurStrSection, StringRef CurStrOffsetSection,
+                       uint16_t Version, SectionLengths &SectionLength,
+                       const Dwarf64StrOffsetsPromotion StrOffsetsOptValue,
+                       bool SingleInput) {
   // Could possibly produce an error or warning if one of these was non-null but
   // the other was null.
   if (CurStrSection.empty() || CurStrOffsetSection.empty())
     return;
 
+  // Fast path: when there is only one input, all strings are unique and offsets
+  // don't need remapping. Copy both sections directly without any hashing.
+  if (SingleInput &&
+      StrOffsetsOptValue != Dwarf64StrOffsetsPromotion::Always) {
+    Out.switchSection(DS_Str);
+    Out.emitBytes(CurStrSection);
+    Out.switchSection(DS_StrOffsets);
+    Out.emitBytes(CurStrOffsetSection);
+    return;
+  }
+
   DenseMap<uint64_t, uint64_t> OffsetRemapping;
+  // Pre-reserve based on estimated string count to avoid rehashing.
+  OffsetRemapping.reserve(CurStrSection.size() / 20);
 
   DataExtractor Data(CurStrSection, true, 0);
   uint64_t LocalOffset = 0;
@@ -464,7 +488,7 @@ void writeStringsAndOffsets(
 
   Data = DataExtractor(CurStrOffsetSection, true, 0);
 
-  Out.switchSection(StrOffsetSection);
+  Out.switchSection(DS_StrOffsets);
 
   uint64_t Offset = 0;
   uint64_t Size = CurStrOffsetSection.size();
@@ -530,9 +554,11 @@ void writeStringsAndOffsets(
 }
 
 enum AccessField { Offset, Length };
-void writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
-                     const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
-                     const AccessField &Field) {
+
+static void
+writeIndexTable(DWPWriter &Out, ArrayRef<unsigned> ContributionOffsets,
+                const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
+                const AccessField &Field) {
   for (const auto &E : IndexEntries)
     for (size_t I = 0; I != std::size(E.second.Contributions); ++I)
       if (ContributionOffsets[I])
@@ -542,10 +568,10 @@ void writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
                          4);
 }
 
-void writeIndex(MCStreamer &Out, MCSection *Section,
-                ArrayRef<unsigned> ContributionOffsets,
-                const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
-                uint32_t IndexVersion) {
+static void writeIndex(DWPWriter &Out, DWPSectionId Section,
+                       ArrayRef<unsigned> ContributionOffsets,
+                       const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
+                       uint32_t IndexVersion) {
   if (IndexEntries.empty())
     return;
 
@@ -596,21 +622,29 @@ void writeIndex(MCStreamer &Out, MCSection *Section,
   writeIndexTable(Out, ContributionOffsets, IndexEntries, AccessField::Length);
 }
 
-Error buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
-                          const CompileUnitIdentifiers &ID, StringRef DWPName) {
-  return make_error<DWPError>(
-      std::string("duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " +
-      buildDWODescription(PrevE.second.Name, PrevE.second.DWPName,
-                          PrevE.second.DWOName) +
-      " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName));
+/// Map input ELF section names to DWP section IDs and DWARF section kinds.
+static const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> &
+getKnownSections() {
+  static const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> Map = {
+      {"debug_info.dwo", {DS_Info, DW_SECT_INFO}},
+      {"debug_types.dwo", {DS_Types, DW_SECT_EXT_TYPES}},
+      {"debug_str_offsets.dwo", {DS_StrOffsets, DW_SECT_STR_OFFSETS}},
+      {"debug_str.dwo", {DS_Str, static_cast<DWARFSectionKind>(0)}},
+      {"debug_loc.dwo", {DS_Loc, DW_SECT_EXT_LOC}},
+      {"debug_line.dwo", {DS_Line, DW_SECT_LINE}},
+      {"debug_macro.dwo", {DS_Macro, DW_SECT_MACRO}},
+      {"debug_abbrev.dwo", {DS_Abbrev, DW_SECT_ABBREV}},
+      {"debug_loclists.dwo", {DS_Loclists, DW_SECT_LOCLISTS}},
+      {"debug_rnglists.dwo", {DS_Rnglists, DW_SECT_RNGLISTS}},
+      {"debug_cu_index", {DS_CUIndex, static_cast<DWARFSectionKind>(0)}},
+      {"debug_tu_index", {DS_TUIndex, static_cast<DWARFSectionKind>(0)}},
+  };
+  return Map;
 }
 
-Error handleSection(
-    const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections,
-    const MCSection *StrSection, const MCSection *StrOffsetSection,
-    const MCSection *TypesSection, const MCSection *CUIndexSection,
-    const MCSection *TUIndexSection, const MCSection *InfoSection,
-    const SectionRef &Section, MCStreamer &Out,
+static Error handleSection(
+    const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> &KnownSections,
+    const SectionRef &Section, DWPWriter &Out,
     std::deque<SmallString<32>> &UncompressedSections,
     uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry,
     StringRef &CurStrSection, StringRef &CurStrOffsetSection,
@@ -644,61 +678,49 @@ Error handleSection(
   if (SectionPair == KnownSections.end())
     return Error::success();
 
-  if (DWARFSectionKind Kind = SectionPair->second.second) {
-    if (Kind != DW_SECT_EXT_TYPES && Kind != DW_SECT_INFO) {
-      SectionLength.push_back(std::make_pair(Kind, Contents.size()));
-    }
+  DWPSectionId SectionId = SectionPair->second.first;
+  DWARFSectionKind Kind = SectionPair->second.second;
 
-    if (Kind == DW_SECT_ABBREV) {
+  if (Kind) {
+    if (Kind != DW_SECT_EXT_TYPES && Kind != DW_SECT_INFO)
+      SectionLength.push_back(std::make_pair(Kind, Contents.size()));
+    if (Kind == DW_SECT_ABBREV)
       AbbrevSection = Contents;
-    }
   }
 
-  MCSection *OutSection = SectionPair->second.first;
-  if (OutSection == StrOffsetSection)
+  switch (SectionId) {
+  case DS_StrOffsets:
     CurStrOffsetSection = Contents;
-  else if (OutSection == StrSection)
+    break;
+  case DS_Str:
     CurStrSection = Contents;
-  else if (OutSection == TypesSection)
+    break;
+  case DS_Types:
     CurTypesSection.push_back(Contents);
-  else if (OutSection == CUIndexSection)
+    break;
+  case DS_CUIndex:
     CurCUIndexSection = Contents;
-  else if (OutSection == TUIndexSection)
+    break;
+  case DS_TUIndex:
     CurTUIndexSection = Contents;
-  else if (OutSection == InfoSection)
+    break;
+  case DS_Info:
     CurInfoSection.push_back(Contents);
-  else {
-    Out.switchSection(OutSection);
+    break;
+  default:
+    // Pass-through: emit directly to output (zero-copy).
+    Out.switchSection(SectionId);
     Out.emitBytes(Contents);
+    break;
   }
   return Error::success();
 }
 
-Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
+Error write(DWPWriter &Out, ArrayRef<std::string> Inputs,
             OnCuIndexOverflow OverflowOptValue,
-            Dwarf64StrOffsetsPromotion StrOffsetsOptValue) {
-  const auto &MCOFI = *Out.getContext().getObjectFileInfo();
-  MCSection *const StrSection = MCOFI.getDwarfStrDWOSection();
-  MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection();
-  MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection();
-  MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection();
-  MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection();
-  MCSection *const InfoSection = MCOFI.getDwarfInfoDWOSection();
-  const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = {
-      {"debug_info.dwo", {InfoSection, DW_SECT_INFO}},
-      {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}},
-      {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}},
-      {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}},
-      {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}},
-      {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}},
-      {"debug_macro.dwo", {MCOFI.getDwarfMacroDWOSection(), DW_SECT_MACRO}},
-      {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
-      {"debug_loclists.dwo",
-       {MCOFI.getDwarfLoclistsDWOSection(), DW_SECT_LOCLISTS}},
-      {"debug_rnglists.dwo",
-       {MCOFI.getDwarfRnglistsDWOSection(), DW_SECT_RNGLISTS}},
-      {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}},
-      {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}};
+            Dwarf64StrOffsetsPromotion StrOffsetsOptValue,
+            raw_pwrite_stream *OutputOS) {
+  const auto &KnownSections = getKnownSections();
 
   MapVector<uint64_t, UnitIndexEntry> IndexEntries;
   MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries;
@@ -709,13 +731,15 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
   StringRef FirstInput;
   bool AnySectionOverflow = false;
 
-  DWPStringPool Strings(Out, StrSection);
+  DWPStringPool Strings(Out.getSectionBuffer(DS_Str));
 
   SmallVector<OwningBinary<object::ObjectFile>, 128> Objects;
   Objects.reserve(Inputs.size());
 
   std::deque<SmallString<32>> UncompressedSections;
 
+  bool MachineSet = false;
+
   for (const auto &Input : Inputs) {
     auto ErrOrObj = object::ObjectFile::createObjectFile(Input);
     if (!ErrOrObj) {
@@ -728,6 +752,15 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
     auto &Obj = *ErrOrObj->getBinary();
     Objects.push_back(std::move(*ErrOrObj));
 
+    // Set the ELF machine type from the first input file.
+    if (!MachineSet) {
+      if (auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) {
+        Out.setMachine(ELFObj->getEMachine());
+        Out.setOSABI(ELFObj->getOS());
+      }
+      MachineSet = true;
+    }
+
     UnitIndexEntry CurEntry = {};
 
     StringRef CurStrSection;
@@ -745,12 +778,11 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
 
     for (const auto &Section : Obj.sections())
       if (auto Err = handleSection(
-              KnownSections, StrSection, StrOffsetSection, TypesSection,
-              CUIndexSection, TUIndexSection, InfoSection, Section, Out,
-              UncompressedSections, ContributionOffsets, CurEntry,
-              CurStrSection, CurStrOffsetSection, CurTypesSection,
-              CurInfoSection, AbbrevSection, CurCUIndexSection,
-              CurTUIndexSection, SectionLength))
+              KnownSections, Section, Out, UncompressedSections,
+              ContributionOffsets, CurEntry, CurStrSection,
+              CurStrOffsetSection, CurTypesSection, CurInfoSection,
+              AbbrevSection, CurCUIndexSection, CurTUIndexSection,
+              SectionLength))
         return Err;
 
     if (CurInfoSection.empty())
@@ -773,9 +805,9 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
           utostr(Version) + ")");
     }
 
-    writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection,
-                           CurStrOffsetSection, Header.Version, SectionLength,
-                           StrOffsetsOptValue);
+    writeStringsAndOffsets(Out, Strings, CurStrSection, CurStrOffsetSection,
+                           Header.Version, SectionLength, StrOffsetsOptValue,
+                           Inputs.size() == 1);
 
     for (auto Pair : SectionLength) {
       auto Index = getContributionIndex(Pair.first, IndexVersion);
@@ -803,7 +835,7 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
         ContributionOffsets[getContributionIndex(DW_SECT_INFO, IndexVersion)];
     if (CurCUIndexSection.empty()) {
       bool FoundCUUnit = false;
-      Out.switchSection(InfoSection);
+      Out.switchSection(DS_Info);
       for (StringRef Info : CurInfoSection) {
         uint64_t UnitOffset = 0;
         while (Info.size() > UnitOffset) {
@@ -869,7 +901,7 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
       if (IndexVersion == 2) {
         // Add types from the .debug_types section from DWARF < 5.
         if (Error Err = addAllTypesFromTypesSection(
-                Out, TypeIndexEntries, TypesSection, CurTypesSection, CurEntry,
+                Out, TypeIndexEntries, DS_Types, CurTypesSection, CurEntry,
                 ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES, 2)],
                 OverflowOptValue, AnySectionOverflow))
           return Err;
@@ -893,7 +925,7 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
                                   utostr(CUIndex.getVersion()) +
                                   " and expecting " + utostr(IndexVersion));
 
-    Out.switchSection(InfoSection);
+    Out.switchSection(DS_Info);
     for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) {
       auto *I = E.getContributions();
       if (!I)
@@ -939,12 +971,12 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
 
     if (!CurTUIndexSection.empty()) {
       llvm::DWARFSectionKind TUSectionKind;
-      MCSection *OutSection;
+      DWPSectionId OutSection;
       StringRef TypeInputSection;
       // Write type units into debug info section for DWARFv5.
       if (Version >= 5) {
         TUSectionKind = DW_SECT_INFO;
-        OutSection = InfoSection;
+        OutSection = DS_Info;
         TypeInputSection = DwpSingleInfoSection;
       } else {
         // Write type units into debug types section for DWARF < 5.
@@ -953,7 +985,7 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
               "multiple type unit sections in .dwp file");
 
         TUSectionKind = DW_SECT_EXT_TYPES;
-        OutSection = TypesSection;
+        OutSection = DS_Types;
         TypeInputSection = CurTypesSection.front();
       }
 
@@ -984,8 +1016,8 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
     // contribution to the info section, so we do not want to lie about it.
     ContributionOffsets[0] = 0;
   }
-  writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets,
-             TypeIndexEntries, IndexVersion);
+  writeIndex(Out, DS_TUIndex, ContributionOffsets, TypeIndexEntries,
+             IndexVersion);
 
   if (Version < 5) {
     // Lie about the type contribution for DWARF < 5. In DWARFv5 the type
@@ -995,9 +1027,177 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
     ContributionOffsets[0] = 1;
   }
 
-  writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
-             IndexEntries, IndexVersion);
+  writeIndex(Out, DS_CUIndex, ContributionOffsets, IndexEntries, IndexVersion);
+
+  // Write ELF output while input data is still alive (zero-copy chunks
+  // reference mmap'd input data held by the Objects vector above).
+  if (OutputOS)
+    return Out.writeELF(*OutputOS);
+
+  return Error::success();
+}
+
+//===----------------------------------------------------------------------===//
+// DWPWriter::writeELF — produce a minimal ELF64 relocatable object.
+//===----------------------------------------------------------------------===//
+
+Error DWPWriter::writeELF(raw_pwrite_stream &OS) {
+  support::endian::Writer Wr(OS, llvm::endianness::little);
+
+  // Section metadata table.
+  struct SectionMeta {
+    DWPSectionId Id;
+    const char *Name;
+    uint64_t Flags;
+    uint64_t EntSize;
+  };
+  static constexpr SectionMeta Meta[] = {
+      {DS_Loclists, ".debug_loclists.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Loc, ".debug_loc.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Abbrev, ".debug_abbrev.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Line, ".debug_line.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Rnglists, ".debug_rnglists.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Macro, ".debug_macro.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Str, ".debug_str.dwo", ELF::SHF_EXCLUDE | ELF::SHF_MERGE | ELF::SHF_STRINGS, 1},
+      {DS_StrOffsets, ".debug_str_offsets.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Info, ".debug_info.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_Types, ".debug_types.dwo", ELF::SHF_EXCLUDE, 0},
+      {DS_TUIndex, ".debug_tu_index", 0, 0},
+      {DS_CUIndex, ".debug_cu_index", 0, 0},
+  };
+
+  // Collect non-empty sections and build the section name string table.
+  struct OutputEntry {
+    const SectionData *Data;
+    const char *Name;
+    uint64_t Flags;
+    uint64_t EntSize;
+    uint32_t NameOffset;
+    uint64_t FileOffset; // filled in during layout
+  };
+  SmallVector<OutputEntry> Entries;
+
+  SmallString<256> Strtab;
+  Strtab.push_back('\0'); // null string at offset 0
+
+  for (const auto &M : Meta) {
+    if (Sections[M.Id].empty())
+      continue;
+    uint32_t NameOff = Strtab.size();
+    Strtab.append(M.Name);
+    Strtab.push_back('\0');
+    Entries.push_back({&Sections[M.Id], M.Name, M.Flags, M.EntSize, NameOff, 0});
+  }
+
+  // Add .strtab and .symtab name entries.
+  uint32_t StrtabNameOff = Strtab.size();
+  Strtab.append(".strtab");
+  Strtab.push_back('\0');
+  uint32_t SymtabNameOff = Strtab.size();
+  Strtab.append(".symtab");
+  Strtab.push_back('\0');
+
+  // Layout:
+  //   [ELF Header]              64 bytes
+  //   [section data...]         variable
+  //   [.strtab data]            variable
+  //   [padding to 8-byte align]
+  //   [.symtab data]            24 bytes (one null entry)
+  //   [padding to 8-byte align]
+  //   [Section Header Table]    64 * NumSections bytes
+
+  constexpr uint64_t EhdrSize = 64;
+  constexpr uint64_t ShdrSize = 64;
+  constexpr uint64_t SymEntSize = 24;
+
+  uint64_t Offset = EhdrSize;
+  for (auto &E : Entries) {
+    E.FileOffset = Offset;
+    Offset += E.Data->totalSize();
+  }
+
+  uint64_t StrtabOffset = Offset;
+  Offset += Strtab.size();
+
+  uint64_t SymtabOffset = alignTo(Offset, 8);
+  Offset = SymtabOffset + SymEntSize;
+
+  uint64_t SHTOffset = alignTo(Offset, 8);
+
+  // Section indices: [0]=null, [1..N]=data, [N+1]=strtab, [N+2]=symtab
+  uint32_t StrtabIdx = 1 + Entries.size();
+  uint32_t SymtabIdx = StrtabIdx + 1;
+  uint32_t NumSections = SymtabIdx + 1;
+
+  // --- Write ELF header ---
+  OS.write(ELF::ElfMagic, 4);
+  Wr.write<uint8_t>(ELF::ELFCLASS64);
+  Wr.write<uint8_t>(ELF::ELFDATA2LSB);
+  Wr.write<uint8_t>(ELF::EV_CURRENT);
+  Wr.write<uint8_t>(ELFOSABI);
+  OS.write_zeros(8);                    // EI_ABIVERSION + padding
+  Wr.write<uint16_t>(ELF::ET_REL);            // e_type
+  Wr.write<uint16_t>(ELFMachine);         // e_machine
+  Wr.write<uint32_t>(ELF::EV_CURRENT);         // e_version
+  Wr.write<uint64_t>(0);                  // e_entry
+  Wr.write<uint64_t>(0);                  // e_phoff
+  Wr.write<uint64_t>(SHTOffset);          // e_shoff
+  Wr.write<uint32_t>(0);                  // e_flags
+  Wr.write<uint16_t>(EhdrSize);           // e_ehsize
+  Wr.write<uint16_t>(0);                  // e_phentsize
+  Wr.write<uint16_t>(0);                  // e_phnum
+  Wr.write<uint16_t>(ShdrSize);           // e_shentsize
+  Wr.write<uint16_t>(NumSections);        // e_shnum
+  Wr.write<uint16_t>(StrtabIdx);          // e_shstrndx
+
+  // --- Write section data ---
+  for (const auto &E : Entries)
+    E.Data->writeTo(OS);
+
+  // --- Write .strtab ---
+  OS.write(Strtab.data(), Strtab.size());
+
+  // --- Pad + write .symtab (one null symbol entry) ---
+  OS.write_zeros(SymtabOffset - (StrtabOffset + Strtab.size()));
+  OS.write_zeros(SymEntSize);
+
+  // --- Pad for section header table ---
+  uint64_t CurPos = SymtabOffset + SymEntSize;
+  OS.write_zeros(SHTOffset - CurPos);
+
+  // Helper to write one section header.
+  auto writeSHdr = [&](uint32_t Name, uint32_t Type, uint64_t Flags,
+                       uint64_t FileOff, uint64_t Size, uint32_t Link,
+                       uint32_t Info, uint64_t Align, uint64_t EntSize) {
+    Wr.write<uint32_t>(Name);
+    Wr.write<uint32_t>(Type);
+    Wr.write<uint64_t>(Flags);
+    Wr.write<uint64_t>(0); // sh_addr
+    Wr.write<uint64_t>(FileOff);
+    Wr.write<uint64_t>(Size);
+    Wr.write<uint32_t>(Link);
+    Wr.write<uint32_t>(Info);
+    Wr.write<uint64_t>(Align);
+    Wr.write<uint64_t>(EntSize);
+  };
+
+  // [0] ELF::SHT_NULL
+  writeSHdr(0, ELF::SHT_NULL, 0, 0, 0, 0, 0, 0, 0);
+
+  // [1..N] data sections
+  for (const auto &E : Entries)
+    writeSHdr(E.NameOffset, ELF::SHT_PROGBITS, E.Flags, E.FileOffset,
+              E.Data->totalSize(), 0, 0, 1, E.EntSize);
+
+  // [N+1] .strtab
+  writeSHdr(StrtabNameOff, ELF::SHT_STRTAB, 0, StrtabOffset, Strtab.size(), 0, 0, 1,
+            0);
+
+  // [N+2] .symtab
+  writeSHdr(SymtabNameOff, ELF::SHT_SYMTAB, 0, SymtabOffset, SymEntSize, StrtabIdx,
+            1, 8, SymEntSize);
 
   return Error::success();
 }
+
 } // namespace llvm
diff --git a/llvm/tools/llvm-dwp/CMakeLists.txt b/llvm/tools/llvm-dwp/CMakeLists.txt
index d38385ce70831..bb8138bfe50ff 100644
--- a/llvm/tools/llvm-dwp/CMakeLists.txt
+++ b/llvm/tools/llvm-dwp/CMakeLists.txt
@@ -1,14 +1,9 @@
 set(LLVM_LINK_COMPONENTS
-  AllTargetsCodeGens
-  AllTargetsDescs
-  AllTargetsInfos
   DebugInfoDWARF
   DWP
-  MC
   Object
   Option
   Support
-  TargetParser
   )
 
 set(LLVM_TARGET_DEFINITIONS Opts.td)
diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp
index c0131111ed22d..d62322a8bb7cd 100644
--- a/llvm/tools/llvm-dwp/llvm-dwp.cpp
+++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -13,31 +13,19 @@
 #include "llvm/DWP/DWP.h"
 #include "llvm/DWP/DWPError.h"
 #include "llvm/DWP/DWPStringPool.h"
-#include "llvm/MC/MCAsmBackend.h"
-#include "llvm/MC/MCAsmInfo.h"
-#include "llvm/MC/MCCodeEmitter.h"
-#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCInstrInfo.h"
-#include "llvm/MC/MCObjectWriter.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.h"
-#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/ObjectFile.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/LLVMDriver.h"
 #include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include <optional>
 
 using namespace llvm;
 using namespace llvm::object;
 
-static mc::RegisterMCTargetOptionsFlags MCTargetOptionsFlags;
-
 // Command-line option boilerplate.
 namespace {
 enum ID {
@@ -112,14 +100,6 @@ static int error(const Twine &Error, const Twine &Context) {
   return 1;
 }
 
-static Expected<Triple> readTargetTriple(StringRef FileName) {
-  auto ErrOrObj = object::ObjectFile::createObjectFile(FileName);
-  if (!ErrOrObj)
-    return ErrOrObj.takeError();
-
-  return ErrOrObj->getBinary()->makeTriple();
-}
-
 int llvm_dwp_main(int argc, char **argv, const llvm::ToolContext &) {
   DwpOptTable Tbl;
   llvm::BumpPtrAllocator A;
@@ -192,11 +172,6 @@ int llvm_dwp_main(int argc, char **argv, const llvm::ToolContext &) {
   for (const llvm::opt::Arg *A : Args.filtered(OPT_INPUT))
     DWOFilenames.emplace_back(A->getValue());
 
-  llvm::InitializeAllTargetInfos();
-  llvm::InitializeAllTargetMCs();
-  llvm::InitializeAllTargets();
-  llvm::InitializeAllAsmPrinters();
-
   for (const auto &ExecFilename : ExecFilenames) {
     auto DWOs = getDWOFilenames(ExecFilename);
     if (!DWOs) {
@@ -249,68 +224,14 @@ int llvm_dwp_main(int argc, char **argv, const llvm::ToolContext &) {
     }
   }
 
-  std::string ErrorStr;
-  StringRef Context = "dwarf streamer init";
-
-  auto ErrOrTriple = readTargetTriple(DWOFilenames.front());
-  if (!ErrOrTriple) {
-    logAllUnhandledErrors(
-        handleErrors(ErrOrTriple.takeError(),
-                     [&](std::unique_ptr<ECError> EC) -> Error {
-                       return createFileError(DWOFilenames.front(),
-                                              Error(std::move(EC)));
-                     }),
-        WithColor::error());
-    return 1;
-  }
-
-  // Get the target.
-  const Target *TheTarget =
-      TargetRegistry::lookupTarget("", *ErrOrTriple, ErrorStr);
-  if (!TheTarget)
-    return error(ErrorStr, Context);
-  std::string TripleName = ErrOrTriple->getTriple();
-  Triple TheTriple(TripleName);
-
-  // Create all the MC Objects.
-  std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TheTriple));
-  if (!MRI)
-    return error(Twine("no register info for target ") + TripleName, Context);
-
-  MCTargetOptions MCOptions = llvm::mc::InitMCTargetOptionsFromFlags();
-  std::unique_ptr<MCAsmInfo> MAI(
-      TheTarget->createMCAsmInfo(*MRI, TheTriple, MCOptions));
-  if (!MAI)
-    return error("no asm info for target " + TripleName, Context);
-
-  std::unique_ptr<MCSubtargetInfo> MSTI(
-      TheTarget->createMCSubtargetInfo(TheTriple, "", ""));
-  if (!MSTI)
-    return error("no subtarget info for target " + TripleName, Context);
-
-  MCContext MC(*ErrOrTriple, MAI.get(), MRI.get(), MSTI.get());
-  std::unique_ptr<MCObjectFileInfo> MOFI(
-      TheTarget->createMCObjectFileInfo(MC, /*PIC=*/false));
-  MC.setObjectFileInfo(MOFI.get());
-  auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions);
-  if (!MAB)
-    return error("no asm backend for target " + TripleName, Context);
-
-  std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
-  if (!MII)
-    return error("no instr info info for target " + TripleName, Context);
-
-  MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, MC);
-  if (!MCE)
-    return error("no code emitter for target " + TripleName, Context);
-
   // Create the output file.
   std::error_code EC;
   ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
   std::optional<buffer_ostream> BOS;
   raw_pwrite_stream *OS;
   if (EC)
-    return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+    return error(Twine(OutputFilename) + ": " + EC.message(),
+                 "dwp output init");
   if (OutFile.os().supportsSeeking()) {
     OS = &OutFile.os();
   } else {
@@ -318,20 +239,15 @@ int llvm_dwp_main(int argc, char **argv, const llvm::ToolContext &) {
     OS = &*BOS;
   }
 
-  std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
-      *ErrOrTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
-      MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE),
-      *MSTI));
-  if (!MS)
-    return error("no object streamer for target " + TripleName, Context);
+  // Use DWPWriter for direct ELF output, bypassing MCStreamer.
+  DWPWriter Writer;
 
-  if (auto Err =
-          write(*MS, DWOFilenames, OverflowOptValue, Dwarf64StrOffsetsValue)) {
+  if (auto Err = write(Writer, DWOFilenames, OverflowOptValue,
+                       Dwarf64StrOffsetsValue, OS)) {
     logAllUnhandledErrors(std::move(Err), WithColor::error());
     return 1;
   }
 
-  MS->finish();
   OutFile.keep();
   return 0;
 }

>From 7d47157dc067a77d6d78ecb8ebf88f7bd431b595 Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Thu, 16 Apr 2026 10:12:04 -0700
Subject: [PATCH 2/2] [NFC] Extract shared ELF header/section-header writer
 utility

Extract the ELF header and section header writing code that was
duplicated between ELFObjectWriter and DWPWriter into shared
functions in llvm/BinaryFormat/ELFWriter.h.

Both MC's ELFObjectWriter::writeHeader()/writeSectionHeaderEntry()
and DWP's writeELF() wrote the same field-by-field ELF structures
independently. The new ELF::writeHeader() and ELF::writeSectionHeader()
functions provide a single implementation that both consumers call.
---
 llvm/include/llvm/BinaryFormat/ELFWriter.h | 38 ++++++++++
 llvm/lib/BinaryFormat/CMakeLists.txt       |  1 +
 llvm/lib/BinaryFormat/ELFWriter.cpp        | 63 ++++++++++++++++
 llvm/lib/DWP/CMakeLists.txt                |  1 +
 llvm/lib/DWP/DWP.cpp                       | 56 +++-----------
 llvm/lib/MC/ELFObjectWriter.cpp            | 87 ++++------------------
 6 files changed, 129 insertions(+), 117 deletions(-)
 create mode 100644 llvm/include/llvm/BinaryFormat/ELFWriter.h
 create mode 100644 llvm/lib/BinaryFormat/ELFWriter.cpp

diff --git a/llvm/include/llvm/BinaryFormat/ELFWriter.h b/llvm/include/llvm/BinaryFormat/ELFWriter.h
new file mode 100644
index 0000000000000..4a11621dba0d6
--- /dev/null
+++ b/llvm/include/llvm/BinaryFormat/ELFWriter.h
@@ -0,0 +1,38 @@
+//===- llvm/BinaryFormat/ELFWriter.h - ELF structure writer -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Shared utilities for writing ELF header and section header structures.
+// Used by both the MC ELFObjectWriter and the DWP direct ELF writer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_BINARYFORMAT_ELFWRITER_H
+#define LLVM_BINARYFORMAT_ELFWRITER_H
+
+#include "llvm/Support/EndianStream.h"
+#include <cstdint>
+
+namespace llvm {
+namespace ELF {
+
+/// Write an ELF file header (Elf32_Ehdr or Elf64_Ehdr) for an ET_REL object.
+void writeHeader(support::endian::Writer &W, bool Is64Bit, uint8_t OSABI,
+                 uint8_t ABIVersion, uint16_t EMachine, uint32_t EFlags,
+                 uint64_t SHOff, uint16_t SHNum, uint16_t SHStrNdx);
+
+/// Write a single ELF section header entry (Elf32_Shdr or Elf64_Shdr).
+void writeSectionHeader(support::endian::Writer &W, bool Is64Bit,
+                        uint32_t Name, uint32_t Type, uint64_t Flags,
+                        uint64_t Address, uint64_t Offset, uint64_t Size,
+                        uint32_t Link, uint32_t Info, uint64_t Alignment,
+                        uint64_t EntrySize);
+
+} // namespace ELF
+} // namespace llvm
+
+#endif // LLVM_BINARYFORMAT_ELFWRITER_H
diff --git a/llvm/lib/BinaryFormat/CMakeLists.txt b/llvm/lib/BinaryFormat/CMakeLists.txt
index 0c8af1e7a4565..df0c67e49fe99 100644
--- a/llvm/lib/BinaryFormat/CMakeLists.txt
+++ b/llvm/lib/BinaryFormat/CMakeLists.txt
@@ -4,6 +4,7 @@ add_llvm_component_library(LLVMBinaryFormat
   Dwarf.cpp
   DXContainer.cpp
   ELF.cpp
+  ELFWriter.cpp
   MachO.cpp
   Magic.cpp
   MsgPackDocument.cpp
diff --git a/llvm/lib/BinaryFormat/ELFWriter.cpp b/llvm/lib/BinaryFormat/ELFWriter.cpp
new file mode 100644
index 0000000000000..4a0fd79d0751e
--- /dev/null
+++ b/llvm/lib/BinaryFormat/ELFWriter.cpp
@@ -0,0 +1,63 @@
+//===- ELFWriter.cpp - Low-level ELF structure writer ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/ELFWriter.h"
+#include "llvm/BinaryFormat/ELF.h"
+
+using namespace llvm;
+
+static void writeWord(support::endian::Writer &W, bool Is64Bit, uint64_t Val) {
+  if (Is64Bit)
+    W.write<uint64_t>(Val);
+  else
+    W.write<uint32_t>(Val);
+}
+
+void ELF::writeHeader(support::endian::Writer &W, bool Is64Bit, uint8_t OSABI,
+                       uint8_t ABIVersion, uint16_t EMachine, uint32_t EFlags,
+                       uint64_t SHOff, uint16_t SHNum, uint16_t SHStrNdx) {
+  W.OS << ElfMagic;
+  W.OS << char(Is64Bit ? ELFCLASS64 : ELFCLASS32);
+  W.OS << char(W.Endian == llvm::endianness::little ? ELFDATA2LSB
+                                                    : ELFDATA2MSB);
+  W.OS << char(EV_CURRENT);
+  W.OS << char(OSABI);
+  W.OS << char(ABIVersion);
+  W.OS.write_zeros(EI_NIDENT - EI_PAD);
+
+  W.write<uint16_t>(ET_REL);
+  W.write<uint16_t>(EMachine);
+  W.write<uint32_t>(EV_CURRENT);
+  writeWord(W, Is64Bit, 0); // e_entry
+  writeWord(W, Is64Bit, 0); // e_phoff
+  writeWord(W, Is64Bit, SHOff);
+  W.write<uint32_t>(EFlags);
+  W.write<uint16_t>(Is64Bit ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr));
+  W.write<uint16_t>(0); // e_phentsize
+  W.write<uint16_t>(0); // e_phnum
+  W.write<uint16_t>(Is64Bit ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr));
+  W.write<uint16_t>(SHNum);
+  W.write<uint16_t>(SHStrNdx);
+}
+
+void ELF::writeSectionHeader(support::endian::Writer &W, bool Is64Bit,
+                              uint32_t Name, uint32_t Type, uint64_t Flags,
+                              uint64_t Address, uint64_t Offset, uint64_t Size,
+                              uint32_t Link, uint32_t Info, uint64_t Alignment,
+                              uint64_t EntrySize) {
+  W.write<uint32_t>(Name);
+  W.write<uint32_t>(Type);
+  writeWord(W, Is64Bit, Flags);
+  writeWord(W, Is64Bit, Address);
+  writeWord(W, Is64Bit, Offset);
+  writeWord(W, Is64Bit, Size);
+  W.write<uint32_t>(Link);
+  W.write<uint32_t>(Info);
+  writeWord(W, Is64Bit, Alignment);
+  writeWord(W, Is64Bit, EntrySize);
+}
diff --git a/llvm/lib/DWP/CMakeLists.txt b/llvm/lib/DWP/CMakeLists.txt
index 56199dec94108..fd4fd6be17acb 100644
--- a/llvm/lib/DWP/CMakeLists.txt
+++ b/llvm/lib/DWP/CMakeLists.txt
@@ -9,6 +9,7 @@ add_llvm_component_library(LLVMDWP
   intrinsics_gen
 
   LINK_COMPONENTS
+  BinaryFormat
   DebugInfoDWARF
   Object
   Support
diff --git a/llvm/lib/DWP/DWP.cpp b/llvm/lib/DWP/DWP.cpp
index 91afb930c9309..d4286566d9694 100644
--- a/llvm/lib/DWP/DWP.cpp
+++ b/llvm/lib/DWP/DWP.cpp
@@ -18,6 +18,7 @@
 #include "llvm/DWP/DWPError.h"
 #include "llvm/Object/Decompressor.h"
 #include "llvm/Object/ELFObjectFile.h"
+#include "llvm/BinaryFormat/ELFWriter.h"
 #include "llvm/Support/EndianStream.h"
 #include "llvm/Support/MathExtras.h"
 #include <limits>
@@ -1106,8 +1107,7 @@ Error DWPWriter::writeELF(raw_pwrite_stream &OS) {
   //   [padding to 8-byte align]
   //   [Section Header Table]    64 * NumSections bytes
 
-  constexpr uint64_t EhdrSize = 64;
-  constexpr uint64_t ShdrSize = 64;
+  constexpr uint64_t EhdrSize = sizeof(ELF::Elf64_Ehdr);
   constexpr uint64_t SymEntSize = 24;
 
   uint64_t Offset = EhdrSize;
@@ -1130,25 +1130,8 @@ Error DWPWriter::writeELF(raw_pwrite_stream &OS) {
   uint32_t NumSections = SymtabIdx + 1;
 
   // --- Write ELF header ---
-  OS.write(ELF::ElfMagic, 4);
-  Wr.write<uint8_t>(ELF::ELFCLASS64);
-  Wr.write<uint8_t>(ELF::ELFDATA2LSB);
-  Wr.write<uint8_t>(ELF::EV_CURRENT);
-  Wr.write<uint8_t>(ELFOSABI);
-  OS.write_zeros(8);                    // EI_ABIVERSION + padding
-  Wr.write<uint16_t>(ELF::ET_REL);            // e_type
-  Wr.write<uint16_t>(ELFMachine);         // e_machine
-  Wr.write<uint32_t>(ELF::EV_CURRENT);         // e_version
-  Wr.write<uint64_t>(0);                  // e_entry
-  Wr.write<uint64_t>(0);                  // e_phoff
-  Wr.write<uint64_t>(SHTOffset);          // e_shoff
-  Wr.write<uint32_t>(0);                  // e_flags
-  Wr.write<uint16_t>(EhdrSize);           // e_ehsize
-  Wr.write<uint16_t>(0);                  // e_phentsize
-  Wr.write<uint16_t>(0);                  // e_phnum
-  Wr.write<uint16_t>(ShdrSize);           // e_shentsize
-  Wr.write<uint16_t>(NumSections);        // e_shnum
-  Wr.write<uint16_t>(StrtabIdx);          // e_shstrndx
+  ELF::writeHeader(Wr, /*Is64Bit=*/true, ELFOSABI, /*ABIVersion=*/0, ELFMachine,
+                 /*EFlags=*/0, SHTOffset, NumSections, StrtabIdx);
 
   // --- Write section data ---
   for (const auto &E : Entries)
@@ -1165,37 +1148,22 @@ Error DWPWriter::writeELF(raw_pwrite_stream &OS) {
   uint64_t CurPos = SymtabOffset + SymEntSize;
   OS.write_zeros(SHTOffset - CurPos);
 
-  // Helper to write one section header.
-  auto writeSHdr = [&](uint32_t Name, uint32_t Type, uint64_t Flags,
-                       uint64_t FileOff, uint64_t Size, uint32_t Link,
-                       uint32_t Info, uint64_t Align, uint64_t EntSize) {
-    Wr.write<uint32_t>(Name);
-    Wr.write<uint32_t>(Type);
-    Wr.write<uint64_t>(Flags);
-    Wr.write<uint64_t>(0); // sh_addr
-    Wr.write<uint64_t>(FileOff);
-    Wr.write<uint64_t>(Size);
-    Wr.write<uint32_t>(Link);
-    Wr.write<uint32_t>(Info);
-    Wr.write<uint64_t>(Align);
-    Wr.write<uint64_t>(EntSize);
-  };
-
   // [0] ELF::SHT_NULL
-  writeSHdr(0, ELF::SHT_NULL, 0, 0, 0, 0, 0, 0, 0);
+  ELF::writeSectionHeader(Wr, true, 0, ELF::SHT_NULL, 0, 0, 0, 0, 0, 0, 0, 0);
 
   // [1..N] data sections
   for (const auto &E : Entries)
-    writeSHdr(E.NameOffset, ELF::SHT_PROGBITS, E.Flags, E.FileOffset,
-              E.Data->totalSize(), 0, 0, 1, E.EntSize);
+    ELF::writeSectionHeader(Wr, true, E.NameOffset, ELF::SHT_PROGBITS, E.Flags,
+                          0, E.FileOffset, E.Data->totalSize(), 0, 0, 1,
+                          E.EntSize);
 
   // [N+1] .strtab
-  writeSHdr(StrtabNameOff, ELF::SHT_STRTAB, 0, StrtabOffset, Strtab.size(), 0, 0, 1,
-            0);
+  ELF::writeSectionHeader(Wr, true, StrtabNameOff, ELF::SHT_STRTAB, 0, 0,
+                        StrtabOffset, Strtab.size(), 0, 0, 1, 0);
 
   // [N+2] .symtab
-  writeSHdr(SymtabNameOff, ELF::SHT_SYMTAB, 0, SymtabOffset, SymEntSize, StrtabIdx,
-            1, 8, SymEntSize);
+  ELF::writeSectionHeader(Wr, true, SymtabNameOff, ELF::SHT_SYMTAB, 0, 0,
+                        SymtabOffset, SymEntSize, StrtabIdx, 1, 8, SymEntSize);
 
   return Error::success();
 }
diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp
index c389ec9502510..d17a2f7e1fd07 100644
--- a/llvm/lib/MC/ELFObjectWriter.cpp
+++ b/llvm/lib/MC/ELFObjectWriter.cpp
@@ -28,6 +28,7 @@
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCFixup.h"
 #include "llvm/MC/MCObjectWriter.h"
+#include "llvm/BinaryFormat/ELFWriter.h"
 #include "llvm/MC/MCSection.h"
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCSymbol.h"
@@ -167,13 +168,6 @@ struct ELFWriter {
 
   MCContext &getContext() const { return Asm.getContext(); }
 
-  void writeWord(uint64_t Word) {
-    if (is64Bit())
-      W.write<uint64_t>(Word);
-    else
-      W.write<uint32_t>(Word);
-  }
-
   template <typename T> void write(T Val) {
     W.write(Val);
   }
@@ -283,64 +277,18 @@ bool ELFWriter::is64Bit() const {
 
 // Emit the ELF header.
 void ELFWriter::writeHeader() {
-  // ELF Header
-  // ----------
-  //
-  // Note
-  // ----
-  // emitWord method behaves differently for ELF32 and ELF64, writing
-  // 4 bytes in the former and 8 in the latter.
-
-  W.OS << ELF::ElfMagic; // e_ident[EI_MAG0] to e_ident[EI_MAG3]
-
-  W.OS << char(is64Bit() ? ELF::ELFCLASS64 : ELF::ELFCLASS32); // e_ident[EI_CLASS]
-
-  // e_ident[EI_DATA]
-  W.OS << char(W.Endian == llvm::endianness::little ? ELF::ELFDATA2LSB
-                                                    : ELF::ELFDATA2MSB);
-
-  W.OS << char(ELF::EV_CURRENT);        // e_ident[EI_VERSION]
-  // e_ident[EI_OSABI]
   uint8_t OSABI = OWriter.TargetObjectWriter->getOSABI();
-  W.OS << char(OSABI == ELF::ELFOSABI_NONE && OWriter.seenGnuAbi()
-                   ? int(ELF::ELFOSABI_GNU)
-                   : OSABI);
-  // e_ident[EI_ABIVERSION]
-  W.OS << char(OWriter.OverrideABIVersion
-                   ? *OWriter.OverrideABIVersion
-                   : OWriter.TargetObjectWriter->getABIVersion());
-
-  W.OS.write_zeros(ELF::EI_NIDENT - ELF::EI_PAD);
-
-  W.write<uint16_t>(ELF::ET_REL);             // e_type
-
-  W.write<uint16_t>(OWriter.TargetObjectWriter->getEMachine()); // e_machine = target
-
-  W.write<uint32_t>(ELF::EV_CURRENT);         // e_version
-  writeWord(0); // e_entry, no entry point in .o file
-  writeWord(0); // e_phoff, no program header for .o
-  writeWord(0); // e_shoff = sec hdr table off in bytes
-
-  // e_flags = whatever the target wants
-  W.write<uint32_t>(OWriter.getELFHeaderEFlags());
-
-  // e_ehsize = ELF header size
-  W.write<uint16_t>(is64Bit() ? sizeof(ELF::Elf64_Ehdr)
-                              : sizeof(ELF::Elf32_Ehdr));
-
-  W.write<uint16_t>(0);                  // e_phentsize = prog header entry size
-  W.write<uint16_t>(0);                  // e_phnum = # prog header entries = 0
-
-  // e_shentsize = Section header entry size
-  W.write<uint16_t>(is64Bit() ? sizeof(ELF::Elf64_Shdr)
-                              : sizeof(ELF::Elf32_Shdr));
-
-  // e_shnum     = # of section header ents
-  W.write<uint16_t>(0);
-
-  // e_shstrndx  = Section # of '.strtab'
+  if (OSABI == ELF::ELFOSABI_NONE && OWriter.seenGnuAbi())
+    OSABI = ELF::ELFOSABI_GNU;
+  uint8_t ABIVersion = OWriter.OverrideABIVersion
+                           ? *OWriter.OverrideABIVersion
+                           : OWriter.TargetObjectWriter->getABIVersion();
   assert(StringTableIndex < ELF::SHN_LORESERVE);
-  W.write<uint16_t>(StringTableIndex);
+  // SHOff, SHNum are written as 0 here and patched later via pwrite.
+  ELF::writeHeader(W, is64Bit(), OSABI, ABIVersion,
+                   OWriter.TargetObjectWriter->getEMachine(),
+                   OWriter.getELFHeaderEFlags(),
+                   /*SHOff=*/0, /*SHNum=*/0, StringTableIndex);
 }
 
 uint64_t ELFWriter::symbolValue(const MCSymbol &Sym) {
@@ -771,16 +719,9 @@ void ELFWriter::writeSectionHeaderEntry(uint32_t Name, uint32_t Type,
                                         uint32_t Link, uint32_t Info,
                                         MaybeAlign Alignment,
                                         uint64_t EntrySize) {
-  W.write<uint32_t>(Name);        // sh_name: index into string table
-  W.write<uint32_t>(Type);        // sh_type
-  writeWord(Flags);               // sh_flags
-  writeWord(Address);             // sh_addr
-  writeWord(Offset);              // sh_offset
-  writeWord(Size);                // sh_size
-  W.write<uint32_t>(Link);        // sh_link
-  W.write<uint32_t>(Info);        // sh_info
-  writeWord(Alignment ? Alignment->value() : 0); // sh_addralign
-  writeWord(EntrySize);                          // sh_entsize
+  ELF::writeSectionHeader(W, is64Bit(), Name, Type, Flags, Address, Offset,
+                          Size, Link, Info, Alignment ? Alignment->value() : 0,
+                          EntrySize);
 }
 
 template <bool Is64>



More information about the llvm-commits mailing list