| //===- 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_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H |
| #define LLVM_LIB_DWARFLINKERPARALLEL_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/DWARFLinkerParallel/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 dwarflinker_parallel { |
| |
| /// List of tracked debug tables. |
| enum class DebugSectionKind : uint8_t { |
| DebugInfo = 0, |
| DebugLine, |
| DebugFrame, |
| DebugRange, |
| DebugRngLists, |
| DebugLoc, |
| DebugLocLists, |
| DebugARanges, |
| DebugAbbrev, |
| DebugMacinfo, |
| DebugMacro, |
| DebugAddr, |
| DebugStr, |
| DebugLineStr, |
| DebugStrOffsets, |
| DebugPubNames, |
| DebugPubTypes, |
| DebugNames, |
| AppleNames, |
| AppleNamespaces, |
| AppleObjC, |
| AppleTypes, |
| NumberOfEnumEntries // must be last |
| }; |
| constexpr static size_t SectionKindsNum = |
| static_cast<size_t>(DebugSectionKind::NumberOfEnumEntries); |
| |
| /// Recognise the table name and match it with the DebugSectionKind. |
| std::optional<DebugSectionKind> parseDebugTableName(StringRef Name); |
| |
| /// Return the name of the section. |
| const StringLiteral &getSectionName(DebugSectionKind SectionKind); |
| |
| /// 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; |
| }; |
| |
| /// 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; |
| }; |
| |
| /// 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 { |
| friend OutputSections; |
| |
| SectionDescriptor(DebugSectionKind SectionKind, LinkingGlobalData &GlobalData, |
| dwarf::FormParams Format, support::endianness Endianess) |
| : OS(Contents), GlobalData(GlobalData), SectionKind(SectionKind), |
| Format(Format), Endianess(Endianess) { |
| ListDebugStrPatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugLineStrPatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugRangePatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugLocPatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugDieRefPatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugULEB128DieRefPatch.setAllocator(&GlobalData.getAllocator()); |
| ListDebugOffsetPatch.setAllocator(&GlobalData.getAllocator()); |
| } |
| |
| /// Erase whole section contents(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) |
| |
| /// Offsets to some fields are not known at the moment of noting patch. |
| /// In that case we remember pointers to patch offset to update them 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() { |
| if (SectionOffsetInsideAsmPrinterOutputStart == 0) |
| return StringRef(Contents.data(), Contents.size()); |
| |
| 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); |
| |
| /// Emit specified string value into the current section contents. |
| void emitString(dwarf::Form StringForm, const char *StringVal); |
| |
| /// Emit specified inplace string value into the current section contents. |
| void emitInplaceString(StringRef String) { |
| OS << GlobalData.translateString(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 section kind. |
| DebugSectionKind getKind() { return SectionKind; } |
| |
| /// Returns section name. |
| const StringLiteral &getName() const { return getSectionName(SectionKind); } |
| |
| /// Returns endianess used by section. |
| support::endianness getEndianess() const { return Endianess; } |
| |
| /// Returns FormParams used by section. |
| dwarf::FormParams getFormParams() const { return Format; } |
| |
| /// 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, |
| support::endianness Endianess) { |
| this->Format = Format; |
| this->Endianess = Endianess; |
| } |
| |
| LinkingGlobalData &GlobalData; |
| |
| /// The section kind. |
| DebugSectionKind SectionKind = DebugSectionKind::NumberOfEnumEntries; |
| |
| /// 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; |
| |
| /// Output format. |
| dwarf::FormParams Format = {4, 4, dwarf::DWARF32}; |
| support::endianness Endianess = support::endianness::little; |
| }; |
| |
| /// 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, |
| support::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()); |
| |
| 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; |
| } |
| |
| /// 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; |
| } |
| |
| /// Returns descriptor for the specified section of \p SectionKind. |
| /// If descriptor does not exist then creates it. |
| SectionDescriptor & |
| getOrCreateSectionDescriptor(DebugSectionKind SectionKind) { |
| return SectionDescriptors |
| .try_emplace(SectionKind, SectionKind, GlobalData, Format, Endianness) |
| .first->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) |
| 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); |
| |
| /// Endiannes for the sections. |
| support::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. |
| support::endianness Endianness = support::endian::system_endianness(); |
| |
| /// All keeping sections. |
| using SectionsSetTy = std::map<DebugSectionKind, SectionDescriptor>; |
| SectionsSetTy SectionDescriptors; |
| }; |
| |
| } // end of namespace dwarflinker_parallel |
| } // end namespace llvm |
| |
| #endif // LLVM_LIB_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H |