285 lines
9.8 KiB
C++
285 lines
9.8 KiB
C++
//===- 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"
|
|
|
|
namespace lld {
|
|
namespace elf {
|
|
|
|
template <class ELFT>
|
|
MipsELFDefinedAtom<ELFT>::MipsELFDefinedAtom(
|
|
const MipsELFFile<ELFT> &file, StringRef symbolName, StringRef sectionName,
|
|
const Elf_Sym *symbol, const Elf_Shdr *section,
|
|
ArrayRef<uint8_t> contentData, unsigned int referenceStart,
|
|
unsigned int referenceEnd, std::vector<ELFReference<ELFT> *> &referenceList)
|
|
: ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section,
|
|
contentData, referenceStart, referenceEnd,
|
|
referenceList) {}
|
|
|
|
template <class ELFT>
|
|
const MipsELFFile<ELFT> &MipsELFDefinedAtom<ELFT>::file() const {
|
|
return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile);
|
|
}
|
|
|
|
template <class ELFT>
|
|
DefinedAtom::CodeModel MipsELFDefinedAtom<ELFT>::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 class MipsELFDefinedAtom<ELF32LE>;
|
|
template class MipsELFDefinedAtom<ELF64LE>;
|
|
|
|
template <class ELFT> static bool isMips64EL() {
|
|
return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little;
|
|
}
|
|
|
|
template <class ELFT, bool isRela>
|
|
static uint32_t
|
|
extractTag(const llvm::object::Elf_Rel_Impl<ELFT, isRela> &rel) {
|
|
return (rel.getType(isMips64EL<ELFT>()) & 0xffffff00) >> 8;
|
|
}
|
|
|
|
template <class ELFT>
|
|
MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rela &rel)
|
|
: ELFReference<ELFT>(&rel, rel.r_offset - symValue,
|
|
Reference::KindArch::Mips,
|
|
rel.getType(isMips64EL<ELFT>()) & 0xff,
|
|
rel.getSymbol(isMips64EL<ELFT>())),
|
|
_tag(extractTag(rel)) {}
|
|
|
|
template <class ELFT>
|
|
MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rel &rel)
|
|
: ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips,
|
|
rel.getType(isMips64EL<ELFT>()) & 0xff,
|
|
rel.getSymbol(isMips64EL<ELFT>())),
|
|
_tag(extractTag(rel)) {}
|
|
|
|
template class MipsELFReference<ELF32LE>;
|
|
template class MipsELFReference<ELF64LE>;
|
|
|
|
template <class ELFT>
|
|
MipsELFFile<ELFT>::MipsELFFile(std::unique_ptr<MemoryBuffer> mb,
|
|
ELFLinkingContext &ctx)
|
|
: ELFFile<ELFT>(std::move(mb), ctx) {}
|
|
|
|
template <class ELFT> bool MipsELFFile<ELFT>::isPIC() const {
|
|
return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC;
|
|
}
|
|
|
|
template <class ELFT> std::error_code MipsELFFile<ELFT>::doParse() {
|
|
if (std::error_code ec = ELFFile<ELFT>::doParse())
|
|
return ec;
|
|
// Retrieve some auxiliary data like GP value, TLS section address etc
|
|
// from the object file.
|
|
return readAuxData();
|
|
}
|
|
|
|
template <class ELFT>
|
|
ELFDefinedAtom<ELFT> *MipsELFFile<ELFT>::createDefinedAtom(
|
|
StringRef symName, StringRef sectionName, const Elf_Sym *sym,
|
|
const Elf_Shdr *sectionHdr, ArrayRef<uint8_t> contentData,
|
|
unsigned int referenceStart, unsigned int referenceEnd,
|
|
std::vector<ELFReference<ELFT> *> &referenceList) {
|
|
return new (this->_readerStorage) MipsELFDefinedAtom<ELFT>(
|
|
*this, symName, sectionName, sym, sectionHdr, contentData, referenceStart,
|
|
referenceEnd, referenceList);
|
|
}
|
|
|
|
template <class ELFT>
|
|
const typename MipsELFFile<ELFT>::Elf_Shdr *
|
|
MipsELFFile<ELFT>::findSectionByType(uint64_t type) const {
|
|
for (const Elf_Shdr §ion : this->_objFile->sections())
|
|
if (section.sh_type == type)
|
|
return §ion;
|
|
return nullptr;
|
|
}
|
|
|
|
template <class ELFT>
|
|
const typename MipsELFFile<ELFT>::Elf_Shdr *
|
|
MipsELFFile<ELFT>::findSectionByFlags(uint64_t flags) const {
|
|
for (const Elf_Shdr §ion : this->_objFile->sections())
|
|
if (section.sh_flags & flags)
|
|
return §ion;
|
|
return nullptr;
|
|
}
|
|
|
|
template <class ELFT>
|
|
ErrorOr<const typename MipsELFFile<ELFT>::Elf_Mips_RegInfo *>
|
|
MipsELFFile<ELFT>::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<uint8_t> 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<const Elf_Mips_Options *>(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<uint8_t> 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<const Elf_Mips_RegInfo *>(raw.data());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template <class ELFT> std::error_code MipsELFFile<ELFT>::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 &ctx = static_cast<MipsLinkingContext &>(this->_ctx);
|
|
|
|
ErrorOr<const Elf_Mips_RegInfo *> regInfoSec = findRegInfoSec();
|
|
if (auto ec = regInfoSec.getError())
|
|
return ec;
|
|
if (const Elf_Mips_RegInfo *regInfo = regInfoSec.get()) {
|
|
ctx.mergeReginfoMask(*regInfo);
|
|
_gp0 = regInfo->ri_gp_value;
|
|
}
|
|
|
|
const Elf_Ehdr *hdr = this->_objFile->getHeader();
|
|
if (std::error_code ec = ctx.mergeElfFlags(hdr->e_flags))
|
|
return ec;
|
|
|
|
return std::error_code();
|
|
}
|
|
|
|
template <class ELFT>
|
|
void MipsELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol,
|
|
ArrayRef<uint8_t> content,
|
|
range<Elf_Rela_Iter> rels) {
|
|
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<ELFT>(value, rel);
|
|
this->addReferenceToSymbol(r, symbol);
|
|
this->_references.push_back(r);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
void MipsELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol,
|
|
ArrayRef<uint8_t> symContent,
|
|
ArrayRef<uint8_t> secContent,
|
|
range<Elf_Rel_Iter> rels) {
|
|
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<ELFT>(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);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
static uint8_t
|
|
getPrimaryType(const llvm::object::Elf_Rel_Impl<ELFT, false> &rel) {
|
|
return rel.getType(isMips64EL<ELFT>()) & 0xff;
|
|
}
|
|
|
|
template <class ELFT>
|
|
Reference::Addend
|
|
MipsELFFile<ELFT>::readAddend(const Elf_Rel &ri,
|
|
const ArrayRef<uint8_t> content) const {
|
|
return readMipsRelocAddend(getPrimaryType(ri), content.data() + ri.r_offset);
|
|
}
|
|
|
|
template <class ELFT>
|
|
uint32_t MipsELFFile<ELFT>::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;
|
|
}
|
|
|
|
template <class ELFT>
|
|
typename MipsELFFile<ELFT>::Elf_Rel_Iter
|
|
MipsELFFile<ELFT>::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<ELFT>()) ==
|
|
rit->getSymbol(isMips64EL<ELFT>());
|
|
});
|
|
}
|
|
|
|
template <class ELFT>
|
|
bool MipsELFFile<ELFT>::isLocalBinding(const Elf_Rel &rel) const {
|
|
return this->_objFile->getSymbol(rel.getSymbol(isMips64EL<ELFT>()))
|
|
->getBinding() == llvm::ELF::STB_LOCAL;
|
|
}
|
|
|
|
template class MipsELFFile<ELF32LE>;
|
|
template class MipsELFFile<ELF64LE>;
|
|
|
|
} // elf
|
|
} // lld
|