#ifndef LLVM_DWP_DWP_H #define LLVM_DWP_DWP_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/Object/ObjectFile.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include #include namespace llvm::object { class ObjectFile; } namespace llvm { class raw_pwrite_stream; enum OnCuIndexOverflow { HardStop, SoftStop, Continue, }; enum Dwarf64StrOffsetsPromotion { Disabled, ///< Don't do any conversion of .debug_str_offsets tables. Enabled, ///< Convert any .debug_str_offsets tables to DWARF64 if needed. 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. /// /// 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 Chunks; // ordered segments (refs + flushed bufs) SmallVector 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> 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(); } } 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 &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(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); } }; class DWPStringPool { DWPWriter &Out; DenseMap Pool; uint64_t Offset = 0; public: DWPStringPool(DWPWriter &Out) : Out(Out) {} uint64_t getOffset(const char *Str, unsigned Length) { assert(strlen(Str) + 1 == Length && "Ensure length hint is correct"); StringRef Key(Str, Length); auto Pair = Pool.insert(std::make_pair(Key, Offset)); if (Pair.second) { Out.emitBytes(Key); Offset += Length; } return Pair.first->second; } }; struct UnitIndexEntry { DWARFUnitIndex::Entry::SectionContribution Contributions[8]; std::string Name; std::string DWOName; StringRef DWPName; }; // Holds data for Skeleton, Split Compilation, and Type Unit Headers (only in // v5) as defined in Dwarf 5 specification, 7.5.1.2, 7.5.1.3 and Dwarf 4 // specification 7.5.1.1. struct InfoSectionUnitHeader { // unit_length field. Note that the type is uint64_t even in 32-bit dwarf. uint64_t Length = 0; // version field. uint16_t Version = 0; // unit_type field. Initialized only if Version >= 5. uint8_t UnitType = 0; // address_size field. uint8_t AddrSize = 0; // debug_abbrev_offset field. Note that the type is uint64_t even in 32-bit // dwarf. It is assumed to be 0. uint64_t DebugAbbrevOffset = 0; // dwo_id field. This resides in the header only if Version >= 5. // In earlier versions, it is read from DW_AT_GNU_dwo_id. std::optional Signature; // Derived from the length of Length field. dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32; // The size of the Header in bytes. This is derived while parsing the header, // and is stored as a convenience. uint8_t HeaderSize = 0; }; struct CompileUnitIdentifiers { uint64_t Signature = 0; const char *Name = ""; const char *DWOName = ""; }; LLVM_ABI Error write(DWPWriter &Out, ArrayRef Inputs, OnCuIndexOverflow OverflowOptValue, Dwarf64StrOffsetsPromotion StrOffsetsOptValue, raw_pwrite_stream *OS = nullptr); typedef std::vector> SectionLengths; LLVM_ABI Expected parseInfoSectionUnitHeader(StringRef Info); } // namespace llvm #endif // LLVM_DWP_DWP_H