//===- lib/ReaderWriter/ELF/MipsELFFile.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_MIPS_MIPS_ELF_FILE_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FILE_H #include "ELFReader.h" #include "MipsLinkingContext.h" #include "MipsRelocationHandler.h" namespace llvm { namespace object { template struct Elf_RegInfo; template struct Elf_RegInfo> { LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, false) Elf_Word ri_gprmask; // bit-mask of used general registers Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers Elf_Addr ri_gp_value; // gp register value }; template struct Elf_RegInfo> { LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, true) Elf_Word ri_gprmask; // bit-mask of used general registers Elf_Word ri_pad; // unused padding field Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers Elf_Addr ri_gp_value; // gp register value }; template struct Elf_Mips_Options { LLVM_ELF_IMPORT_TYPES(ELFT::TargetEndianness, ELFT::MaxAlignment, ELFT::Is64Bits) uint8_t kind; // Determines interpretation of variable part of descriptor uint8_t size; // Byte size of descriptor, including this header Elf_Half section; // Section header index of section affected, // or 0 for global options Elf_Word info; // Kind-specific information }; } // end namespace object. } // end namespace llvm. namespace lld { namespace elf { template class MipsELFFile; template class MipsELFDefinedAtom : public ELFDefinedAtom { typedef llvm::object::Elf_Sym_Impl Elf_Sym; typedef llvm::object::Elf_Shdr_Impl Elf_Shdr; public: MipsELFDefinedAtom(const MipsELFFile &file, StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, const Elf_Shdr *section, ArrayRef contentData, unsigned int referenceStart, unsigned int referenceEnd, std::vector *> &referenceList) : ELFDefinedAtom(file, symbolName, sectionName, symbol, section, contentData, referenceStart, referenceEnd, referenceList) {} const MipsELFFile& file() const override { return static_cast &>(this->_owningFile); } DefinedAtom::CodeModel codeModel() const override { switch (this->_symbol->st_other & llvm::ELF::STO_MIPS_MIPS16) { case llvm::ELF::STO_MIPS_MIPS16: return DefinedAtom::codeMips16; case llvm::ELF::STO_MIPS_PIC: return DefinedAtom::codeMipsPIC; case llvm::ELF::STO_MIPS_MICROMIPS: return DefinedAtom::codeMipsMicro; case llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC: return DefinedAtom::codeMipsMicroPIC; default: return DefinedAtom::codeNA; } } }; template class MipsELFReference : public ELFReference { typedef llvm::object::Elf_Rel_Impl Elf_Rel; typedef llvm::object::Elf_Rel_Impl Elf_Rela; static const bool _isMips64EL = ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; public: MipsELFReference(uint64_t symValue, const Elf_Rela &rel) : ELFReference( &rel, rel.r_offset - symValue, Reference::KindArch::Mips, rel.getType(_isMips64EL) & 0xff, rel.getSymbol(_isMips64EL)), _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} MipsELFReference(uint64_t symValue, const Elf_Rel &rel) : ELFReference(rel.r_offset - symValue, Reference::KindArch::Mips, rel.getType(_isMips64EL) & 0xff, rel.getSymbol(_isMips64EL)), _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} uint32_t tag() const override { return _tag; } void setTag(uint32_t tag) { _tag = tag; } private: uint32_t _tag; }; template class MipsELFFile : public ELFFile { public: MipsELFFile(std::unique_ptr mb, MipsLinkingContext &ctx) : ELFFile(std::move(mb), ctx) {} static ErrorOr> create(std::unique_ptr mb, MipsLinkingContext &ctx) { return std::unique_ptr>( new MipsELFFile(std::move(mb), ctx)); } bool isPIC() const { return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; } /// \brief gp register value stored in the .reginfo section. int64_t getGP0() const { return _gp0 ? *_gp0 : 0; } /// \brief .tdata section address plus fixed offset. uint64_t getTPOffset() const { return *_tpOff; } uint64_t getDTPOffset() const { return *_dtpOff; } protected: std::error_code doParse() override { if (std::error_code ec = ELFFile::doParse()) return ec; // Retrieve some auxiliary data like GP value, TLS section address etc // from the object file. return readAuxData(); } private: typedef llvm::object::Elf_Sym_Impl Elf_Sym; typedef llvm::object::Elf_Shdr_Impl Elf_Shdr; typedef llvm::object::Elf_Rel_Impl Elf_Rel; typedef typename llvm::object::ELFFile::Elf_Rel_Iter Elf_Rel_Iter; typedef typename llvm::object::ELFFile::Elf_Rela_Iter Elf_Rela_Iter; enum { TP_OFFSET = 0x7000, DTP_OFFSET = 0x8000 }; static const bool _isMips64EL = ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; llvm::Optional _gp0; llvm::Optional _tpOff; llvm::Optional _dtpOff; ErrorOr *> handleDefinedSymbol( StringRef symName, StringRef sectionName, const Elf_Sym *sym, const Elf_Shdr *sectionHdr, ArrayRef contentData, unsigned int referenceStart, unsigned int referenceEnd, std::vector *> &referenceList) override { return new (this->_readerStorage) MipsELFDefinedAtom( *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, referenceEnd, referenceList); } const Elf_Shdr *findSectionByType(uint64_t type) { for (const Elf_Shdr §ion : this->_objFile->sections()) if (section.sh_type == type) return §ion; return nullptr; } const Elf_Shdr *findSectionByFlags(uint64_t flags) { for (const Elf_Shdr §ion : this->_objFile->sections()) if (section.sh_flags & flags) return §ion; return nullptr; } std::error_code readAuxData() { using namespace llvm::ELF; if (const Elf_Shdr *sec = findSectionByFlags(SHF_TLS)) { _tpOff = sec->sh_addr + TP_OFFSET; _dtpOff = sec->sh_addr + DTP_OFFSET; } typedef llvm::object::Elf_RegInfo Elf_RegInfo; typedef llvm::object::Elf_Mips_Options Elf_Mips_Options; if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_OPTIONS)) { auto contents = this->getSectionContents(sec); if (std::error_code ec = contents.getError()) return ec; ArrayRef raw = contents.get(); while (!raw.empty()) { if (raw.size() < sizeof(Elf_Mips_Options)) return make_dynamic_error_code( StringRef("Invalid size of MIPS_OPTIONS section")); const auto *opt = reinterpret_cast(raw.data()); if (opt->kind == ODK_REGINFO) { _gp0 = reinterpret_cast(opt + 1)->ri_gp_value; break; } raw = raw.slice(opt->size); } } else if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_REGINFO)) { auto contents = this->getSectionContents(sec); if (std::error_code ec = contents.getError()) return ec; ArrayRef raw = contents.get(); if (raw.size() != sizeof(Elf_RegInfo)) return make_dynamic_error_code( StringRef("Invalid size of MIPS_REGINFO section")); _gp0 = reinterpret_cast(raw.data())->ri_gp_value; } return std::error_code(); } void createRelocationReferences(const Elf_Sym *symbol, ArrayRef content, range rels) override { const auto value = this->getSymbolValue(symbol); for (const auto &rel : rels) { if (rel.r_offset < value || value + content.size() <= rel.r_offset) continue; auto r = new (this->_readerStorage) MipsELFReference(value, rel); this->addReferenceToSymbol(r, symbol); this->_references.push_back(r); } } void createRelocationReferences(const Elf_Sym *symbol, ArrayRef symContent, ArrayRef secContent, range rels) override { const auto value = this->getSymbolValue(symbol); for (Elf_Rel_Iter rit = rels.begin(), eit = rels.end(); rit != eit; ++rit) { if (rit->r_offset < value || value + symContent.size() <= rit->r_offset) continue; auto r = new (this->_readerStorage) MipsELFReference(value, *rit); this->addReferenceToSymbol(r, symbol); this->_references.push_back(r); auto addend = readAddend(*rit, secContent); auto pairRelType = getPairRelocation(*rit); if (pairRelType != llvm::ELF::R_MIPS_NONE) { addend <<= 16; auto mit = findMatchingRelocation(pairRelType, rit, eit); if (mit != eit) addend += int16_t(readAddend(*mit, secContent)); else // FIXME (simon): Show detailed warning. llvm::errs() << "lld warning: cannot matching LO16 relocation\n"; } this->_references.back()->setAddend(addend); } } Reference::Addend readAddend(const Elf_Rel &ri, const ArrayRef content) const { const auto &rh = this->_ctx.template getTargetHandler().getRelocationHandler(); return static_cast(rh) .readAddend(getPrimaryType(ri), content.data() + ri.r_offset); } uint32_t getPairRelocation(const Elf_Rel &rel) const { switch (getPrimaryType(rel)) { case llvm::ELF::R_MIPS_HI16: return llvm::ELF::R_MIPS_LO16; case llvm::ELF::R_MIPS_PCHI16: return llvm::ELF::R_MIPS_PCLO16; case llvm::ELF::R_MIPS_GOT16: if (isLocalBinding(rel)) return llvm::ELF::R_MIPS_LO16; break; case llvm::ELF::R_MICROMIPS_HI16: return llvm::ELF::R_MICROMIPS_LO16; case llvm::ELF::R_MICROMIPS_GOT16: if (isLocalBinding(rel)) return llvm::ELF::R_MICROMIPS_LO16; break; default: // Nothing to do. break; } return llvm::ELF::R_MIPS_NONE; } Elf_Rel_Iter findMatchingRelocation(uint32_t pairRelType, Elf_Rel_Iter rit, Elf_Rel_Iter eit) const { return std::find_if(rit, eit, [&](const Elf_Rel &rel) { return getPrimaryType(rel) == pairRelType && rel.getSymbol(_isMips64EL) == rit->getSymbol(_isMips64EL); }); } static uint8_t getPrimaryType(const Elf_Rel &rel) { return rel.getType(_isMips64EL) & 0xff; } bool isLocalBinding(const Elf_Rel &rel) const { return this->_objFile->getSymbol(rel.getSymbol(_isMips64EL)) ->getBinding() == llvm::ELF::STB_LOCAL; } }; template class MipsDynamicFile : public DynamicFile { public: MipsDynamicFile(const MipsLinkingContext &context, StringRef name) : DynamicFile(context, name) {} }; } // elf } // lld #endif