//===- lib/ReaderWriter/ELF/SectionChunks.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_SECTION_CHUNKS_H #define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H #include "Chunk.h" #include "Layout.h" #include "TargetHandler.h" #include "Writer.h" #include "lld/Core/DefinedAtom.h" #include "lld/Core/range.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" namespace { LLVM_ATTRIBUTE_UNUSED std::string kindOrUnknown(llvm::ErrorOr k) { if (k) return *k; return ""; } } namespace lld { namespace elf { template class MergedSections; using namespace llvm::ELF; template class Segment; /// \brief An ELF section. template class Section : public Chunk { public: Section(const ELFTargetInfo &ti, StringRef name, typename Chunk::Kind k = Chunk::K_ELFSection) : Chunk(name, k, ti), _parent(nullptr), _flags(0), _entSize(0), _type(0), _link(0), _info(0), _segmentType(SHT_NULL) {} /// \brief Modify the section contents before assigning virtual addresses // or assigning file offsets virtual void doPreFlight() {} /// \brief Finalize the section contents before writing virtual void finalize() {} /// \brief Does this section have an output segment. virtual bool hasOutputSegment() { return false; } /// \brief Assign file offsets starting at offset. virtual void assignOffsets(uint64_t offset) {} /// \brief Assign virtual addresses starting at addr. Addr is modified to be /// the next available virtual address. virtual void assignVirtualAddress(uint64_t &addr) {} uint64_t getFlags() const { return _flags; } uint64_t getEntSize() const { return _entSize; } uint32_t getType() const { return _type; } uint32_t getLink() const { return _link; } uint32_t getInfo() const { return _info; } Layout::SegmentType getSegmentType() const { return _segmentType; } /// \brief convert the segment type to a String for diagnostics and printing /// purposes StringRef segmentKindToStr() const; // TODO: Move this down to AtomSection. virtual bool findAtomAddrByName(StringRef name, uint64_t &addr) { return false; } /// \brief Records the segmentType, that this section belongs to void setSegmentType(const Layout::SegmentType segmentType) { this->_segmentType = segmentType; } void setMergedSection(MergedSections *ms) { _parent = ms; } static bool classof(const Chunk *c) { return c->kind() == Chunk::K_ELFSection || c->kind() == Chunk::K_AtomSection; } protected: /// \brief MergedSections this Section is a member of, or nullptr. MergedSections *_parent; /// \brief ELF SHF_* flags. uint64_t _flags; /// \brief The size of each entity. uint64_t _entSize; /// \brief ELF SHT_* type. uint32_t _type; /// \brief sh_link field. uint32_t _link; /// \brief the sh_info field. uint32_t _info; /// \brief the output ELF segment type of this section. Layout::SegmentType _segmentType; }; /// \brief A section containing atoms. template class AtomSection : public Section { public: AtomSection(const ELFTargetInfo &ti, StringRef name, int32_t contentType, int32_t permissions, int32_t order) : Section(ti, name, Chunk::K_AtomSection), _contentType(contentType), _contentPermissions(permissions) { this->setOrder(order); switch (contentType) { case DefinedAtom::typeCode: case DefinedAtom::typeDataFast: case DefinedAtom::typeData: case DefinedAtom::typeConstant: case DefinedAtom::typeGOT: case DefinedAtom::typeStub: case DefinedAtom::typeResolver: case DefinedAtom::typeTLVInitialData: this->_type = SHT_PROGBITS; break; case DefinedAtom::typeTLVInitialZeroFill: case DefinedAtom::typeZeroFillFast: case DefinedAtom::typeZeroFill: this->_type = SHT_NOBITS; break; } switch (permissions) { case DefinedAtom::permR__: this->_flags = SHF_ALLOC; break; case DefinedAtom::permR_X: this->_flags = SHF_ALLOC | SHF_EXECINSTR; break; case DefinedAtom::permRW_: case DefinedAtom::permRW_L: this->_flags = SHF_ALLOC | SHF_WRITE; if (_contentType == DefinedAtom::typeTLVInitialData || _contentType == DefinedAtom::typeTLVInitialZeroFill) this->_flags |= SHF_TLS; break; case DefinedAtom::permRWX: this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; break; } } /// Align the offset to the required modulus defined by the atom alignment uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign); // \brief Append an atom to a Section. The atom gets pushed into a vector // contains the atom, the atom file offset, the atom virtual address // the atom file offset is aligned appropriately as set by the Reader virtual const AtomLayout &appendAtom(const Atom *atom); /// \brief Set the virtual address of each Atom in the Section. This /// routine gets called after the linker fixes up the virtual address /// of the section virtual void assignVirtualAddress(uint64_t &addr) { for (auto &ai : _atoms) { ai->_virtualAddr = addr + ai->_fileOffset; } } /// \brief Set the file offset of each Atom in the section. This routine /// gets called after the linker fixes up the section offset virtual void assignOffsets(uint64_t offset) { for (auto &ai : _atoms) { ai->_fileOffset = offset + ai->_fileOffset; } } /// \brief Find the Atom address given a name, this is needed to to properly /// apply relocation. The section class calls this to find the atom address /// to fix the relocation virtual bool findAtomAddrByName(StringRef name, uint64_t &addr) { for (auto ai : _atoms) { if (ai->_atom->name() == name) { addr = ai->_virtualAddr; return true; } } return false; } /// \brief Does the Atom occupy any disk space bool occupiesNoDiskSpace() const { return ((_contentType == DefinedAtom::typeZeroFill) || (_contentType == DefinedAtom::typeZeroFillFast)); } /// \brief The permission of the section is the most permissive permission /// of all atoms that the section contains void setContentPermissions(int32_t perm) { _contentPermissions = std::max(perm, _contentPermissions); } /// \brief Return the raw flags, we need this to sort segments inline int64_t atomflags() const { return _contentPermissions; } /// Atom Iterators typedef typename std::vector::iterator atom_iter; range atoms() { return _atoms; } virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); static bool classof(const Chunk *c) { return c->kind() == Chunk::K_AtomSection; } protected: llvm::BumpPtrAllocator _alloc; int32_t _contentType; int32_t _contentPermissions; std::vector _atoms; }; /// Align the offset to the required modulus defined by the atom alignment template uint64_t AtomSection::alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign) { uint64_t requiredModulus = atomAlign.modulus; uint64_t align2 = 1u << atomAlign.powerOf2; uint64_t currentModulus = (offset % align2); uint64_t retOffset = offset; if (currentModulus != requiredModulus) { if (requiredModulus > currentModulus) retOffset += requiredModulus - currentModulus; else retOffset += align2 + requiredModulus - currentModulus; } return retOffset; } // \brief Append an atom to a Section. The atom gets pushed into a vector // contains the atom, the atom file offset, the atom virtual address // the atom file offset is aligned appropriately as set by the Reader template const AtomLayout &AtomSection::appendAtom(const Atom *atom) { Atom::Definition atomType = atom->definition(); const DefinedAtom *definedAtom = cast(atom); DefinedAtom::Alignment atomAlign = definedAtom->alignment(); uint64_t align2 = 1u << atomAlign.powerOf2; // Align the atom to the required modulus/ align the file offset and the // memory offset seperately this is required so that BSS symbols are handled // properly as the BSS symbols only occupy memory size and not file size uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); uint64_t mOffset = alignOffset(this->memSize(), atomAlign); switch (atomType) { case Atom::definitionRegular: switch(definedAtom->contentType()) { case DefinedAtom::typeCode: case DefinedAtom::typeConstant: case DefinedAtom::typeData: case DefinedAtom::typeDataFast: case DefinedAtom::typeGOT: case DefinedAtom::typeStub: case DefinedAtom::typeResolver: case DefinedAtom::typeTLVInitialData: _atoms.push_back(new (_alloc) AtomLayout(atom, fOffset, 0)); this->_fsize = fOffset + definedAtom->size(); this->_msize = mOffset + definedAtom->size(); DEBUG_WITH_TYPE("Section", llvm::dbgs() << "[" << this->name() << " " << this << "] " << "Adding atom: " << atom->name() << "@" << fOffset << "\n"); break; case DefinedAtom::typeTLVInitialZeroFill: case DefinedAtom::typeZeroFill: case DefinedAtom::typeZeroFillFast: _atoms.push_back(new (_alloc) AtomLayout(atom, mOffset, 0)); this->_msize = mOffset + definedAtom->size(); break; default: llvm::dbgs() << definedAtom->contentType() << "\n"; llvm_unreachable("Uexpected content type."); } break; default: llvm_unreachable("Expecting only definedAtoms being passed here"); break; } // Set the section alignment to the largest alignment // std::max doesnot support uint64_t if (this->_align2 < align2) this->_align2 = align2; return *_atoms.back(); } /// \brief convert the segment type to a String for diagnostics /// and printing purposes template StringRef Section::segmentKindToStr() const { switch(_segmentType) { case llvm::ELF::PT_DYNAMIC: return "DYNAMIC"; case llvm::ELF::PT_INTERP: return "INTERP"; case llvm::ELF::PT_LOAD: return "LOAD"; case llvm::ELF::PT_GNU_EH_FRAME: return "EH_FRAME"; case llvm::ELF::PT_GNU_RELRO: return "RELRO"; case llvm::ELF::PT_NOTE: return "NOTE"; case llvm::ELF::PT_NULL: return "NULL"; case llvm::ELF::PT_TLS: return "TLS"; default: return "UNKNOWN"; } } /// \brief Write the section and the atom contents to the buffer template void AtomSection::write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); for (auto &ai : _atoms) { DEBUG_WITH_TYPE("Section", llvm::dbgs() << "Writing atom: " << ai->_atom->name() << " | " << ai->_fileOffset << "\n"); const DefinedAtom *definedAtom = cast(ai->_atom); if ((definedAtom->contentType() == DefinedAtom::typeZeroFill) || (definedAtom->contentType() == DefinedAtom::typeZeroFillFast)) continue; // Copy raw content of atom to file buffer. llvm::ArrayRef content = definedAtom->rawContent(); uint64_t contentSize = content.size(); if (contentSize == 0) continue; uint8_t *atomContent = chunkBuffer + ai->_fileOffset; std::copy_n(content.data(), contentSize, atomContent); const TargetRelocationHandler &relHandler = this->_targetInfo.template getTargetHandler() .getRelocationHandler(); for (const auto ref : *definedAtom) relHandler.applyRelocation(*writer, buffer, *ai, *ref); } } /// \brief A MergedSections represents a set of sections grouped by the same /// name. The output file that gets written by the linker has sections grouped /// by similiar names template class MergedSections { public: // Iterators typedef typename std::vector *>::iterator ChunkIter; MergedSections(StringRef name); // Appends a section into the list of sections that are part of this Merged // Section void appendSection(Chunk *c); // Set the MergedSections is associated with a segment inline void setHasSegment() { _hasSegment = true; } /// Sets the ordinal inline void setOrdinal(uint64_t ordinal) { _ordinal = ordinal; } /// Sets the Memory size inline void setMemSize(uint64_t memsz) { _memSize = memsz; } /// Sets the size fo the merged Section inline void setSize(uint64_t fsiz) { _size = fsiz; } // The offset of the first section contained in the merged section is // contained here inline void setFileOffset(uint64_t foffset) { _fileOffset = foffset; } // Sets the starting address of the section inline void setAddr(uint64_t addr) { _virtualAddr = addr; } void setLink(uint64_t link) { _link = link; } void setInfo(uint64_t info) { _shInfo = info; } inline range sections() { return _sections; } // The below functions returns the properties of the MergeSection inline bool hasSegment() const { return _hasSegment; } inline StringRef name() const { return _name; } inline int64_t shinfo() const { return _shInfo; } inline uint64_t align2() const { return _align2; } inline int64_t link() const { return _link; } inline int64_t type() const { return _type; } inline uint64_t virtualAddr() const { return _virtualAddr; } inline int64_t ordinal() const { return _ordinal; } inline int64_t kind() const { return _kind; } inline uint64_t fileSize() const { return _size; } inline int64_t entsize() const { return _entSize; } inline uint64_t fileOffset() const { return _fileOffset; } inline int64_t flags() const { return _flags; } inline uint64_t memSize() { return _memSize; } private: StringRef _name; bool _hasSegment; uint64_t _ordinal; uint64_t _flags; uint64_t _size; uint64_t _memSize; uint64_t _fileOffset; uint64_t _virtualAddr; int64_t _shInfo; int64_t _entSize; int64_t _link; uint64_t _align2; int64_t _kind; int64_t _type; std::vector *> _sections; }; /// MergedSections template MergedSections::MergedSections(StringRef name) : _name(name) ,_hasSegment(false) ,_ordinal(0) ,_flags(0) ,_size(0) ,_memSize(0) ,_fileOffset(0) ,_virtualAddr(0) ,_shInfo(0) ,_entSize(0) ,_link(0) ,_align2(0) ,_kind(0) ,_type(0) { } template void MergedSections::appendSection(Chunk *c) { if (c->align2() > _align2) _align2 = c->align2(); if (const auto section = dyn_cast>(c)) { assert(!_link && "Section already has a link!"); _link = section->getLink(); _shInfo = section->getInfo(); _entSize = section->getEntSize(); _type = section->getType(); if (_flags < section->getFlags()) _flags = section->getFlags(); section->setMergedSection(this); } _kind = c->kind(); _sections.push_back(c); } /// \brief The class represents the ELF String Table template class StringTable : public Section { public: StringTable(const ELFTargetInfo &, const char *str, int32_t order, bool dynamic = false); uint64_t addString(StringRef symname); virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); private: std::vector _strings; struct StringRefMappingInfo { static StringRef getEmptyKey() { return StringRef(); } static StringRef getTombstoneKey() { return StringRef(" ", 0); } static unsigned getHashValue(StringRef const val) { return llvm::HashString(val); } static bool isEqual(StringRef const lhs, StringRef const rhs) { return lhs.equals(rhs); } }; typedef typename llvm::DenseMap StringMapT; typedef typename StringMapT::iterator StringMapTIter; StringMapT _stringMap; }; template StringTable::StringTable(const ELFTargetInfo &ti, const char *str, int32_t order, bool dynamic) : Section(ti, str) { // the string table has a NULL entry for which // add an empty string _strings.push_back(""); this->_fsize = 1; this->_align2 = 1; this->setOrder(order); this->_type = SHT_STRTAB; if (dynamic) { this->_flags = SHF_ALLOC; this->_msize = this->_fsize; } } template uint64_t StringTable::addString(StringRef symname) { if (symname.size() == 0) return 0; StringMapTIter stringIter = _stringMap.find(symname); if (stringIter == _stringMap.end()) { _strings.push_back(symname); uint64_t offset = this->_fsize; this->_fsize += symname.size() + 1; if (this->_flags & SHF_ALLOC) this->_msize = this->_fsize; _stringMap[symname] = offset; return offset; } return stringIter->second; } template void StringTable::write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); for (auto si : _strings) { memcpy(dest, si.data(), si.size()); dest += si.size(); memcpy(dest, "", 1); dest += 1; } } /// \brief The SymbolTable class represents the symbol table in a ELF file template class SymbolTable : public Section { typedef typename llvm::object::ELFDataTypeTypedefHelper::Elf_Addr Elf_Addr; typedef llvm::object::Elf_Sym_Impl Elf_Sym; struct SymbolEntry { SymbolEntry(const Atom *a, const Elf_Sym &sym, const AtomLayout *layout) : _atom(a), _symbol(sym), _atomLayout(layout) {} SymbolEntry() : _atom(nullptr) {} const Atom *_atom; Elf_Sym _symbol; const AtomLayout *_atomLayout; }; public: SymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order); void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0, const AtomLayout *layout=nullptr); /// \brief Get the symbol table index for an Atom. If it's not in the symbol /// table, return STN_UNDEF. uint32_t getSymbolTableIndex(const Atom *a) const { auto se = std::find_if(_symbolTable.begin(), _symbolTable.end(), [=](const SymbolEntry &se) { return se._atom == a; }); if (se == _symbolTable.end()) return STN_UNDEF; return std::distance(_symbolTable.begin(), se); } virtual void finalize(); virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); void setStringSection(StringTable *s) { _stringSection = s; } StringTable *getStringTable() const { return _stringSection; } protected: llvm::BumpPtrAllocator _symbolAllocate; StringTable *_stringSection; std::vector _symbolTable; }; /// ELF Symbol Table template SymbolTable::SymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order) : Section(ti, str) { this->setOrder(order); Elf_Sym symbol; std::memset(&symbol, 0, sizeof(Elf_Sym)); _symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr)); this->_entSize = sizeof(Elf_Sym); this->_fsize = sizeof(Elf_Sym); this->_align2 = sizeof(Elf_Addr); this->_type = SHT_SYMTAB; } /// Add a symbol to the symbol Table, definedAtoms which get added to the symbol /// section dont have their virtual addresses set at the time of adding the /// symbol to the symbol table(Example: dynamic symbols), the addresses needs /// to be updated in the table before writing the dynamic symbol table /// information template void SymbolTable::addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr, const AtomLayout *atomLayout) { Elf_Sym symbol; unsigned char binding = 0, type = 0; symbol.st_name = _stringSection->addString(atom->name()); symbol.st_size = 0; symbol.st_shndx = sectionIndex; symbol.st_value = 0; symbol.st_other = llvm::ELF::STV_DEFAULT; if (const DefinedAtom *da = dyn_cast(atom)){ symbol.st_size = da->size(); DefinedAtom::ContentType ct; switch (ct = da->contentType()){ case DefinedAtom::typeCode: case DefinedAtom::typeStub: symbol.st_value = addr; type = llvm::ELF::STT_FUNC; break; case DefinedAtom::typeResolver: symbol.st_value = addr; type = llvm::ELF::STT_GNU_IFUNC; break; case DefinedAtom::typeDataFast: case DefinedAtom::typeData: case DefinedAtom::typeConstant: symbol.st_value = addr; type = llvm::ELF::STT_OBJECT; break; case DefinedAtom::typeGOT: symbol.st_value = addr; type = llvm::ELF::STT_NOTYPE; break; case DefinedAtom::typeZeroFill: case DefinedAtom::typeZeroFillFast: type = llvm::ELF::STT_OBJECT; symbol.st_value = addr; break; case DefinedAtom::typeTLVInitialData: case DefinedAtom::typeTLVInitialZeroFill: type = llvm::ELF::STT_TLS; symbol.st_value = addr; break; default: type = llvm::ELF::STT_NOTYPE; } if (da->customSectionName() == da->name()) type = llvm::ELF::STT_SECTION; if (da->scope() == DefinedAtom::scopeTranslationUnit) binding = llvm::ELF::STB_LOCAL; else binding = llvm::ELF::STB_GLOBAL; } else if (const AbsoluteAtom *aa = dyn_cast(atom)){ type = llvm::ELF::STT_OBJECT; symbol.st_shndx = llvm::ELF::SHN_ABS; switch (aa->scope()) { case AbsoluteAtom::scopeLinkageUnit: symbol.st_other = llvm::ELF::STV_HIDDEN; binding = llvm::ELF::STB_LOCAL; break; case AbsoluteAtom::scopeTranslationUnit: binding = llvm::ELF::STB_LOCAL; break; case AbsoluteAtom::scopeGlobal: binding = llvm::ELF::STB_GLOBAL; break; } symbol.st_value = addr; } else if (isa(atom)) { type = llvm::ELF::STT_FUNC; symbol.st_shndx = llvm::ELF::SHN_UNDEF; binding = llvm::ELF::STB_GLOBAL; } else { symbol.st_value = 0; type = llvm::ELF::STT_NOTYPE; binding = llvm::ELF::STB_WEAK; } symbol.setBindingAndType(binding, type); _symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout)); this->_fsize += sizeof(Elf_Sym); if (this->_flags & SHF_ALLOC) this->_msize = this->_fsize; } template void SymbolTable::finalize() { // sh_info should be one greater than last symbol with STB_LOCAL binding // we sort the symbol table to keep all local symbols at the beginning std::stable_sort(_symbolTable.begin(), _symbolTable.end(), [](const SymbolEntry & A, const SymbolEntry & B) { return A._symbol.getBinding() < B._symbol.getBinding(); }); uint16_t shInfo = 0; for (const auto &i : _symbolTable) { if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL) break; shInfo++; } this->_info = shInfo; this->_link = _stringSection->ordinal(); if (this->_parent) { this->_parent->setInfo(this->_info); this->_parent->setLink(this->_link); } } template void SymbolTable::write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); for (const auto &sti : _symbolTable) { memcpy(dest, &sti._symbol, sizeof(Elf_Sym)); dest += sizeof(Elf_Sym); } } template class DynamicSymbolTable : public SymbolTable { public: DynamicSymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order) : SymbolTable(ti, str, order) { this->_type = SHT_DYNSYM; this->_flags = SHF_ALLOC; this->_msize = this->_fsize; } virtual void finalize() { // Defined symbols which have been added into the dynamic symbol table // dont have their addresses known until addresses have been assigned // so lets update the symbol values after they have got assigned for (auto &ste: this->_symbolTable) { const AtomLayout *atomLayout = ste._atomLayout; if (!atomLayout) continue; ste._symbol.st_value = atomLayout->_virtualAddr; } SymbolTable::finalize(); } }; template class RelocationTable : public Section { public: typedef llvm::object::Elf_Rel_Impl Elf_Rela; RelocationTable(const ELFTargetInfo &ti, StringRef str, int32_t order) : Section(ti, str), _symbolTable(nullptr) { this->setOrder(order); this->_entSize = sizeof(Elf_Rela); this->_align2 = llvm::alignOf(); this->_type = SHT_RELA; this->_flags = SHF_ALLOC; } /// \returns the index of the relocation added. uint32_t addRelocation(const DefinedAtom &da, const Reference &r) { _relocs.emplace_back(&da, &r); this->_fsize = _relocs.size() * sizeof(Elf_Rela); this->_msize = this->_fsize; return _relocs.size() - 1; } bool getRelocationIndex(const Reference &r, uint32_t &res) { auto rel = std::find_if( _relocs.begin(), _relocs.end(), [&](const std::pair &p) { if (p.second == &r) return true; return false; }); if (rel == _relocs.end()) return false; res = std::distance(_relocs.begin(), rel); return true; } void setSymbolTable(const SymbolTable *symbolTable) { _symbolTable = symbolTable; } virtual void finalize() { this->_link = _symbolTable ? _symbolTable->ordinal() : 0; if (this->_parent) this->_parent->setLink(this->_link); } virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); for (const auto &rel : _relocs) { Elf_Rela *r = reinterpret_cast(dest); uint32_t index = _symbolTable ? _symbolTable->getSymbolTableIndex(rel.second->target()) : (uint32_t) STN_UNDEF; r->setSymbolAndType(index, rel.second->kind()); r->r_offset = writer->addressOfAtom(rel.first) + rel.second->offsetInAtom(); r->r_addend = writer->addressOfAtom(rel.second->target()) + rel.second->addend(); dest += sizeof(Elf_Rela); DEBUG_WITH_TYPE("ELFRelocationTable", llvm::dbgs() << kindOrUnknown(this->_targetInfo.stringFromRelocKind( rel.second->kind())) << " relocation at " << rel.first->name() << "@" << r->r_offset << " to " << rel.second->target()->name() << "@" << r->r_addend << "\n"); } } private: std::vector> _relocs; const SymbolTable *_symbolTable; }; template class HashSection; template class DynamicTable : public Section { typedef llvm::object::Elf_Dyn_Impl Elf_Dyn; typedef std::vector EntriesT; public: DynamicTable(const ELFTargetInfo &ti, StringRef str, int32_t order) : Section(ti, str) { this->setOrder(order); this->_entSize = sizeof(Elf_Dyn); this->_align2 = llvm::alignOf(); // Reserve space for the DT_NULL entry. this->_fsize = sizeof(Elf_Dyn); this->_msize = sizeof(Elf_Dyn); this->_type = SHT_DYNAMIC; this->_flags = SHF_ALLOC; _layout = &ti.getTargetHandler().targetLayout(); } range entries() { return _entries; } /// \returns the index of the entry. std::size_t addEntry(Elf_Dyn e) { _entries.push_back(e); this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); this->_msize = this->_fsize; return _entries.size() - 1; } void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); // Add the null entry. Elf_Dyn d; d.d_tag = 0; d.d_un.d_val = 0; _entries.push_back(d); std::memcpy(dest, _entries.data(), this->_fsize); } void createDefaultEntries() { Elf_Dyn dyn; dyn.d_un.d_val = 0; dyn.d_tag = DT_HASH; _dt_hash = addEntry(dyn); dyn.d_tag = DT_STRTAB; _dt_strtab = addEntry(dyn); dyn.d_tag = DT_SYMTAB; _dt_symtab = addEntry(dyn); dyn.d_tag = DT_STRSZ; _dt_strsz = addEntry(dyn); dyn.d_tag = DT_SYMENT; _dt_syment = addEntry(dyn); if (_layout->hasDynamicRelocationTable()) { dyn.d_tag = DT_RELA; _dt_rela = addEntry(dyn); dyn.d_tag = DT_RELASZ; _dt_relasz = addEntry(dyn); dyn.d_tag = DT_RELAENT; _dt_relaent = addEntry(dyn); } if (_layout->hasPLTRelocationTable()) { dyn.d_tag = DT_PLTRELSZ; _dt_pltrelsz = addEntry(dyn); dyn.d_tag = DT_PLTGOT; _dt_pltgot = addEntry(dyn); dyn.d_tag = DT_PLTREL; dyn.d_un.d_val = DT_RELA; _dt_pltrel = addEntry(dyn); dyn.d_un.d_val = 0; dyn.d_tag = DT_JMPREL; _dt_jmprel = addEntry(dyn); } } void updateDynamicTable(HashSection *hashTable, DynamicSymbolTable *dynamicSymbolTable) { StringTable *dynamicStringTable = dynamicSymbolTable->getStringTable(); _entries[_dt_hash].d_un.d_val = hashTable->virtualAddr(); _entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr(); _entries[_dt_symtab].d_un.d_val = dynamicSymbolTable->virtualAddr(); _entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize(); _entries[_dt_syment].d_un.d_val = dynamicSymbolTable->getEntSize(); if (_layout->hasDynamicRelocationTable()) { auto relaTbl = _layout->getDynamicRelocationTable(); _entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr(); _entries[_dt_relasz].d_un.d_val = relaTbl->memSize(); _entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize(); } if (_layout->hasPLTRelocationTable()) { auto relaTbl = _layout->getPLTRelocationTable(); _entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr(); _entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize(); auto gotplt = _layout->findOutputSection(".got.plt"); _entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr(); } } private: EntriesT _entries; std::size_t _dt_hash; std::size_t _dt_strtab; std::size_t _dt_symtab; std::size_t _dt_rela; std::size_t _dt_relasz; std::size_t _dt_relaent; std::size_t _dt_strsz; std::size_t _dt_syment; std::size_t _dt_pltrelsz; std::size_t _dt_pltgot; std::size_t _dt_pltrel; std::size_t _dt_jmprel; TargetLayout *_layout; }; template class InterpSection : public Section { public: InterpSection(const ELFTargetInfo &ti, StringRef str, int32_t order, StringRef interp) : Section(ti, str), _interp(interp){ this->setOrder(order); this->_align2 = 1; // + 1 for null term. this->_fsize = interp.size() + 1; this->_msize = this->_fsize; this->_type = SHT_PROGBITS; this->_flags = SHF_ALLOC; } void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); std::memcpy(dest, _interp.data(), _interp.size()); } private: StringRef _interp; }; template class HashSection : public Section { struct SymbolTableEntry { StringRef _name; uint32_t _index; }; public: HashSection(const ELFTargetInfo &ti, StringRef name, int32_t order) : Section(ti, name) { this->setOrder(order); this->_align2 = 4; // Alignment of Elf32_Word. this->_type = SHT_HASH; this->_flags = SHF_ALLOC; // The size of nbucket and nchain. this->_fsize = 8; this->_msize = this->_fsize; } void addSymbol(StringRef name, uint32_t index) { SymbolTableEntry ste; ste._name = name; ste._index = index; _entries.push_back(ste); } virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); // TODO: Calculate hashes and build the hash table in finalize. We currently // just emit an empty hash table so the dynamic loader doesn't crash. std::memset(dest, 0, this->_fsize); } private: std::vector _entries; }; } // end namespace elf } // end namespace lld #endif