[llvm-dwp] Replace MCStreamer with direct ELF writer for zero-copy output (#192112)
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.
## Rationale
The MCStreamer pipeline copies all section data into 16KB MCDataFragment
blocks, accumulates them in memory, then writes everything out during
MCAssembler::Finish(). This can be cause lots of memory pressure and
slow down llvm-dwp.
For instance, on a 3.3GB DWP file, this translates to rougly ~3.3GB of
heap allocation and two full copies of the data.
The new DWPWriter avoids this via:
- 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. (minor optimization)
Bonus: this also removes all MC library dependencies from llvm-dwp
(AllTargetsCodeGens, AllTargetsDescs, AllTargetsInfos, MC,
TargetParser), reducing the binary size.
## Benchmark
I benchmarked on a 3.3GB production DWP file (8638 CUs, ~981MB
.debug_str.dwo):
Results:
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).
This commit is contained in:
@@ -4,18 +4,23 @@
|
||||
#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"
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm::object {
|
||||
class ObjectFile;
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
class raw_pwrite_stream;
|
||||
|
||||
enum OnCuIndexOverflow {
|
||||
HardStop,
|
||||
SoftStop,
|
||||
@@ -28,6 +33,115 @@ 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: ordered sequence of zero-copy chunks and inline
|
||||
/// data. emitBytes() adds zero-copy StringRef references, emitIntValue()
|
||||
/// appends to an inline buffer. When emitBytes() is called with pending
|
||||
/// inline data, the buffer is flushed to an owned block first to preserve
|
||||
/// the correct interleaving order in the output.
|
||||
struct SectionData {
|
||||
SmallVector<StringRef, 4> Chunks; // ordered segments (refs + flushed bufs)
|
||||
SmallVector<char, 0> Buffer; // pending inline data (emitIntValue)
|
||||
// Heap storage for flushed buffers. Uses std::deque so that push_back
|
||||
// does not invalidate existing elements (StringRefs point into these).
|
||||
std::deque<SmallVector<char, 0>> OwnedBuffers;
|
||||
|
||||
/// Flush pending Buffer data into Chunks as an owned block.
|
||||
void flushBuffer() {
|
||||
if (!Buffer.empty()) {
|
||||
OwnedBuffers.push_back(std::move(Buffer));
|
||||
auto &B = OwnedBuffers.back();
|
||||
Chunks.push_back(StringRef(B.data(), B.size()));
|
||||
Buffer = SmallVector<char, 0>();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
bool IsWASM = false;
|
||||
|
||||
public:
|
||||
DWPWriter() = default;
|
||||
|
||||
void setMachine(uint16_t Machine) { ELFMachine = Machine; }
|
||||
void setOSABI(uint8_t OSABI) { ELFOSABI = OSABI; }
|
||||
void setIsWASM(bool V) { IsWASM = V; }
|
||||
|
||||
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.
|
||||
/// Flushes any pending inline data first to preserve output order.
|
||||
void emitBytes(StringRef Data) {
|
||||
if (!Data.empty()) {
|
||||
auto &SD = Sections[CurrentSection];
|
||||
SD.flushBuffer();
|
||||
SD.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);
|
||||
Error writeWASM(raw_pwrite_stream &OS);
|
||||
Error write(raw_pwrite_stream &OS) {
|
||||
return IsWASM ? writeWASM(OS) : writeELF(OS);
|
||||
}
|
||||
};
|
||||
|
||||
struct UnitIndexEntry {
|
||||
DWARFUnitIndex::Entry::SectionContribution Contributions[8];
|
||||
std::string Name;
|
||||
@@ -73,45 +187,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
37
llvm/include/llvm/DWP/ELFWriter.h
Normal file
37
llvm/include/llvm/DWP/ELFWriter.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//===- llvm/DWP/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_DWP_ELFWRITER_H
|
||||
#define LLVM_DWP_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_DWP_ELFWRITER_H
|
||||
@@ -1,6 +1,7 @@
|
||||
add_llvm_component_library(LLVMDWP
|
||||
DWP.cpp
|
||||
DWPError.cpp
|
||||
ELFWriter.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/DWP
|
||||
@@ -10,7 +11,6 @@ add_llvm_component_library(LLVMDWP
|
||||
|
||||
LINK_COMPONENTS
|
||||
DebugInfoDWARF
|
||||
MC
|
||||
Object
|
||||
Support
|
||||
)
|
||||
|
||||
@@ -11,20 +11,22 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#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/DWP/ELFWriter.h"
|
||||
#include "llvm/Object/Decompressor.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Support/EndianStream.h"
|
||||
#include "llvm/Support/LEB128.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 +202,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 +251,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 +353,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 +440,30 @@ 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 +489,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 +555,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 +569,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 +623,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 +679,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 +732,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 +753,17 @@ Error write(MCStreamer &Out, ArrayRef<std::string> Inputs,
|
||||
auto &Obj = *ErrOrObj->getBinary();
|
||||
Objects.push_back(std::move(*ErrOrObj));
|
||||
|
||||
// Set output format metadata from the first input file.
|
||||
if (!MachineSet) {
|
||||
if (auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) {
|
||||
Out.setMachine(ELFObj->getEMachine());
|
||||
Out.setOSABI(ELFObj->getOS());
|
||||
} else if (Obj.isWasm()) {
|
||||
Out.setIsWASM(true);
|
||||
}
|
||||
MachineSet = true;
|
||||
}
|
||||
|
||||
UnitIndexEntry CurEntry = {};
|
||||
|
||||
StringRef CurStrSection;
|
||||
@@ -745,11 +781,9 @@ 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,
|
||||
KnownSections, Section, Out, UncompressedSections,
|
||||
ContributionOffsets, CurEntry, CurStrSection, CurStrOffsetSection,
|
||||
CurTypesSection, CurInfoSection, AbbrevSection, CurCUIndexSection,
|
||||
CurTUIndexSection, SectionLength))
|
||||
return Err;
|
||||
|
||||
@@ -773,9 +807,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 +837,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 +903,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 +927,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 +973,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 +987,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 +1018,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 +1029,207 @@ 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.write(*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 = sizeof(ELF::Elf64_Ehdr);
|
||||
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 ---
|
||||
ELF::writeHeader(Wr, /*Is64Bit=*/true, ELFOSABI, /*ABIVersion=*/0, ELFMachine,
|
||||
/*EFlags=*/0, SHTOffset, NumSections, StrtabIdx);
|
||||
|
||||
// --- 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);
|
||||
|
||||
// [0] ELF::SHT_NULL
|
||||
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)
|
||||
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
|
||||
ELF::writeSectionHeader(Wr, true, StrtabNameOff, ELF::SHT_STRTAB, 0, 0,
|
||||
StrtabOffset, Strtab.size(), 0, 0, 1, 0);
|
||||
|
||||
// [N+2] .symtab
|
||||
ELF::writeSectionHeader(Wr, true, SymtabNameOff, ELF::SHT_SYMTAB, 0, 0,
|
||||
SymtabOffset, SymEntSize, StrtabIdx, 1, 8,
|
||||
SymEntSize);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DWPWriter::writeWASM — produce a minimal WASM object with custom sections.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Error DWPWriter::writeWASM(raw_pwrite_stream &OS) {
|
||||
// Section name table (same names as ELF but without SHF_EXCLUDE flags).
|
||||
static constexpr struct {
|
||||
DWPSectionId Id;
|
||||
const char *Name;
|
||||
} Meta[] = {
|
||||
{DS_Loclists, ".debug_loclists.dwo"},
|
||||
{DS_Loc, ".debug_loc.dwo"},
|
||||
{DS_Abbrev, ".debug_abbrev.dwo"},
|
||||
{DS_Line, ".debug_line.dwo"},
|
||||
{DS_Rnglists, ".debug_rnglists.dwo"},
|
||||
{DS_Macro, ".debug_macro.dwo"},
|
||||
{DS_Str, ".debug_str.dwo"},
|
||||
{DS_StrOffsets, ".debug_str_offsets.dwo"},
|
||||
{DS_Info, ".debug_info.dwo"},
|
||||
{DS_Types, ".debug_types.dwo"},
|
||||
{DS_TUIndex, ".debug_tu_index"},
|
||||
{DS_CUIndex, ".debug_cu_index"},
|
||||
};
|
||||
|
||||
// WASM magic and version.
|
||||
OS.write("\0asm", 4);
|
||||
const uint8_t Version[] = {0x01, 0x00, 0x00, 0x00};
|
||||
OS.write(reinterpret_cast<const char *>(Version), 4);
|
||||
|
||||
// Emit each non-empty section as a WASM custom section (id=0).
|
||||
for (const auto &M : Meta) {
|
||||
const SectionData &SD = Sections[M.Id];
|
||||
if (SD.empty())
|
||||
continue;
|
||||
|
||||
size_t NameLen = strlen(M.Name);
|
||||
uint64_t PayloadSize = SD.totalSize();
|
||||
|
||||
// Custom section payload = ULEB128(name_len) + name + data.
|
||||
uint8_t NameLenEncoded[10];
|
||||
unsigned NameLenSize = encodeULEB128(NameLen, NameLenEncoded);
|
||||
uint64_t SectionPayloadSize = NameLenSize + NameLen + PayloadSize;
|
||||
|
||||
// Section header: id byte + ULEB128(section_payload_size).
|
||||
OS.write(0x00); // Custom section id
|
||||
uint8_t SizeEncoded[10];
|
||||
unsigned SizeLen = encodeULEB128(SectionPayloadSize, SizeEncoded);
|
||||
OS.write(reinterpret_cast<const char *>(SizeEncoded), SizeLen);
|
||||
|
||||
// Name
|
||||
OS.write(reinterpret_cast<const char *>(NameLenEncoded), NameLenSize);
|
||||
OS.write(M.Name, NameLen);
|
||||
|
||||
// Data
|
||||
SD.writeTo(OS);
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
63
llvm/lib/DWP/ELFWriter.cpp
Normal file
63
llvm/lib/DWP/ELFWriter.cpp
Normal file
@@ -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/DWP/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);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
# REQUIRES: webassembly-registered-target
|
||||
# RUN: llvm-mc %s -filetype obj -triple wasm32-unknown-unknown -o %t.o \
|
||||
# RUN: -split-dwarf-file=%t.dwo -dwarf-version=5
|
||||
# RUN: llvm-dwp %t.dwo -o %t.dwp
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsInfos
|
||||
DebugInfoDWARF
|
||||
DWP
|
||||
MC
|
||||
Object
|
||||
Option
|
||||
Support
|
||||
TargetParser
|
||||
)
|
||||
|
||||
set(LLVM_TARGET_DEFINITIONS Opts.td)
|
||||
|
||||
@@ -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, 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user