| //===- OutputSections.h -----------------------------------------*- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIB_DWARFLINKER_PARALLEL_OUTPUTSECTIONS_H |
| #define LLVM_LIB_DWARFLINKER_PARALLEL_OUTPUTSECTIONS_H |
| |
| #include "ArrayList.h" |
| #include "StringEntryToDwarfStringPoolEntryMap.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/CodeGen/DwarfStringPoolEntry.h" |
| #include "llvm/DWARFLinker/StringPool.h" |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" |
| #include "llvm/DebugInfo/DWARF/DWARFObject.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Endian.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/LEB128.h" |
| #include "llvm/Support/MemoryBufferRef.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <array> |
| #include <cstdint> |
| |
| namespace llvm { |
| namespace dwarf_linker { |
| namespace parallel { |
| |
| class TypeUnit; |
| |
| /// There are fields(sizes, offsets) which should be updated after |
| /// sections are generated. To remember offsets and related data |
| /// the descendants of SectionPatch structure should be used. |
| |
| struct SectionPatch { |
| uint64_t PatchOffset = 0; |
| }; |
| |
| /// This structure is used to update strings offsets into .debug_str. |
| struct DebugStrPatch : SectionPatch { |
| const StringEntry *String = nullptr; |
| }; |
| |
| /// This structure is used to update strings offsets into .debug_line_str. |
| struct DebugLineStrPatch : SectionPatch { |
| const StringEntry *String = nullptr; |
| }; |
| |
| /// This structure is used to update range list offset into |
| /// .debug_ranges/.debug_rnglists. |
| struct DebugRangePatch : SectionPatch { |
| /// Indicates patch which points to immediate compile unit's attribute. |
| bool IsCompileUnitRanges = false; |
| }; |
| |
| /// This structure is used to update location list offset into |
| /// .debug_loc/.debug_loclists. |
| struct DebugLocPatch : SectionPatch { |
| int64_t AddrAdjustmentValue = 0; |
| }; |
| |
| /// This structure is used to update offset with start of another section. |
| struct SectionDescriptor; |
| struct DebugOffsetPatch : SectionPatch { |
| DebugOffsetPatch(uint64_t PatchOffset, SectionDescriptor *SectionPtr, |
| bool AddLocalValue = false) |
| : SectionPatch({PatchOffset}), SectionPtr(SectionPtr, AddLocalValue) {} |
| |
| PointerIntPair<SectionDescriptor *, 1> SectionPtr; |
| }; |
| |
| /// This structure is used to update reference to the DIE. |
| struct DebugDieRefPatch : SectionPatch { |
| DebugDieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, CompileUnit *RefCU, |
| uint32_t RefIdx); |
| |
| PointerIntPair<CompileUnit *, 1> RefCU; |
| uint64_t RefDieIdxOrClonedOffset = 0; |
| }; |
| |
| /// This structure is used to update reference to the DIE of ULEB128 form. |
| struct DebugULEB128DieRefPatch : SectionPatch { |
| DebugULEB128DieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, |
| CompileUnit *RefCU, uint32_t RefIdx); |
| |
| PointerIntPair<CompileUnit *, 1> RefCU; |
| uint64_t RefDieIdxOrClonedOffset = 0; |
| }; |
| |
| /// This structure is used to update reference to the type DIE. |
| struct DebugDieTypeRefPatch : SectionPatch { |
| DebugDieTypeRefPatch(uint64_t PatchOffset, TypeEntry *RefTypeName); |
| |
| TypeEntry *RefTypeName = nullptr; |
| }; |
| |
| /// This structure is used to update reference to the type DIE. |
| struct DebugType2TypeDieRefPatch : SectionPatch { |
| DebugType2TypeDieRefPatch(uint64_t PatchOffset, DIE *Die, TypeEntry *TypeName, |
| TypeEntry *RefTypeName); |
| |
| DIE *Die = nullptr; |
| TypeEntry *TypeName = nullptr; |
| TypeEntry *RefTypeName = nullptr; |
| }; |
| |
| struct DebugTypeStrPatch : SectionPatch { |
| DebugTypeStrPatch(uint64_t PatchOffset, DIE *Die, TypeEntry *TypeName, |
| StringEntry *String); |
| |
| DIE *Die = nullptr; |
| TypeEntry *TypeName = nullptr; |
| StringEntry *String = nullptr; |
| }; |
| |
| struct DebugTypeLineStrPatch : SectionPatch { |
| DebugTypeLineStrPatch(uint64_t PatchOffset, DIE *Die, TypeEntry *TypeName, |
| StringEntry *String); |
| |
| DIE *Die = nullptr; |
| TypeEntry *TypeName = nullptr; |
| StringEntry *String = nullptr; |
| }; |
| |
| struct DebugTypeDeclFilePatch { |
| DebugTypeDeclFilePatch(DIE *Die, TypeEntry *TypeName, StringEntry *Directory, |
| StringEntry *FilePath); |
| |
| DIE *Die = nullptr; |
| TypeEntry *TypeName = nullptr; |
| StringEntry *Directory = nullptr; |
| StringEntry *FilePath = nullptr; |
| uint32_t FileID = 0; |
| }; |
| |
| /// Type for section data. |
| using OutSectionDataTy = SmallString<0>; |
| |
| /// Type for list of pointers to patches offsets. |
| using OffsetsPtrVector = SmallVector<uint64_t *>; |
| |
| class OutputSections; |
| |
| /// This structure is used to keep data of the concrete section. |
| /// Like data bits, list of patches, format. |
| struct SectionDescriptor : SectionDescriptorBase { |
| friend OutputSections; |
| |
| SectionDescriptor(DebugSectionKind SectionKind, LinkingGlobalData &GlobalData, |
| dwarf::FormParams Format, llvm::endianness Endianess) |
| : SectionDescriptorBase(SectionKind, Format, Endianess), OS(Contents), |
| ListDebugStrPatch(&GlobalData.getAllocator()), |
| ListDebugLineStrPatch(&GlobalData.getAllocator()), |
| ListDebugRangePatch(&GlobalData.getAllocator()), |
| ListDebugLocPatch(&GlobalData.getAllocator()), |
| ListDebugDieRefPatch(&GlobalData.getAllocator()), |
| ListDebugULEB128DieRefPatch(&GlobalData.getAllocator()), |
| ListDebugOffsetPatch(&GlobalData.getAllocator()), |
| ListDebugDieTypeRefPatch(&GlobalData.getAllocator()), |
| ListDebugType2TypeDieRefPatch(&GlobalData.getAllocator()), |
| ListDebugTypeStrPatch(&GlobalData.getAllocator()), |
| ListDebugTypeLineStrPatch(&GlobalData.getAllocator()), |
| ListDebugTypeDeclFilePatch(&GlobalData.getAllocator()), |
| GlobalData(GlobalData) {} |
| |
| /// Erase whole section content(data bits, list of patches). |
| void clearAllSectionData(); |
| |
| /// Erase only section output data bits. |
| void clearSectionContent(); |
| |
| /// When objects(f.e. compile units) are glued into the single file, |
| /// the debug sections corresponding to the concrete object are assigned |
| /// with offsets inside the whole file. This field keeps offset |
| /// to the debug section, corresponding to this object. |
| uint64_t StartOffset = 0; |
| |
| /// Stream which stores data to the Contents. |
| raw_svector_ostream OS; |
| |
| /// Section patches. |
| #define ADD_PATCHES_LIST(T) \ |
| T ¬ePatch(const T &Patch) { return List##T.add(Patch); } \ |
| ArrayList<T> List##T; |
| |
| ADD_PATCHES_LIST(DebugStrPatch) |
| ADD_PATCHES_LIST(DebugLineStrPatch) |
| ADD_PATCHES_LIST(DebugRangePatch) |
| ADD_PATCHES_LIST(DebugLocPatch) |
| ADD_PATCHES_LIST(DebugDieRefPatch) |
| ADD_PATCHES_LIST(DebugULEB128DieRefPatch) |
| ADD_PATCHES_LIST(DebugOffsetPatch) |
| ADD_PATCHES_LIST(DebugDieTypeRefPatch) |
| ADD_PATCHES_LIST(DebugType2TypeDieRefPatch) |
| ADD_PATCHES_LIST(DebugTypeStrPatch) |
| ADD_PATCHES_LIST(DebugTypeLineStrPatch) |
| ADD_PATCHES_LIST(DebugTypeDeclFilePatch) |
| |
| /// While creating patches, offsets to attributes may be partially |
| /// unknown(because size of abbreviation number is unknown). In such case we |
| /// remember patch itself and pointer to patch application offset to add size |
| /// of abbreviation number later. |
| template <typename T> |
| void notePatchWithOffsetUpdate(const T &Patch, |
| OffsetsPtrVector &PatchesOffsetsList) { |
| PatchesOffsetsList.emplace_back(¬ePatch(Patch).PatchOffset); |
| } |
| |
| /// Some sections are emitted using AsmPrinter. In that case "Contents" |
| /// member of SectionDescriptor contains elf file. This method searches |
| /// for section data inside elf file and remember offset to it. |
| void setSizesForSectionCreatedByAsmPrinter(); |
| |
| /// Returns section content. |
| StringRef getContents() override { |
| if (SectionOffsetInsideAsmPrinterOutputStart == 0) |
| return Contents; |
| |
| return Contents.slice(SectionOffsetInsideAsmPrinterOutputStart, |
| SectionOffsetInsideAsmPrinterOutputEnd); |
| } |
| |
| /// Emit unit length into the current section contents. |
| void emitUnitLength(uint64_t Length) { |
| maybeEmitDwarf64Mark(); |
| emitIntVal(Length, getFormParams().getDwarfOffsetByteSize()); |
| } |
| |
| /// Emit DWARF64 mark into the current section contents. |
| void maybeEmitDwarf64Mark() { |
| if (getFormParams().Format != dwarf::DWARF64) |
| return; |
| emitIntVal(dwarf::DW_LENGTH_DWARF64, 4); |
| } |
| |
| /// Emit specified offset value into the current section contents. |
| void emitOffset(uint64_t Val) { |
| emitIntVal(Val, getFormParams().getDwarfOffsetByteSize()); |
| } |
| |
| /// Emit specified integer value into the current section contents. |
| void emitIntVal(uint64_t Val, unsigned Size); |
| |
| void emitString(dwarf::Form StringForm, const char *StringVal); |
| |
| void emitBinaryData(llvm::StringRef Data); |
| |
| /// Emit specified inplace string value into the current section contents. |
| void emitInplaceString(StringRef String) { |
| OS << String; |
| emitIntVal(0, 1); |
| } |
| |
| /// Emit string placeholder into the current section contents. |
| void emitStringPlaceholder() { |
| // emit bad offset which should be updated later. |
| emitOffset(0xBADDEF); |
| } |
| |
| /// Write specified \p Value of \p AttrForm to the \p PatchOffset. |
| void apply(uint64_t PatchOffset, dwarf::Form AttrForm, uint64_t Val); |
| |
| /// Returns integer value of \p Size located by specified \p PatchOffset. |
| uint64_t getIntVal(uint64_t PatchOffset, unsigned Size); |
| |
| protected: |
| /// Writes integer value \p Val of \p Size by specified \p PatchOffset. |
| void applyIntVal(uint64_t PatchOffset, uint64_t Val, unsigned Size); |
| |
| /// Writes integer value \p Val of ULEB128 format by specified \p PatchOffset. |
| void applyULEB128(uint64_t PatchOffset, uint64_t Val); |
| |
| /// Writes integer value \p Val of SLEB128 format by specified \p PatchOffset. |
| void applySLEB128(uint64_t PatchOffset, uint64_t Val); |
| |
| /// Sets output format. |
| void setOutputFormat(dwarf::FormParams Format, llvm::endianness Endianess) { |
| this->Format = Format; |
| this->Endianess = Endianess; |
| } |
| |
| LinkingGlobalData &GlobalData; |
| |
| /// Section data bits. |
| OutSectionDataTy Contents; |
| |
| /// Some sections are generated using AsmPrinter. The real section data |
| /// located inside elf file in that case. Following fields points to the |
| /// real section content inside elf file. |
| size_t SectionOffsetInsideAsmPrinterOutputStart = 0; |
| size_t SectionOffsetInsideAsmPrinterOutputEnd = 0; |
| }; |
| |
| /// This class keeps contents and offsets to the debug sections. Any objects |
| /// which is supposed to be emitted into the debug sections should use this |
| /// class to track debug sections offsets and keep sections data. |
| class OutputSections { |
| public: |
| OutputSections(LinkingGlobalData &GlobalData) : GlobalData(GlobalData) {} |
| |
| /// Sets output format for all keeping sections. |
| void setOutputFormat(dwarf::FormParams Format, llvm::endianness Endianness) { |
| this->Format = Format; |
| this->Endianness = Endianness; |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// The descriptor should already be created. The llvm_unreachable |
| /// would be raised if it is not. |
| const SectionDescriptor & |
| getSectionDescriptor(DebugSectionKind SectionKind) const { |
| SectionsSetTy::const_iterator It = SectionDescriptors.find(SectionKind); |
| |
| if (It == SectionDescriptors.end()) |
| llvm_unreachable( |
| formatv("Section {0} does not exist", getSectionName(SectionKind)) |
| .str() |
| .c_str()); |
| |
| return *It->second; |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// The descriptor should already be created. The llvm_unreachable |
| /// would be raised if it is not. |
| SectionDescriptor &getSectionDescriptor(DebugSectionKind SectionKind) { |
| SectionsSetTy::iterator It = SectionDescriptors.find(SectionKind); |
| |
| if (It == SectionDescriptors.end()) |
| llvm_unreachable( |
| formatv("Section {0} does not exist", getSectionName(SectionKind)) |
| .str() |
| .c_str()); |
| |
| assert(It->second.get() != nullptr); |
| |
| return *It->second; |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// Returns std::nullopt if section descriptor is not created yet. |
| std::optional<const SectionDescriptor *> |
| tryGetSectionDescriptor(DebugSectionKind SectionKind) const { |
| SectionsSetTy::const_iterator It = SectionDescriptors.find(SectionKind); |
| |
| if (It == SectionDescriptors.end()) |
| return std::nullopt; |
| |
| return It->second.get(); |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// Returns std::nullopt if section descriptor is not created yet. |
| std::optional<SectionDescriptor *> |
| tryGetSectionDescriptor(DebugSectionKind SectionKind) { |
| SectionsSetTy::iterator It = SectionDescriptors.find(SectionKind); |
| |
| if (It == SectionDescriptors.end()) |
| return std::nullopt; |
| |
| return It->second.get(); |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// If descriptor does not exist then creates it. |
| SectionDescriptor & |
| getOrCreateSectionDescriptor(DebugSectionKind SectionKind) { |
| auto [It, Inserted] = SectionDescriptors.try_emplace(SectionKind); |
| |
| if (Inserted) |
| It->second = std::make_shared<SectionDescriptor>(SectionKind, GlobalData, |
| Format, Endianness); |
| |
| return *It->second; |
| } |
| |
| /// Erases data of all sections. |
| void eraseSections() { |
| for (auto &Section : SectionDescriptors) |
| Section.second->clearAllSectionData(); |
| } |
| |
| /// Enumerate all sections and call \p Handler for each. |
| void forEach(function_ref<void(SectionDescriptor &)> Handler) { |
| for (auto &Section : SectionDescriptors) { |
| assert(Section.second.get() != nullptr); |
| Handler(*(Section.second)); |
| } |
| } |
| |
| /// Enumerate all sections and call \p Handler for each. |
| void forEach( |
| function_ref<void(std::shared_ptr<SectionDescriptor> Section)> Handler) { |
| for (auto &Section : SectionDescriptors) |
| Handler(Section.second); |
| } |
| |
| /// Enumerate all sections, for each section set current offset |
| /// (kept by \p SectionSizesAccumulator), update current offset with section |
| /// length. |
| void assignSectionsOffsetAndAccumulateSize( |
| std::array<uint64_t, SectionKindsNum> &SectionSizesAccumulator) { |
| for (auto &Section : SectionDescriptors) { |
| Section.second->StartOffset = |
| SectionSizesAccumulator[static_cast<uint8_t>( |
| Section.second->getKind())]; |
| SectionSizesAccumulator[static_cast<uint8_t>( |
| Section.second->getKind())] += Section.second->getContents().size(); |
| } |
| } |
| |
| /// Enumerate all sections, for each section apply all section patches. |
| void applyPatches(SectionDescriptor &Section, |
| StringEntryToDwarfStringPoolEntryMap &DebugStrStrings, |
| StringEntryToDwarfStringPoolEntryMap &DebugLineStrStrings, |
| TypeUnit *TypeUnitPtr); |
| |
| /// Endiannes for the sections. |
| llvm::endianness getEndianness() const { return Endianness; } |
| |
| /// Return DWARF version. |
| uint16_t getVersion() const { return Format.Version; } |
| |
| /// Return size of header of debug_info table. |
| uint16_t getDebugInfoHeaderSize() const { |
| return Format.Version >= 5 ? 12 : 11; |
| } |
| |
| /// Return size of header of debug_ table. |
| uint16_t getDebugAddrHeaderSize() const { |
| assert(Format.Version >= 5); |
| return Format.Format == dwarf::DwarfFormat::DWARF32 ? 8 : 16; |
| } |
| |
| /// Return size of header of debug_str_offsets table. |
| uint16_t getDebugStrOffsetsHeaderSize() const { |
| assert(Format.Version >= 5); |
| return Format.Format == dwarf::DwarfFormat::DWARF32 ? 8 : 16; |
| } |
| |
| /// Return size of address. |
| const dwarf::FormParams &getFormParams() const { return Format; } |
| |
| protected: |
| LinkingGlobalData &GlobalData; |
| |
| /// Format for sections. |
| dwarf::FormParams Format = {4, 4, dwarf::DWARF32}; |
| |
| /// Endiannes for sections. |
| llvm::endianness Endianness = llvm::endianness::native; |
| |
| /// All keeping sections. |
| using SectionsSetTy = |
| std::map<DebugSectionKind, std::shared_ptr<SectionDescriptor>>; |
| SectionsSetTy SectionDescriptors; |
| }; |
| |
| } // end of namespace parallel |
| } // end of namespace dwarf_linker |
| } // end of namespace llvm |
| |
| #endif // LLVM_LIB_DWARFLINKER_PARALLEL_OUTPUTSECTIONS_H |