[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