diff options
Diffstat (limited to 'lld/lib/ReaderWriter/ELF/SectionChunks.h')
| -rw-r--r-- | lld/lib/ReaderWriter/ELF/SectionChunks.h | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/ELF/SectionChunks.h b/lld/lib/ReaderWriter/ELF/SectionChunks.h new file mode 100644 index 00000000000..ffb38c53740 --- /dev/null +++ b/lld/lib/ReaderWriter/ELF/SectionChunks.h @@ -0,0 +1,744 @@ +//===- 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/StringRef.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 lld { +namespace elf { + +/// \brief A section contains a set of atoms that have similiar properties +/// The atoms that have similiar properties are merged to form a section +template<class ELFT> +class Section : public Chunk<ELFT> { +public: + // The Kind of section that the object represents + enum SectionKind { + K_Default, + K_Target, // The section is handed over to the target + K_SymbolTable, + K_StringTable, + }; + // Create a section object, the section is set to the default type if the + // caller doesnot set it + Section(const ELFTargetInfo &, const StringRef sectionName, + const int32_t contentType, const int32_t contentPermissions, + const int32_t order, const SectionKind kind = K_Default); + + /// return the section kind + inline SectionKind sectionKind() const { + return _sectionKind; + } + + /// 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 + void 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 + inline void assignVirtualAddress(uint64_t &addr) { + for (auto &ai : _atoms) { + ai._virtualAddr = addr + ai._fileOffset; + } + addr += this->memSize(); + } + + /// \brief Set the file offset of each Atom in the section. This routine + /// gets called after the linker fixes up the section offset + inline 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 + inline bool findAtomAddrByName(const 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 + inline bool occupiesNoDiskSpace() const { + return _contentType == DefinedAtom::typeZeroFill; + } + + /// \brief The permission of the section is the most permissive permission + /// of all atoms that the section contains + inline void setContentPermissions(int32_t perm) { + _contentPermissions = std::max(perm, _contentPermissions); + } + + /// \brief Get the section flags, defined by the permissions of the section + int64_t flags(); + + /// \brief Return the section type, the returned value is recorded in the + /// sh_type field of the Section Header + int type(); + + /// \brief convert the segment type to a String for diagnostics + /// and printing purposes + StringRef segmentKindToStr() const; + + /// \brief Return the raw flags, we need this to sort segments + inline int64_t atomflags() const { + return _contentPermissions; + } + + /// \brief Returns the section link field, the returned value is + /// recorded in the sh_link field of the Section Header + inline int link() const { + return _link; + } + + inline void setLink(int32_t link) { + _link = link; + } + + /// \brief Returns the section entsize field, the returned value is + /// recorded in the sh_entsize field of the Section Header + inline int entsize() const { + return _entSize; + } + + /// \brief Returns the shinfo field, the returned value is + /// recorded in the sh_info field of the Section Header + inline int shinfo() const { + return _shInfo; + } + + /// \brief Records the segmentType, that this section belongs to + inline void setSegment(const Layout::SegmentType segmentType) { + _segmentType = segmentType; + } + + /// \brief for LLVM style RTTI information + static inline bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::K_ELFSection; + } + + /// \brief Finalize the section contents before writing + inline void finalize() { } + + /// \brief Write the section and the atom contents to the buffer + void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); + + /// Atom Iterators + typedef typename std::vector<AtomLayout>::iterator atom_iter; + + range<atom_iter> atoms() { return _atoms; } + +protected: + int32_t _contentType; + int32_t _contentPermissions; + SectionKind _sectionKind; + std::vector<AtomLayout> _atoms; + Layout::SegmentType _segmentType; + int64_t _entSize; + int64_t _shInfo; + int64_t _link; +}; + +// Create a section object, the section is set to the default type if the +// caller doesnot set it +template <class ELFT> +Section<ELFT>::Section(const ELFTargetInfo &ti, const StringRef sectionName, + const int32_t contentType, + const int32_t contentPermissions, const int32_t order, + const SectionKind kind) + : Chunk<ELFT>(sectionName, Chunk<ELFT>::K_ELFSection, ti), + _contentType(contentType), _contentPermissions(contentPermissions), + _sectionKind(kind), _entSize(0), _shInfo(0), _link(0) { + this->setOrder(order); +} + +/// Align the offset to the required modulus defined by the atom alignment +template<class ELFT> +uint64_t +Section<ELFT>::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<class ELFT> +void +Section<ELFT>::appendAtom(const Atom *atom) { + Atom::Definition atomType = atom->definition(); + const DefinedAtom *definedAtom = cast<DefinedAtom>(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::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + _atoms.push_back(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::typeZeroFill: + _atoms.push_back(AtomLayout(atom, mOffset, 0)); + this->_msize = mOffset + definedAtom->size(); + break; + default: + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + break; + } + 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; +} + +/// \brief Get the section flags, defined by the permissions of the section +template<class ELFT> +int64_t +Section<ELFT>::flags() { + switch (_contentPermissions) { + case DefinedAtom::perm___: + return 0; + + case DefinedAtom::permR__: + return llvm::ELF::SHF_ALLOC; + + case DefinedAtom::permR_X: + return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR; + + case DefinedAtom::permRW_: + case DefinedAtom::permRW_L: + return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE; + + case DefinedAtom::permRWX: + return llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_WRITE | + llvm::ELF::SHF_EXECINSTR; + + default: + break; + } + return llvm::ELF::SHF_ALLOC; +} + +/// \brief Return the section type, the returned value is recorded in the +/// sh_type field of the Section Header + +template<class ELFT> +int +Section<ELFT>::type() { + if (_sectionKind == K_SymbolTable) + return llvm::ELF::SHT_SYMTAB; + + switch (_contentType) { + case DefinedAtom::typeCode: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + return llvm::ELF::SHT_PROGBITS; + + case DefinedAtom::typeZeroFill: + return llvm::ELF::SHT_NOBITS; + + // Case to handle section types + // Symtab, String Table ... + default: + return _contentType; + } +} + +/// \brief convert the segment type to a String for diagnostics +/// and printing purposes +template<class ELFT> +StringRef +Section<ELFT>::segmentKindToStr() const { + switch(_segmentType) { + 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_NOTE: + return "NOTE"; + case llvm::ELF::PT_DYNAMIC: + return "DYNAMIC"; + case llvm::ELF::PT_GNU_RELRO: + return "RELRO"; + case llvm::ELF::PT_NULL: + return "NULL"; + default: + return "UNKNOWN"; + } +} + +/// \brief Write the section and the atom contents to the buffer +template <class ELFT> +void Section<ELFT>::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<DefinedAtom>(ai._atom); + if (definedAtom->contentType() == DefinedAtom::typeZeroFill) + continue; + // Copy raw content of atom to file buffer. + llvm::ArrayRef<uint8_t> 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); + for (const auto ref : *definedAtom) { + uint32_t offset = ref->offsetInAtom(); + uint64_t targetAddress = 0; + assert(ref->target() != nullptr && "Found the target to be NULL"); + targetAddress = writer->addressOfAtom(ref->target()); + uint64_t fixupAddress = writer->addressOfAtom(ai._atom) + offset; + // apply the relocation + writer->kindHandler()->applyFixup(ref->kind(), + ref->addend(), + &atomContent[offset], + fixupAddress, + targetAddress); + } + } +} + +/// \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 ELFT> +class MergedSections { +public: + // Iterators + typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + + MergedSections(StringRef name); + + // Appends a section into the list of sections that are part of this Merged + // Section + void appendSection(Chunk<ELFT> *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; + } + + inline range<ChunkIter> 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; + int64_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<Chunk<ELFT> *> _sections; +}; + +/// MergedSections +template<class ELFT> +MergedSections<ELFT>::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<class ELFT> +void +MergedSections<ELFT>::appendSection(Chunk<ELFT> *c) { + if (c->align2() > _align2) + _align2 = c->align2(); + if (const auto section = dyn_cast<Section<ELFT>>(c)) { + _link = section->link(); + _shInfo = section->shinfo(); + _entSize = section->entsize(); + _type = section->type(); + if (_flags < section->flags()) + _flags = section->flags(); + } + _kind = c->kind(); + _sections.push_back(c); +} + +/// \brief The class represents the ELF String Table +template<class ELFT> +class StringTable : public Section<ELFT> { +public: + StringTable(const ELFTargetInfo &, const char *str, int32_t order); + + static inline bool classof(const Chunk<ELFT> *c) { + return c->kind() == Section<ELFT>::K_StringTable; + } + + uint64_t addString(const StringRef symname); + + void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); + + inline void finalize() { } + +private: + std::vector<StringRef> _strings; +}; + +template <class ELFT> +StringTable<ELFT>::StringTable(const ELFTargetInfo &ti, const char *str, + int32_t order) + : Section<ELFT>(ti, str, llvm::ELF::SHT_STRTAB, DefinedAtom::perm___, order, + Section<ELFT>::K_StringTable) { + // 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); +} + +template<class ELFT> +uint64_t +StringTable<ELFT>::addString(const StringRef symname) { + _strings.push_back(symname); + uint64_t offset = this->_fsize; + this->_fsize += symname.size() + 1; + return offset; +} + +template <class ELFT> +void StringTable<ELFT>::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 ELFT> +class SymbolTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + SymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order); + + void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0); + + void finalize(); + + void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer); + + static inline bool classof(const Chunk<ELFT> *c) { + return c->kind() == Section<ELFT>::K_SymbolTable; + } + + inline void setStringSection(StringTable<ELFT> *s) { + _stringSection = s; + } + +private: + StringTable<ELFT> *_stringSection; + std::vector<Elf_Sym*> _symbolTable; + llvm::BumpPtrAllocator _symbolAllocate; + int64_t _link; +}; + +/// ELF Symbol Table +template <class ELFT> +SymbolTable<ELFT>::SymbolTable(const ELFTargetInfo &ti, const char *str, + int32_t order) + : Section<ELFT>(ti, str, llvm::ELF::SHT_SYMTAB, 0, order, + Section<ELFT>::K_SymbolTable) { + this->setOrder(order); + Elf_Sym *symbol = new (_symbolAllocate.Allocate<Elf_Sym>()) Elf_Sym; + memset((void *)symbol, 0, sizeof(Elf_Sym)); + _symbolTable.push_back(symbol); + this->_entSize = sizeof(Elf_Sym); + this->_fsize = sizeof(Elf_Sym); + this->_align2 = sizeof(void *); +} + +template<class ELFT> +void +SymbolTable<ELFT>::addSymbol(const Atom *atom, + int32_t sectionIndex, + uint64_t addr) { + Elf_Sym *symbol = new(_symbolAllocate.Allocate<Elf_Sym>()) Elf_Sym; + 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<const DefinedAtom>(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::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + symbol->st_value = addr; + type = llvm::ELF::STT_OBJECT; + break; + case DefinedAtom::typeZeroFill: + type = llvm::ELF::STT_OBJECT; + symbol->st_value = addr; + break; + default: + type = llvm::ELF::STT_NOTYPE; + } + if (da->scope() == DefinedAtom::scopeTranslationUnit) + binding = llvm::ELF::STB_LOCAL; + else + binding = llvm::ELF::STB_GLOBAL; + } else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(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 { + symbol->st_value = 0; + type = llvm::ELF::STT_NOTYPE; + binding = llvm::ELF::STB_WEAK; + } + symbol->setBindingAndType(binding, type); + _symbolTable.push_back(symbol); + this->_fsize += sizeof(Elf_Sym); +} + +template<class ELFT> +void +SymbolTable<ELFT>::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 Elf_Sym *A, const Elf_Sym *B) { + return A->getBinding() < B->getBinding(); + }); + uint16_t shInfo = 0; + for (auto i : _symbolTable) { + if (i->getBinding() != llvm::ELF::STB_LOCAL) + break; + shInfo++; + } + this->_shInfo = shInfo; + this->setLink(_stringSection->ordinal()); +} + +template <class ELFT> +void SymbolTable<ELFT>::write(ELFWriter *writer, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto sti : _symbolTable) { + memcpy(dest, sti, sizeof(Elf_Sym)); + dest += sizeof(Elf_Sym); + } +} + +template <class ELFT> class RelocationTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + RelocationTable(const ELFTargetInfo &ti, StringRef str, int32_t order) + : Section<ELFT>(ti, str, llvm::ELF::SHT_RELA, DefinedAtom::permR__, order, + Section<ELFT>::K_Default) { + this->setOrder(order); + this->_entSize = sizeof(Elf_Rela); + this->_align2 = llvm::alignOf<Elf_Rela>(); + } + + void addRelocation(const DefinedAtom &da, const Reference &r) { + _relocs.emplace_back(da, r); + this->_fsize = _relocs.size() * sizeof(Elf_Rela); + this->_msize = this->_fsize; + } + + 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<Elf_Rela *>(dest); + r->setSymbolAndType(0, 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() + << "IRELATIVE relocation at " << rel.first.name() << "@" + << r->r_offset << " to " << rel.second.target()->name() + << "@" << r->r_addend << "\n"); + } + } + +private: + std::vector<std::pair<const DefinedAtom &, const Reference &>> _relocs; +}; +} // end namespace elf +} // end namespace lld + +#endif |

