//===- lib/ReaderWriter/ELF/DefaultLayout.h -------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H #define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H #include "Chunk.h" #include "HeaderChunks.h" #include "Layout.h" #include "SectionChunks.h" #include "SegmentChunks.h" #include "lld/Core/LinkerOptions.h" #include "lld/Core/STDExtras.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include #include #include namespace lld { namespace elf { /// \brief The DefaultLayout class is used by the Writer to arrange /// sections and segments in the order determined by the target ELF /// format. The writer creates a single instance of the DefaultLayout /// class template class DefaultLayout : public Layout { public: // The order in which the sections appear in the output file // If its determined, that the layout needs to change // just changing the order of enumerations would essentially // change the layout in the output file // Change the enumerations so that Target can override and stick // a section anywhere it wants to enum DefaultSectionOrder { ORDER_NOT_DEFINED = 0, ORDER_INTERP = 10, ORDER_NOTE = 20, ORDER_HASH = 30, ORDER_DYNAMIC_SYMBOLS = 40, ORDER_DYNAMIC_STRINGS = 50, ORDER_DYNAMIC_RELOCS = 52, ORDER_DYNAMIC_PLT_RELOCS = 54, ORDER_INIT = 60, ORDER_PLT = 70, ORDER_TEXT = 80, ORDER_FINI = 90, ORDER_REL = 95, ORDER_RODATA = 100, ORDER_EH_FRAME = 110, ORDER_EH_FRAMEHDR = 120, ORDER_TDATA = 124, ORDER_TBSS = 128, ORDER_CTORS = 130, ORDER_DTORS = 140, ORDER_INIT_ARRAY = 150, ORDER_FINI_ARRAY = 160, ORDER_DYNAMIC = 170, ORDER_GOT = 180, ORDER_GOT_PLT = 190, ORDER_DATA = 200, ORDER_BSS = 210, ORDER_OTHER = 220, ORDER_SECTION_STRINGS = 230, ORDER_SYMBOL_TABLE = 240, ORDER_STRING_TABLE = 250, ORDER_SECTION_HEADERS = 260 }; public: // The Key used for creating Sections // The sections are created using // SectionName, contentPermissions struct SectionKey { SectionKey(StringRef name, DefinedAtom::ContentPermissions perm) : _name(name), _perm(perm) { } // Data members StringRef _name; DefinedAtom::ContentPermissions _perm; }; struct SectionKeyHash { int64_t operator()(const SectionKey &k) const { return llvm::hash_combine(k._name, k._perm); } }; struct SectionKeyEq { bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { return ((lhs._name == rhs._name) && (lhs._perm == rhs._perm)); } }; typedef typename std::vector *>::iterator ChunkIter; // The additional segments are used to figure out // if there is a segment by that type already created // For example : PT_TLS, we have two sections .tdata/.tbss // that are part of PT_TLS, we need to create this additional // segment only once typedef int64_t AdditionalSegmentKey; // The segments are created using // SegmentName, Segment flags typedef std::pair SegmentKey; // HashKey for the Segment class SegmentHashKey { public: int64_t operator() (const SegmentKey &k) const { // k.first = SegmentName // k.second = SegmentFlags return llvm::hash_combine(k.first, k.second); } }; // Merged Sections contain the map of Sectionnames to a vector of sections, // that have been merged to form a single section typedef std::map *> MergedSectionMapT; typedef typename std::vector *>::iterator MergedSectionIter; typedef std::unordered_map *, SectionKeyHash, SectionKeyEq> SectionMapT; typedef std::map *> AdditionalSegmentMapT; typedef std::unordered_map *, SegmentHashKey> SegmentMapT; /// \brief find a absolute atom pair given a absolute atom name struct FindByName { const std::string _name; FindByName(StringRef name) : _name(name) {} bool operator()(const AtomLayout *j) { return j->_atom->name() == _name; } }; typedef typename std::vector::iterator AbsoluteAtomIterT; DefaultLayout(const ELFTargetInfo &ti) : _targetInfo(ti) {} /// \brief Return the section order for a input section virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, int32_t contentPermissions); /// \brief This maps the input sections to the output section names virtual StringRef getSectionName(StringRef name, const int32_t contentType, const int32_t contentPermissions); /// \brief Gets or creates a section. AtomSection *getSection( StringRef name, int32_t contentType, DefinedAtom::ContentPermissions contentPermissions); /// \brief Gets the segment for a output section virtual Layout::SegmentType getSegmentType(Section *section) const; /// \brief Returns true/false depending on whether the section has a Output // segment or not static bool hasOutputSegment(Section *section); // Adds an atom to the section virtual ErrorOr addAtom(const Atom *atom); /// \brief Find an output Section given a section name. MergedSections *findOutputSection(StringRef name) { auto iter = _mergedSectionMap.find(name); if (iter == _mergedSectionMap.end()) return nullptr; return iter->second; } /// \brief find a absolute atom given a name AbsoluteAtomIterT findAbsoluteAtom(StringRef name) { return std::find_if(_absoluteAtoms.begin(), _absoluteAtoms.end(), FindByName(name)); } // Merge sections with the same name into a MergedSections void mergeSimiliarSections(); void assignSectionsToSegments(); void assignVirtualAddress(); void assignOffsetsForMiscSections(); void assignFileOffsets(); /// Inline functions inline range absoluteAtoms() { return _absoluteAtoms; } inline void addSection(Chunk *c) { _sections.push_back(c); } inline void finalize() { for (auto &si : _sections) si->finalize(); } inline void doPreFlight() { for (auto &si : _sections) si->doPreFlight(); } inline bool findAtomAddrByName(StringRef name, uint64_t &addr) { for (auto sec : _sections) if (auto section = dyn_cast >(sec)) if (section->findAtomAddrByName(name, addr)) return true; return false; } inline void setHeader(Header *e) { _header = e; } inline void setProgramHeader(ProgramHeader *p) { _programHeader = p; } inline range mergedSections() { return _mergedSections; } inline range sections() { return _sections; } inline range segments() { return _segments; } inline Header *getHeader() { return _header; } inline ProgramHeader *getProgramHeader() { return _programHeader; } bool hasDynamicRelocationTable() const { return !!_dynamicRelocationTable; } bool hasPLTRelocationTable() const { return !!_pltRelocationTable; } /// \brief Get or create the dynamic relocation table. All relocations in this /// table are processed at startup. RelocationTable *getDynamicRelocationTable() { if (!_dynamicRelocationTable) { _dynamicRelocationTable.reset(new (_allocator) RelocationTable( _targetInfo, ".rela.dyn", ORDER_REL)); addSection(_dynamicRelocationTable.get()); } return _dynamicRelocationTable.get(); } /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. RelocationTable *getPLTRelocationTable() { if (!_pltRelocationTable) { _pltRelocationTable.reset(new (_allocator) RelocationTable( _targetInfo, ".rela.plt", ORDER_REL)); addSection(_pltRelocationTable.get()); } return _pltRelocationTable.get(); } uint64_t getTLSSize() const { for (const auto &phdr : *_programHeader) if (phdr->p_type == llvm::ELF::PT_TLS) return phdr->p_memsz; return 0; } protected: /// \brief Allocate a new section. virtual AtomSection *createSection( StringRef name, int32_t contentType, DefinedAtom::ContentPermissions contentPermissions, SectionOrder sectionOrder); private: llvm::BumpPtrAllocator _allocator; SectionMapT _sectionMap; MergedSectionMapT _mergedSectionMap; AdditionalSegmentMapT _additionalSegmentMap; SegmentMapT _segmentMap; std::vector *> _sections; std::vector *> _segments; std::vector *> _mergedSections; Header *_header; ProgramHeader *_programHeader; LLD_UNIQUE_BUMP_PTR(RelocationTable) _dynamicRelocationTable; LLD_UNIQUE_BUMP_PTR(RelocationTable) _pltRelocationTable; std::vector _absoluteAtoms; const ELFTargetInfo &_targetInfo; }; template Layout::SectionOrder DefaultLayout::getSectionOrder( StringRef name, int32_t contentType, int32_t contentPermissions) { switch (contentType) { case DefinedAtom::typeResolver: case DefinedAtom::typeCode: return llvm::StringSwitch(name) .StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR) .StartsWith(".eh_frame", ORDER_EH_FRAME) .StartsWith(".init", ORDER_INIT) .StartsWith(".fini", ORDER_FINI) .StartsWith(".hash", ORDER_HASH) .Default(ORDER_TEXT); case DefinedAtom::typeConstant: return ORDER_RODATA; case DefinedAtom::typeData: case DefinedAtom::typeDataFast: return llvm::StringSwitch(name) .StartsWith(".init_array", ORDER_INIT_ARRAY) .Default(ORDER_DATA); case DefinedAtom::typeZeroFill: case DefinedAtom::typeZeroFillFast: return ORDER_BSS; case DefinedAtom::typeGOT: return ORDER_GOT; case DefinedAtom::typeStub: return ORDER_PLT; case DefinedAtom::typeTLVInitialData: return ORDER_TDATA; case DefinedAtom::typeTLVInitialZeroFill: return ORDER_TBSS; default: // If we get passed in a section push it to OTHER if (contentPermissions == DefinedAtom::perm___) return ORDER_OTHER; return ORDER_NOT_DEFINED; } } /// \brief This maps the input sections to the output section names template StringRef DefaultLayout::getSectionName( StringRef name, const int32_t contentType, const int32_t contentPermissions) { if ((contentType == DefinedAtom::typeZeroFill) || (contentType == DefinedAtom::typeZeroFillFast)) return ".bss"; if (name.startswith(".text")) return ".text"; if (name.startswith(".rodata")) return ".rodata"; if (name.startswith(".tdata")) return ".tdata"; if (name.startswith(".tbss")) return ".tbss"; return name; } /// \brief Gets the segment for a output section template Layout::SegmentType DefaultLayout::getSegmentType( Section *section) const { switch (section->order()) { case ORDER_INTERP: return llvm::ELF::PT_INTERP; case ORDER_TEXT: case ORDER_HASH: case ORDER_DYNAMIC_SYMBOLS: case ORDER_DYNAMIC_STRINGS: case ORDER_REL: case ORDER_INIT: case ORDER_PLT: case ORDER_FINI: case ORDER_RODATA: case ORDER_EH_FRAME: case ORDER_EH_FRAMEHDR: return llvm::ELF::PT_LOAD; case ORDER_NOTE: return llvm::ELF::PT_NOTE; case ORDER_DYNAMIC: return llvm::ELF::PT_DYNAMIC; case ORDER_CTORS: case ORDER_DTORS: return llvm::ELF::PT_GNU_RELRO; case ORDER_GOT: case ORDER_GOT_PLT: case ORDER_DATA: case ORDER_BSS: case ORDER_INIT_ARRAY: case ORDER_FINI_ARRAY: return llvm::ELF::PT_LOAD; case ORDER_TDATA: case ORDER_TBSS: return llvm::ELF::PT_TLS; default: return llvm::ELF::PT_NULL; } } template bool DefaultLayout::hasOutputSegment(Section *section) { switch (section->order()) { case ORDER_INTERP: case ORDER_HASH: case ORDER_DYNAMIC_SYMBOLS: case ORDER_DYNAMIC_STRINGS: case ORDER_REL: case ORDER_INIT: case ORDER_PLT: case ORDER_TEXT: case ORDER_FINI: case ORDER_RODATA: case ORDER_EH_FRAME: case ORDER_EH_FRAMEHDR: case ORDER_TDATA: case ORDER_TBSS: case ORDER_NOTE: case ORDER_DYNAMIC: case ORDER_CTORS: case ORDER_DTORS: case ORDER_GOT: case ORDER_GOT_PLT: case ORDER_DATA: case ORDER_INIT_ARRAY: case ORDER_FINI_ARRAY: case ORDER_BSS: return true; default: return section->hasOutputSegment(); } } template AtomSection *DefaultLayout::createSection( StringRef sectionName, int32_t contentType, DefinedAtom::ContentPermissions permissions, SectionOrder sectionOrder) { return new (_allocator) AtomSection( _targetInfo, sectionName, contentType, permissions, sectionOrder); } template AtomSection *DefaultLayout::getSection( StringRef sectionName, int32_t contentType, DefinedAtom::ContentPermissions permissions) { const SectionKey sectionKey(sectionName, permissions); auto sec = _sectionMap.find(sectionKey); if (sec != _sectionMap.end()) return sec->second; SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions); AtomSection *newSec = createSection(sectionName, contentType, permissions, sectionOrder); newSec->setOrder(sectionOrder); _sections.push_back(newSec); _sectionMap.insert(std::make_pair(sectionKey, newSec)); return newSec; } template ErrorOr DefaultLayout::addAtom(const Atom *atom) { if (const DefinedAtom *definedAtom = dyn_cast(atom)) { // HACK: Ignore undefined atoms. We need to adjust the interface so that // undefined atoms can still be included in the output symbol table for // -noinhibit-exec. if (definedAtom->contentType() == DefinedAtom::typeUnknown) return make_error_code(llvm::errc::invalid_argument); StringRef sectionName = definedAtom->customSectionName(); const DefinedAtom::ContentPermissions permissions = definedAtom->permissions(); const DefinedAtom::ContentType contentType = definedAtom->contentType(); sectionName = getSectionName(sectionName, contentType, permissions); AtomSection *section = getSection(sectionName, contentType, permissions); // Add runtime relocations to the .rela section. for (const auto &reloc : *definedAtom) if (_targetInfo.isDynamicRelocation(*definedAtom, *reloc)) getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); else if (_targetInfo.isPLTRelocation(*definedAtom, *reloc)) getPLTRelocationTable()->addRelocation(*definedAtom, *reloc); return section->appendAtom(atom); } else if (const AbsoluteAtom *absoluteAtom = dyn_cast(atom)) { // Absolute atoms are not part of any section, they are global for the whole // link _absoluteAtoms.push_back( new (_allocator) AtomLayout(absoluteAtom, 0, absoluteAtom->value())); return *_absoluteAtoms.back(); } else { llvm_unreachable("Only absolute / defined atoms can be added here"); } } /// Merge sections with the same name into a MergedSections template void DefaultLayout::mergeSimiliarSections() { MergedSections *mergedSection; for (auto &si : _sections) { const std::pair *> currentMergedSections(si->name(), nullptr); std::pair mergedSectionInsert (_mergedSectionMap.insert(currentMergedSections)); if (!mergedSectionInsert.second) { mergedSection = mergedSectionInsert.first->second; } else { mergedSection = new (_allocator.Allocate>()) MergedSections(si->name()); _mergedSections.push_back(mergedSection); mergedSectionInsert.first->second = mergedSection; } mergedSection->appendSection(si); } } template void DefaultLayout::assignSectionsToSegments() { // TODO: Do we want to give a chance for the targetHandlers // to sort segments in an arbitrary order ? // sort the sections by their order as defined by the layout std::stable_sort(_sections.begin(), _sections.end(), [](Chunk *A, Chunk *B) { return A->order() < B->order(); }); // Merge all sections mergeSimiliarSections(); // Set the ordinal after sorting the sections int ordinal = 1; for (auto msi : _mergedSections) { msi->setOrdinal(ordinal); for (auto ai : msi->sections()) { ai->setOrdinal(ordinal); } ++ordinal; } for (auto msi : _mergedSections) { for (auto ai : msi->sections()) { if (auto section = dyn_cast >(ai)) { if (!hasOutputSegment(section)) continue; // Get the segment type for the section int64_t segmentType = getSegmentType(section); msi->setHasSegment(); section->setSegmentType(segmentType); StringRef segmentName = section->segmentKindToStr(); int64_t lookupSectionFlag = msi->flags(); if (!(lookupSectionFlag & llvm::ELF::SHF_WRITE)) lookupSectionFlag &= ~llvm::ELF::SHF_EXECINSTR; lookupSectionFlag &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); Segment *segment; // We need a seperate segment for sections that dont have // the segment type to be PT_LOAD if (segmentType != llvm::ELF::PT_LOAD) { const std::pair *> additionalSegment(segmentType, nullptr); std::pair additionalSegmentInsert( _additionalSegmentMap.insert(additionalSegment)); if (!additionalSegmentInsert.second) { segment = additionalSegmentInsert.first->second; } else { segment = new (_allocator) Segment(_targetInfo, segmentName, segmentType); additionalSegmentInsert.first->second = segment; _segments.push_back(segment); } segment->append(section); } // Use the flags of the merged Section for the segment const SegmentKey key("PT_LOAD", lookupSectionFlag); const std::pair *> currentSegment(key, nullptr); std::pair segmentInsert( _segmentMap.insert(currentSegment)); if (!segmentInsert.second) { segment = segmentInsert.first->second; } else { segment = new (_allocator) Segment(_targetInfo, "PT_LOAD", llvm::ELF::PT_LOAD); segmentInsert.first->second = segment; _segments.push_back(segment); } segment->append(section); } } } if (_targetInfo.isDynamic()) { Segment *segment = new (_allocator) ProgramHeaderSegment(_targetInfo); _segments.push_back(segment); segment->append(_header); segment->append(_programHeader); } } template void DefaultLayout::assignFileOffsets() { // TODO: Do we want to give a chance for the targetHandlers // to sort segments in an arbitrary order ? std::sort(_segments.begin(), _segments.end(), Segment::compareSegments); int ordinal = 0; // Compute the number of segments that might be needed, so that the // size of the program header can be computed uint64_t offset = 0; for (auto si : _segments) { si->setOrdinal(++ordinal); // Dont assign offsets for segments that are not loadable if (si->segmentType() != llvm::ELF::PT_LOAD) continue; si->assignOffsets(offset); offset += si->fileSize(); } } template void DefaultLayout::assignVirtualAddress() { if (_segments.empty()) return; uint64_t virtualAddress = _targetInfo.getBaseAddress(); // HACK: This is a super dirty hack. The elf header and program header are // not part of a section, but we need them to be loaded at the base address // so that AT_PHDR is set correctly by the loader and so they are accessible // at runtime. To do this we simply prepend them to the first loadable Segment // and let the layout logic take care of it. Segment *firstLoadSegment = nullptr; for (auto si : _segments) { if (si->segmentType() == llvm::ELF::PT_LOAD) { firstLoadSegment = si; break; } } firstLoadSegment->prepend(_programHeader); firstLoadSegment->prepend(_header); bool newSegmentHeaderAdded = true; while (true) { for (auto si : _segments) { si->finalize(); newSegmentHeaderAdded = _programHeader->addSegment(si); } if (!newSegmentHeaderAdded) break; uint64_t fileoffset = 0; uint64_t address = virtualAddress; // Fix the offsets after adding the program header for (auto &si : _segments) { // Dont assign offsets for non loadable segments if (si->segmentType() != llvm::ELF::PT_LOAD) continue; // Align the segment to a page boundary fileoffset = llvm::RoundUpToAlignment(fileoffset, _targetInfo.getPageSize()); si->assignOffsets(fileoffset); fileoffset = si->fileOffset() + si->fileSize(); } // start assigning virtual addresses for (auto si = _segments.begin(); si != _segments.end(); ++si) { // Dont assign addresses for non loadable segments if ((*si)->segmentType() != llvm::ELF::PT_LOAD) continue; (*si)->setVAddr(virtualAddress); // The first segment has the virtualAddress set to the base address as // we have added the file header and the program header dont align the // first segment to the pagesize (*si)->assignVirtualAddress(address); (*si)->setMemSize(address - virtualAddress); virtualAddress = llvm::RoundUpToAlignment(address, _targetInfo.getPageSize()); } _programHeader->resetProgramHeaders(); } Section *section; // Fix the offsets of all the atoms within a section for (auto &si : _sections) { section = dyn_cast>(si); if (section && DefaultLayout::hasOutputSegment(section)) section->assignOffsets(section->fileOffset()); } // Set the size of the merged Sections for (auto msi : _mergedSections) { uint64_t sectionfileoffset = 0; uint64_t startFileOffset = 0; uint64_t sectionsize = 0; bool isFirstSection = true; for (auto si : msi->sections()) { if (isFirstSection) { startFileOffset = si->fileOffset(); isFirstSection = false; } sectionfileoffset = si->fileOffset(); sectionsize = si->fileSize(); } sectionsize = (sectionfileoffset - startFileOffset) + sectionsize; msi->setFileOffset(startFileOffset); msi->setSize(sectionsize); } // Set the virtual addr of the merged Sections for (auto msi : _mergedSections) { uint64_t sectionstartaddr = 0; uint64_t startaddr = 0; uint64_t sectionsize = 0; bool isFirstSection = true; for (auto si : msi->sections()) { if (isFirstSection) { startaddr = si->virtualAddr(); isFirstSection = false; } sectionstartaddr = si->virtualAddr(); sectionsize = si->memSize(); } sectionsize = (sectionstartaddr - startaddr) + sectionsize; msi->setMemSize(sectionsize); msi->setAddr(startaddr); } } template void DefaultLayout::assignOffsetsForMiscSections() { uint64_t fileoffset = 0; uint64_t size = 0; for (auto si : _segments) { // Dont calculate offsets from non loadable segments if (si->segmentType() != llvm::ELF::PT_LOAD) continue; fileoffset = si->fileOffset(); size = si->fileSize(); } fileoffset = fileoffset + size; Section *section; for (auto si : _sections) { section = dyn_cast>(si); if (section && DefaultLayout::hasOutputSegment(section)) continue; fileoffset = llvm::RoundUpToAlignment(fileoffset, si->align2()); si->setFileOffset(fileoffset); si->setVAddr(0); fileoffset += si->fileSize(); } } } // end namespace elf } // end namespace lld #endif