//===- lib/ReaderWriter/ELF/MipsELFFile.cpp -------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "MipsELFFile.h" #include "MipsTargetHandler.h" #include "llvm/ADT/StringExtras.h" namespace lld { namespace elf { template MipsELFDefinedAtom::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) {} template const MipsELFFile &MipsELFDefinedAtom::file() const { return static_cast &>(this->_owningFile); } template DefinedAtom::CodeModel MipsELFDefinedAtom::codeModel() const { 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 bool MipsELFDefinedAtom::isPIC() const { return file().isPIC() || codeModel() == DefinedAtom::codeMipsMicroPIC || codeModel() == DefinedAtom::codeMipsPIC; } template class MipsELFDefinedAtom; template class MipsELFDefinedAtom; template class MipsELFDefinedAtom; template class MipsELFDefinedAtom; template static bool isMips64EL() { return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; } template static uint32_t extractTag(const llvm::object::Elf_Rel_Impl &rel) { return (rel.getType(isMips64EL()) & 0xffffff00) >> 8; } template MipsELFReference::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(extractTag(rel)) {} template MipsELFReference::MipsELFReference(uint64_t symValue, const Elf_Rel &rel) : ELFReference(rel.r_offset - symValue, Reference::KindArch::Mips, rel.getType(isMips64EL()) & 0xff, rel.getSymbol(isMips64EL())), _tag(extractTag(rel)) {} template class MipsELFReference; template class MipsELFReference; template class MipsELFReference; template class MipsELFReference; template MipsELFFile::MipsELFFile(std::unique_ptr mb, ELFLinkingContext &ctx) : ELFFile(std::move(mb), ctx) {} template bool MipsELFFile::isPIC() const { return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; } template std::error_code MipsELFFile::doParse() { 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(); } template ELFDefinedAtom *MipsELFFile::createDefinedAtom( StringRef symName, StringRef sectionName, const Elf_Sym *sym, const Elf_Shdr *sectionHdr, ArrayRef contentData, unsigned int referenceStart, unsigned int referenceEnd, std::vector *> &referenceList) { return new (this->_readerStorage) MipsELFDefinedAtom( *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, referenceEnd, referenceList); } template const typename MipsELFFile::Elf_Shdr * MipsELFFile::findSectionByType(uint64_t type) const { for (const Elf_Shdr §ion : this->_objFile->sections()) if (section.sh_type == type) return §ion; return nullptr; } template const typename MipsELFFile::Elf_Shdr * MipsELFFile::findSectionByFlags(uint64_t flags) const { for (const Elf_Shdr §ion : this->_objFile->sections()) if (section.sh_flags & flags) return §ion; return nullptr; } template ErrorOr::Elf_Mips_RegInfo *> MipsELFFile::findRegInfoSec() const { using namespace llvm::ELF; 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) return &opt->getRegInfo(); 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_Mips_RegInfo)) return make_dynamic_error_code( StringRef("Invalid size of MIPS_REGINFO section")); return reinterpret_cast(raw.data()); } return nullptr; } template ErrorOr::Elf_Mips_ABIFlags *> MipsELFFile::findAbiFlagsSec() const { const Elf_Shdr *sec = findSectionByType(SHT_MIPS_ABIFLAGS); if (!sec) return nullptr; auto contents = this->getSectionContents(sec); if (std::error_code ec = contents.getError()) return ec; ArrayRef raw = contents.get(); if (raw.size() != sizeof(Elf_Mips_ABIFlags)) return make_dynamic_error_code( StringRef("Invalid size of MIPS_ABIFLAGS section")); const auto *abi = reinterpret_cast(raw.data()); if (abi->version != 0) return make_dynamic_error_code( StringRef(".MIPS.abiflags section has unsupported version '") + llvm::utostr(abi->version) + "'"); return abi; } template std::error_code MipsELFFile::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; } auto &handler = static_cast &>(this->_ctx.getTargetHandler()); auto &abi = handler.getAbiInfoHandler(); ErrorOr regInfoSec = findRegInfoSec(); if (auto ec = regInfoSec.getError()) return ec; if (const Elf_Mips_RegInfo *regInfo = regInfoSec.get()) { abi.mergeRegistersMask(*regInfo); _gp0 = regInfo->ri_gp_value; } ErrorOr abiFlagsSec = findAbiFlagsSec(); if (auto ec = abiFlagsSec.getError()) return ec; const Elf_Ehdr *hdr = this->_objFile->getHeader(); if (std::error_code ec = abi.mergeFlags(hdr->e_flags, abiFlagsSec.get())) return ec; return std::error_code(); } template void MipsELFFile::createRelocationReferences( const Elf_Sym *symbol, ArrayRef content, range rels) { const auto value = this->getSymbolValue(symbol); unsigned numInGroup = 0; for (const auto &rel : rels) { if (rel.r_offset < value || value + content.size() <= rel.r_offset) { numInGroup = 0; continue; } if (numInGroup > 0) { auto &last = *static_cast *>(this->_references.back()); if (last.offsetInAtom() + value == rel.r_offset) { last.setTag(last.tag() | (rel.getType(isMips64EL()) << 8 * (numInGroup - 1))); ++numInGroup; continue; } } auto r = new (this->_readerStorage) MipsELFReference(value, rel); this->addReferenceToSymbol(r, symbol); this->_references.push_back(r); numInGroup = 1; } } template void MipsELFFile::createRelocationReferences(const Elf_Sym *symbol, ArrayRef symContent, ArrayRef secContent, const Elf_Shdr *relSec) { const Elf_Shdr *symtab = *this->_objFile->getSection(relSec->sh_link); auto rels = this->_objFile->rels(relSec); const auto value = this->getSymbolValue(symbol); for (const Elf_Rel *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(symtab, *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); } } template static uint8_t getPrimaryType(const llvm::object::Elf_Rel_Impl &rel) { return rel.getType(isMips64EL()) & 0xff; } template Reference::Addend MipsELFFile::readAddend(const Elf_Rel &ri, const ArrayRef content) const { return readMipsRelocAddend(getPrimaryType(ri), content.data() + ri.r_offset); } template uint32_t MipsELFFile::getPairRelocation(const Elf_Shdr *symtab, 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(symtab, 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(symtab, rel)) return llvm::ELF::R_MICROMIPS_LO16; break; default: // Nothing to do. break; } return llvm::ELF::R_MIPS_NONE; } template const typename MipsELFFile::Elf_Rel * MipsELFFile::findMatchingRelocation(uint32_t pairRelType, const Elf_Rel *rit, const Elf_Rel *eit) const { return std::find_if(rit, eit, [&](const Elf_Rel &rel) { return getPrimaryType(rel) == pairRelType && rel.getSymbol(isMips64EL()) == rit->getSymbol(isMips64EL()); }); } template bool MipsELFFile::isLocalBinding(const Elf_Shdr *symtab, const Elf_Rel &rel) const { return this->_objFile->getSymbol(symtab, rel.getSymbol(isMips64EL())) ->getBinding() == llvm::ELF::STB_LOCAL; } template class MipsELFFile; template class MipsELFFile; template class MipsELFFile; template class MipsELFFile; } // elf } // lld