blob: 020440568af352881b871cbc648545672fdfa8c0 [file]
//===-- ELF_mips.cpp - JIT linker implementation for ELF/MIPS ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/JITLink/ELF_mips.h"
#include "EHFrameSupportImpl.h"
#include "ELFLinkGraphBuilder.h"
#include "JITLinkGeneric.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
#include "llvm/ExecutionEngine/JITLink/mips.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MipsABIFlags.h"
#include "llvm/Support/raw_ostream.h"
#include <tuple>
#define DEBUG_TYPE "jitlink"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::jitlink::mips;
namespace {
constexpr StringRef GOTSectionName = "$__GOT";
constexpr StringRef StubsSectionName = "$__STUBS";
constexpr StringRef TLSInfoSectionName = "$__TLSINFO";
constexpr StringRef TLSBaseName = "__jitlink_mips_tls_base";
constexpr StringRef GOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
constexpr StringRef GPName = "_gp";
constexpr StringRef GPDispName = "_gp_disp";
constexpr StringRef LocalGPName = "__gnu_local_gp";
constexpr unsigned RelocationTypeBits = 8;
constexpr unsigned MaxRelocationOperations = 3;
constexpr int64_t CompactBranchPCBias = InstructionSize;
// The MIPS psABI defines $gp as 0x7ff0 bytes past the GOT base.
constexpr uint64_t GPAnchorOffset = 0x7ff0;
enum class MipsABI { O32, N32, N64 };
static Edge::Kind getPagePointerEdgeKind(const LinkGraph &G) {
return G.getPointerSize() == 8 ? PagePointer64 : PagePointer32;
}
template <unsigned Bits, unsigned Shift>
static int64_t decodeInstructionImmediate(uint32_t Instruction) {
static_assert(Bits + Shift <= 32);
uint32_t Value = (Instruction & maskTrailingOnes<uint32_t>(Bits)) << Shift;
return SignExtend64<Bits + Shift>(Value);
}
static uint8_t getPackedRelocationField(uint32_t Packed, unsigned Index) {
return static_cast<uint8_t>((Packed >> (Index * RelocationTypeBits)) &
maskTrailingOnes<uint32_t>(RelocationTypeBits));
}
static std::string getRelocationChainName(ArrayRef<uint8_t> Types) {
std::string Result;
raw_string_ostream OS(Result);
for (auto [Index, Type] : llvm::enumerate(Types)) {
if (Index)
OS << '/';
OS << object::getELFRelocationTypeName(ELF::EM_MIPS, Type);
}
return Result;
}
static unsigned getFixupSize(Edge::Kind K) {
switch (K) {
case Abs16:
return sizeof(uint16_t);
case Pointer64:
case PagePointer64:
case Delta64:
case GPRel64:
case DTPRel64:
return sizeof(uint64_t);
case Pointer32:
case PagePointer32:
case Delta32:
case NegDelta32:
case Hi16:
case Lo16:
case Higher16:
case Highest16:
case Jump26:
case PC16:
case PC32:
case PC18S3:
case PC19S2:
case PC21S2:
case PC26S2:
case PCHi16:
case PCLo16:
case GPDispHi16:
case GPDispLo16:
case GPRel16:
case GPRel32:
case GOTOffset16:
case GOTOffsetHi16:
case GOTOffsetLo16:
case GOTPageOffset16:
case DTPRelHi16:
case DTPRelLo16:
case DTPRel32:
case NegGPRelHi16:
case NegGPRelLo16:
case RequestGOTAndTransformToOffset16:
case RequestGOTPageAndTransformToOffset16:
case RequestGOTAndTransformToOffsetHi16:
case RequestGOTAndTransformToOffsetLo16:
case RequestTLSGDAndTransformToOffset16:
case RequestTLSLDMAndTransformToOffset16:
return sizeof(uint32_t);
default:
llvm_unreachable("not a MIPS relocation edge");
}
}
class ELFJITLinker_mips : public JITLinker<ELFJITLinker_mips> {
friend class JITLinker<ELFJITLinker_mips>;
public:
ELFJITLinker_mips(std::unique_ptr<JITLinkContext> Ctx,
std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {
getPassConfig().PreFixupPasses.push_back([this](LinkGraph &G) {
GPSymbol = G.findDefinedSymbolByName(G.intern(GPName));
TLSBaseSymbol = G.findDefinedSymbolByName(G.intern(TLSBaseName));
return Error::success();
});
}
private:
Symbol *GPSymbol = nullptr;
Symbol *TLSBaseSymbol = nullptr;
Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
return mips::applyFixup(G, B, E, GPSymbol, TLSBaseSymbol);
}
};
template <typename ELFT>
class ELFLinkGraphBuilder_mips : public ELFLinkGraphBuilder<ELFT> {
using Base = ELFLinkGraphBuilder<ELFT>;
using Rel = typename ELFT::Rel;
using Rela = typename ELFT::Rela;
struct Reloc {
uint64_t Offset;
uint32_t Symbol;
SmallVector<uint8_t, MaxRelocationOperations> Types;
int64_t Addend;
};
public:
ELFLinkGraphBuilder_mips(StringRef FileName, const object::ELFFile<ELFT> &Obj,
std::shared_ptr<orc::SymbolStringPool> SSP,
Triple TT, SubtargetFeatures Features, MipsABI ABI)
: Base(Obj, std::move(SSP), std::move(TT), std::move(Features), FileName,
mips::getEdgeKindName),
ABI(ABI) {}
private:
MipsABI ABI;
bool excludeSection(const typename ELFT::Shdr &Sec) const override {
return Sec.sh_type == ELF::SHT_MIPS_ABIFLAGS ||
Sec.sh_type == ELF::SHT_MIPS_REGINFO ||
Sec.sh_type == ELF::SHT_MIPS_OPTIONS;
}
Error error(const typename ELFT::Shdr &FixupSect, uint64_t Offset,
const Twine &Message) const {
auto Name = Base::Obj.getSectionName(FixupSect);
if (!Name)
return Name.takeError();
return make_error<JITLinkError>(Base::G->getName() + ": section " + *Name +
"+0x" + utohexstr(Offset) + ": " + Message);
}
Expected<int64_t> implicitAddend(const typename ELFT::Shdr &RelSect,
uint8_t Type, const Block &B,
uint64_t Offset) const {
if (Type == ELF::R_MIPS_NONE || Type == ELF::R_MIPS_JALR)
return 0;
unsigned FixupSize = Type == ELF::R_MIPS_16 ? sizeof(uint16_t)
: Type == ELF::R_MIPS_64 ? sizeof(uint64_t)
: sizeof(uint32_t);
if (Offset > B.getSize() || FixupSize > B.getSize() - Offset)
return error(RelSect, Offset,
"relocation fixup extends past the end of its block");
const char *P = B.getContent().data() + Offset;
if (Type == ELF::R_MIPS_16)
return SignExtend64<16>(support::endian::read16<ELFT::Endianness>(P));
if (Type == ELF::R_MIPS_64)
return support::endian::read64<ELFT::Endianness>(P);
uint32_t W = support::endian::read32<ELFT::Endianness>(P);
switch (Type) {
case ELF::R_MIPS_32:
case ELF::R_MIPS_GPREL32:
case ELF::R_MIPS_PC32:
case ELF::R_MIPS_TLS_DTPREL32:
return SignExtend64<32>(W);
case ELF::R_MIPS_26:
return decodeInstructionImmediate<26, 2>(W);
case ELF::R_MIPS_HI16:
case ELF::R_MIPS_PCHI16:
case ELF::R_MIPS_GOT16:
case ELF::R_MIPS_GOT_HI16:
case ELF::R_MIPS_CALL_HI16:
return decodeInstructionImmediate<16, 16>(W);
case ELF::R_MIPS_PC16:
return decodeInstructionImmediate<16, 2>(W);
case ELF::R_MIPS_PC18_S3:
return decodeInstructionImmediate<18, 3>(W);
case ELF::R_MIPS_PC19_S2:
return decodeInstructionImmediate<19, 2>(W);
case ELF::R_MIPS_PC21_S2:
return decodeInstructionImmediate<21, 2>(W);
case ELF::R_MIPS_PC26_S2:
return decodeInstructionImmediate<26, 2>(W);
default:
return decodeInstructionImmediate<16, 0>(W);
}
}
static uint8_t matchingLow(uint8_t Type, bool IsLocal) {
switch (Type) {
case ELF::R_MIPS_HI16:
return ELF::R_MIPS_LO16;
case ELF::R_MIPS_PCHI16:
return ELF::R_MIPS_PCLO16;
case ELF::R_MIPS_GOT16:
return IsLocal ? ELF::R_MIPS_LO16 : ELF::R_MIPS_NONE;
default:
return ELF::R_MIPS_NONE;
}
}
Error validateSymbols() {
if (!Base::SymTabSec)
return Error::success();
auto Symbols = Base::Obj.symbols(Base::SymTabSec);
if (!Symbols)
return Symbols.takeError();
for (const auto &Sym : *Symbols)
if ((Sym.st_other & ELF::STO_MIPS_MICROMIPS) ||
(Sym.st_other & ELF::STO_MIPS_MIPS16) == ELF::STO_MIPS_MIPS16)
return make_error<JITLinkError>(
Base::G->getName() +
": compact-mode MIPS16/microMIPS symbols are unsupported");
return Error::success();
}
Error collectRelocs(const typename ELFT::Shdr &RelSect, Block &B,
SmallVectorImpl<Reloc> &Out) {
if (RelSect.sh_type == ELF::SHT_RELA) {
auto Rs = Base::Obj.relas(RelSect);
if (!Rs)
return Rs.takeError();
for (auto I = Rs->begin(), E = Rs->end(); I != E;) {
const Rela &R = *I++;
if (ABI == MipsABI::N32) {
Reloc RR{R.r_offset, R.getSymbol(false), {}, R.r_addend};
RR.Types.push_back(R.getType(false));
while (I != E && I->r_offset == R.r_offset) {
if (RR.Types.size() == MaxRelocationOperations)
return error(RelSect, R.r_offset,
"more than three N32 relocation operations");
if (I->getSymbol(false) != 0)
return error(RelSect, R.r_offset,
"non-zero symbol in a secondary N32 relocation");
RR.Types.push_back(I->getType(false));
++I;
}
Out.push_back(std::move(RR));
} else {
uint32_t PackedType = R.getType(Base::Obj.isMips64EL());
Reloc RR{
R.r_offset, R.getSymbol(Base::Obj.isMips64EL()), {}, R.r_addend};
uint8_t T2 = getPackedRelocationField(PackedType, 1);
uint8_t T3 = getPackedRelocationField(PackedType, 2);
RR.Types.push_back(getPackedRelocationField(PackedType, 0));
if (T2 || T3)
RR.Types.push_back(T2);
if (T3)
RR.Types.push_back(T3);
uint8_t SSym = getPackedRelocationField(PackedType, 3);
if (SSym != ELF::RSS_UNDEF)
return error(RelSect, R.r_offset,
"unsupported packed relocation special symbol " +
Twine(SSym));
Out.push_back(std::move(RR));
}
}
return Error::success();
}
auto Rs = Base::Obj.rels(RelSect);
if (!Rs)
return Rs.takeError();
for (auto I = Rs->begin(), E = Rs->end(); I != E;) {
const Rel &R = *I++;
Reloc RR{R.r_offset, R.getSymbol(false), {}, 0};
RR.Types.push_back(R.getType(false));
if (ABI == MipsABI::N32) {
while (I != E && I->r_offset == R.r_offset) {
if (RR.Types.size() == MaxRelocationOperations)
return error(RelSect, R.r_offset,
"more than three N32 relocation operations");
if (I->getSymbol(false) != 0)
return error(RelSect, R.r_offset,
"non-zero symbol in a secondary N32 relocation");
RR.Types.push_back(I->getType(false));
++I;
}
}
Out.push_back(std::move(RR));
}
for (size_t I = 0; I != Out.size(); ++I) {
auto &R = Out[I];
uint8_t FinalType = R.Types.back();
auto Addend = implicitAddend(RelSect, FinalType, B, R.Offset);
if (!Addend)
return Addend.takeError();
R.Addend = *Addend;
bool IsLocal = false;
if (auto *S = Base::getGraphSymbol(R.Symbol))
IsLocal = S->getScope() == Scope::Local;
uint8_t LowType = matchingLow(R.Types.front(), IsLocal);
if (LowType == ELF::R_MIPS_NONE)
continue;
bool Found = false;
for (size_t J = I + 1; J != Out.size(); ++J)
if (Out[J].Types.front() == LowType && Out[J].Symbol == R.Symbol) {
auto LowAddend = implicitAddend(RelSect, LowType, B, Out[J].Offset);
if (!LowAddend)
return LowAddend.takeError();
R.Addend += *LowAddend;
Found = true;
break;
}
if (!Found)
return error(RelSect, R.Offset,
"unmatched " +
Twine(object::getELFRelocationTypeName(
ELF::EM_MIPS, R.Types.front())) +
" relocation");
}
return Error::success();
}
Expected<Edge::Kind> edgeKind(const Reloc &R, Symbol &Target) const {
if (R.Types.size() > 1) {
if (R.Types.size() == 2 && R.Types[1] == ELF::R_MIPS_64) {
switch (R.Types[0]) {
case ELF::R_MIPS_32:
case ELF::R_MIPS_64:
return Pointer64;
case ELF::R_MIPS_PC32:
return Delta64;
case ELF::R_MIPS_GPREL32:
return GPRel64;
default:
break;
}
}
if (R.Types.size() == 3 && R.Types[0] == ELF::R_MIPS_GPREL16 &&
R.Types[1] == ELF::R_MIPS_SUB && R.Types[2] == ELF::R_MIPS_HI16)
return NegGPRelHi16;
if (R.Types.size() == 3 && R.Types[0] == ELF::R_MIPS_GPREL16 &&
R.Types[1] == ELF::R_MIPS_SUB && R.Types[2] == ELF::R_MIPS_LO16)
return NegGPRelLo16;
return make_error<JITLinkError>("unsupported packed MIPS relocation " +
getRelocationChainName(R.Types));
}
StringRef Name = Target.hasName() ? *Target.getName() : StringRef();
switch (R.Types.front()) {
case ELF::R_MIPS_16:
return Abs16;
case ELF::R_MIPS_32:
return Pointer32;
case ELF::R_MIPS_64:
return Pointer64;
case ELF::R_MIPS_HI16:
return Name == GPDispName ? GPDispHi16 : Hi16;
case ELF::R_MIPS_LO16:
return Name == GPDispName ? GPDispLo16 : Lo16;
case ELF::R_MIPS_HIGHER:
return Higher16;
case ELF::R_MIPS_HIGHEST:
return Highest16;
case ELF::R_MIPS_26:
return Jump26;
case ELF::R_MIPS_PC16:
return PC16;
case ELF::R_MIPS_PC32:
return PC32;
case ELF::R_MIPS_PC18_S3:
return PC18S3;
case ELF::R_MIPS_PC19_S2:
return PC19S2;
case ELF::R_MIPS_PC21_S2:
return PC21S2;
case ELF::R_MIPS_PC26_S2:
return PC26S2;
case ELF::R_MIPS_PCHI16:
return PCHi16;
case ELF::R_MIPS_PCLO16:
return PCLo16;
case ELF::R_MIPS_GPREL16:
return GPRel16;
case ELF::R_MIPS_GPREL32:
return GPRel32;
case ELF::R_MIPS_GOT16:
return Target.getScope() == Scope::Local
? RequestGOTPageAndTransformToOffset16
: RequestGOTAndTransformToOffset16;
case ELF::R_MIPS_CALL16:
case ELF::R_MIPS_GOT_DISP:
return RequestGOTAndTransformToOffset16;
case ELF::R_MIPS_GOT_PAGE:
return RequestGOTPageAndTransformToOffset16;
case ELF::R_MIPS_GOT_OFST:
return GOTPageOffset16;
case ELF::R_MIPS_GOT_HI16:
case ELF::R_MIPS_CALL_HI16:
return RequestGOTAndTransformToOffsetHi16;
case ELF::R_MIPS_GOT_LO16:
case ELF::R_MIPS_CALL_LO16:
return RequestGOTAndTransformToOffsetLo16;
case ELF::R_MIPS_TLS_GD:
return RequestTLSGDAndTransformToOffset16;
case ELF::R_MIPS_TLS_LDM:
return RequestTLSLDMAndTransformToOffset16;
case ELF::R_MIPS_TLS_DTPREL_HI16:
return DTPRelHi16;
case ELF::R_MIPS_TLS_DTPREL_LO16:
return DTPRelLo16;
case ELF::R_MIPS_TLS_DTPREL32:
return DTPRel32;
case ELF::R_MIPS_TLS_DTPREL64:
return DTPRel64;
case ELF::R_MIPS_TLS_GOTTPREL:
case ELF::R_MIPS_TLS_TPREL32:
case ELF::R_MIPS_TLS_TPREL64:
case ELF::R_MIPS_TLS_TPREL_HI16:
case ELF::R_MIPS_TLS_TPREL_LO16:
return make_error<JITLinkError>(
"initial/local-exec MIPS TLS relocations are unsupported");
default:
return make_error<JITLinkError>("unsupported MIPS relocation " +
Twine(object::getELFRelocationTypeName(
ELF::EM_MIPS, R.Types.front())));
}
}
Error addRelocations() override {
if (auto Err = validateSymbols())
return Err;
for (const auto &RelSect : Base::Sections) {
if (RelSect.sh_type != ELF::SHT_REL && RelSect.sh_type != ELF::SHT_RELA)
continue;
if ((ABI != MipsABI::O32) != (RelSect.sh_type == ELF::SHT_RELA))
return make_error<JITLinkError>(
Base::G->getName() +
": invalid MIPS relocation-section format for ABI");
auto FixupSect = Base::Obj.getSection(RelSect.sh_info);
if (!FixupSect)
return FixupSect.takeError();
Block *B = Base::getGraphBlock(RelSect.sh_info);
if (!B)
continue;
SmallVector<Reloc> Rs;
if (auto Err = collectRelocs(RelSect, *B, Rs))
return Err;
for (const Reloc &R : Rs) {
uint8_t Primary = R.Types.front();
if (Primary == ELF::R_MIPS_NONE || Primary == ELF::R_MIPS_JALR)
continue;
Symbol *Target = Base::getGraphSymbol(R.Symbol);
if (!Target)
return error(**FixupSect, R.Offset,
"relocation references missing symbol index " +
Twine(R.Symbol));
auto Kind = edgeKind(R, *Target);
if (!Kind)
return joinErrors(
error(**FixupSect, R.Offset, "cannot lower relocation"),
Kind.takeError());
auto FixupAddress = orc::ExecutorAddr((*FixupSect)->sh_addr) + R.Offset;
Edge::OffsetT Offset = FixupAddress - B->getAddress();
unsigned FixupSize = getFixupSize(*Kind);
if (Offset > B->getSize() || FixupSize > B->getSize() - Offset)
return error(**FixupSect, R.Offset,
"relocation fixup extends past the end of its block");
B->addEdge(*Kind, Offset, *Target, R.Addend);
}
}
return Error::success();
}
};
static Symbol &getOrCreateTLSBase(LinkGraph &G) {
auto Name = G.intern(TLSBaseName);
if (auto *S = G.findDefinedSymbolByName(Name))
return *S;
Section *TLS = G.findSectionByName(".tdata");
if (!TLS || TLS->empty())
TLS = G.findSectionByName(".tbss");
if (!TLS || TLS->empty()) {
auto &S =
G.createSection(".tdata", orc::MemProt::Read | orc::MemProt::Write);
auto &B = G.createMutableContentBlock(S, 0, orc::ExecutorAddr(), 1, 0);
return G.addDefinedSymbol(B, 0, Name, 0, Linkage::Strong, Scope::Local,
false, true);
}
auto &B = **TLS->blocks().begin();
return G.addDefinedSymbol(B, 0, Name, 0, Linkage::Strong, Scope::Local, false,
true);
}
class MipsTableManager {
public:
explicit MipsTableManager(LinkGraph &G) : G(G) {}
bool visitEdge(LinkGraph &, Block *, Edge &E) {
switch (E.getKind()) {
case RequestGOTAndTransformToOffset16:
rewriteGOT(E, GOTEntryKind::Exact, GOTOffset16);
return true;
case RequestGOTPageAndTransformToOffset16:
rewriteGOT(E, GOTEntryKind::Page, GOTOffset16);
return true;
case RequestGOTAndTransformToOffsetHi16:
rewriteGOT(E, GOTEntryKind::Exact, GOTOffsetHi16);
return true;
case RequestGOTAndTransformToOffsetLo16:
rewriteGOT(E, GOTEntryKind::Exact, GOTOffsetLo16);
return true;
case RequestTLSGDAndTransformToOffset16:
rewriteTLS(E, TLSDescriptorKind::GeneralDynamic);
return true;
case RequestTLSLDMAndTransformToOffset16:
rewriteTLS(E, TLSDescriptorKind::LocalDynamic);
return true;
case Jump26:
if (!E.getTarget().isDefined()) {
rewriteBranchToStub(E);
return true;
}
return false;
case PC26S2:
if (mips::isR6(G) && !E.getTarget().isDefined()) {
rewriteBranchToStub(E);
return true;
}
return false;
default:
return false;
}
}
private:
enum class GOTEntryKind { Exact, Page };
enum class TLSDescriptorKind { GeneralDynamic, LocalDynamic };
using GOTKey = std::tuple<Symbol *, int64_t, GOTEntryKind>;
using StubKey = std::tuple<Symbol *, int64_t>;
LinkGraph &G;
DenseMap<GOTKey, Symbol *> GOTEntries;
DenseMap<StubKey, Symbol *> Stubs;
DenseMap<std::pair<Symbol *, int64_t>, Symbol *> TLSGDEntries;
Symbol *TLSLDMEntry = nullptr;
Section &getSection(StringRef Name, orc::MemProt Prot) {
if (auto *S = G.findSectionByName(Name))
return *S;
return G.createSection(Name, Prot);
}
Symbol &getGOT(Symbol &Target, int64_t Addend, GOTEntryKind Kind) {
GOTKey Key{&Target, Addend, Kind};
auto I = GOTEntries.find(Key);
if (I != GOTEntries.end())
return *I->second;
auto &Sec =
getSection(GOTSectionName, orc::MemProt::Read | orc::MemProt::Write);
auto &Entry = mips::createAnonymousPointer(G, Sec);
Edge::Kind PointerKind = Kind == GOTEntryKind::Page
? getPagePointerEdgeKind(G)
: mips::getPointerEdgeKind(G);
Entry.getBlock().addEdge(PointerKind, 0, Target, Addend);
GOTEntries[Key] = &Entry;
return Entry;
}
void rewriteGOT(Edge &E, GOTEntryKind EntryKind, Edge::Kind NewKind) {
Symbol &Entry = getGOT(E.getTarget(), E.getAddend(), EntryKind);
E.setKind(NewKind);
E.setTarget(Entry);
E.setAddend(0);
}
Symbol &createTLSInfo(Symbol &Target, int64_t Addend) {
auto &Sec = getSection(TLSInfoSectionName,
orc::MemProt::Read | orc::MemProt::Write);
auto &B =
G.createMutableContentBlock(Sec, 2 * G.getPointerSize(),
orc::ExecutorAddr(), G.getPointerSize(), 0);
B.addEdge(mips::getPointerEdgeKind(G), G.getPointerSize(), Target, Addend);
return G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
}
void rewriteTLS(Edge &E, TLSDescriptorKind Kind) {
Symbol *Entry = nullptr;
if (Kind == TLSDescriptorKind::LocalDynamic) {
if (!TLSLDMEntry)
TLSLDMEntry = &createTLSInfo(getOrCreateTLSBase(G), 0);
Entry = TLSLDMEntry;
} else {
auto Key = std::make_pair(&E.getTarget(), E.getAddend());
auto I = TLSGDEntries.find(Key);
if (I == TLSGDEntries.end())
I = TLSGDEntries
.try_emplace(Key, &createTLSInfo(E.getTarget(), E.getAddend()))
.first;
Entry = I->second;
}
E.setKind(GOTOffset16);
E.setTarget(*Entry);
E.setAddend(0);
}
void rewriteBranchToStub(Edge &E) {
// PC26_S2 uses PC + 4; JITLink edges use the fixup address.
int64_t PointerAddend = E.getAddend();
int64_t BranchAddend = 0;
if (E.getKind() == PC26S2) {
PointerAddend += CompactBranchPCBias;
BranchAddend = -CompactBranchPCBias;
}
StubKey Key{&E.getTarget(), PointerAddend};
auto I = Stubs.find(Key);
if (I == Stubs.end()) {
auto &Ptr = getGOT(E.getTarget(), PointerAddend, GOTEntryKind::Exact);
auto &Sec =
getSection(StubsSectionName, orc::MemProt::Read | orc::MemProt::Exec);
I = Stubs
.try_emplace(Key,
&mips::createAnonymousPointerJumpStub(G, Sec, Ptr))
.first;
}
E.setTarget(*I->second);
E.setAddend(BranchAddend);
}
};
static Error buildTables(LinkGraph &G) {
MipsTableManager Tables(G);
visitExistingEdges(G, Tables);
return Error::success();
}
static Symbol &defineMagic(LinkGraph &G, Block &Anchor, StringRef Name,
uint64_t Offset) {
auto N = G.intern(Name);
if (auto *S = G.findDefinedSymbolByName(N)) {
S->setLive(true);
return *S;
}
if (auto *S = G.findExternalSymbolByName(N)) {
G.makeDefined(*S, Anchor, Offset, 0, Linkage::Strong, Scope::Local, true);
return *S;
}
return G.addDefinedSymbol(Anchor, Offset, N, 0, Linkage::Strong, Scope::Local,
false, true);
}
static void prepareGPRegion(LinkGraph &G,
ArrayRef<std::string> GPRelSectionNames) {
Section *GOT = G.findSectionByName(GOTSectionName);
if (!GOT)
GOT = &G.createSection(GOTSectionName,
orc::MemProt::Read | orc::MemProt::Write);
// Keep the reserved GP window as content so it can share the GOT section.
auto &Anchor = G.createMutableContentBlock(
*GOT, GPAnchorOffset, orc::ExecutorAddr(), G.getPointerSize(), 0);
defineMagic(G, Anchor, GOTSymbolName, 0);
defineMagic(G, Anchor, GPName, GPAnchorOffset);
defineMagic(G, Anchor, GPDispName, GPAnchorOffset);
defineMagic(G, Anchor, LocalGPName, GPAnchorOffset);
for (const std::string &Name : GPRelSectionNames)
if (auto *S = G.findSectionByName(Name)) {
SmallVector<Block *> Blocks(S->blocks().begin(), S->blocks().end());
for (auto *B : Blocks)
G.transferBlock(*B, *GOT);
}
}
static Error orderGPRegion(LinkGraph &G) {
auto *GOT = G.findSectionByName(GOTSectionName);
if (!GOT)
return Error::success();
if (auto *TLSInfo = G.findSectionByName(TLSInfoSectionName))
G.mergeSections(*GOT, *TLSInfo);
auto *GOTBase = G.findDefinedSymbolByName(G.intern(GOTSymbolName));
assert(GOTBase && &GOTBase->getBlock().getSection() == GOT &&
GOTBase->getOffset() == 0 && "invalid MIPS GOT base");
// BasicLayout orders equal-address blocks by size. Give synthesized entries
// the anchor's end address to keep the reserved GP window first.
Block &Anchor = GOTBase->getBlock();
Anchor.setAddress(orc::ExecutorAddr());
for (auto *B : GOT->blocks())
if (B != &Anchor)
B->setAddress(orc::ExecutorAddr(Anchor.getSize()));
return Error::success();
}
template <typename ELFT>
Error validateABIFlags(const object::ELFFile<ELFT> &Obj, MipsABI ABI,
StringRef FileName) {
auto Sections = Obj.sections();
if (!Sections)
return Sections.takeError();
const typename ELFT::Shdr *ABIFlagsSec = nullptr;
for (const auto &Sec : *Sections)
if (Sec.sh_type == ELF::SHT_MIPS_ABIFLAGS) {
if (ABIFlagsSec)
return make_error<JITLinkError>(FileName.str() +
": multiple .MIPS.abiflags sections");
ABIFlagsSec = &Sec;
}
if (!ABIFlagsSec)
return Error::success();
auto Contents = Obj.template getSectionContentsAsArray<char>(*ABIFlagsSec);
if (!Contents)
return Contents.takeError();
if (Contents->size() != sizeof(object::Elf_Mips_ABIFlags<ELFT>))
return make_error<JITLinkError>(FileName.str() +
": invalid .MIPS.abiflags size");
const auto &AF = *reinterpret_cast<const object::Elf_Mips_ABIFlags<ELFT> *>(
Contents->data());
if (AF.version != 0)
return make_error<JITLinkError>(FileName.str() +
": unsupported .MIPS.abiflags version");
if ((ABI == MipsABI::N32 || ABI == MipsABI::N64) &&
AF.gpr_size != Mips::AFL_REG_64)
return make_error<JITLinkError>(
FileName.str() + ": N32/N64 requires 64-bit general registers");
if (static_cast<uint32_t>(AF.ases) &
(Mips::AFL_ASE_MIPS16 | Mips::AFL_ASE_MICROMIPS))
return make_error<JITLinkError>(
FileName.str() +
": MIPS16 and microMIPS ABI flags are unsupported by JITLink");
return Error::success();
}
template <typename ELFT>
Expected<std::unique_ptr<LinkGraph>>
buildGraph(object::ELFObjectFile<ELFT> &ObjFile,
std::shared_ptr<orc::SymbolStringPool> SSP, MipsABI ABI, Triple TT,
SubtargetFeatures Features) {
auto &Obj = ObjFile.getELFFile();
if (auto Err = validateABIFlags(Obj, ABI, ObjFile.getFileName()))
return std::move(Err);
ELFLinkGraphBuilder_mips<ELFT> Builder(ObjFile.getFileName(), Obj,
std::move(SSP), std::move(TT),
std::move(Features), ABI);
auto G = Builder.buildGraph();
if (!G)
return G.takeError();
SmallVector<std::string> GPRelSections;
auto Sections = Obj.sections();
if (!Sections)
return Sections.takeError();
for (const auto &Sec : *Sections)
if (Sec.sh_flags & ELF::SHF_MIPS_GPREL) {
auto Name = Obj.getSectionName(Sec);
if (!Name)
return Name.takeError();
GPRelSections.push_back(Name->str());
}
prepareGPRegion(**G, GPRelSections);
return G;
}
static Expected<MipsABI> validateHeader(const object::ELFObjectFileBase &Obj,
uint8_t ELFClass, StringRef FileName) {
uint32_t Flags = Obj.getPlatformFlags();
if (Flags & (ELF::EF_MIPS_ARCH_ASE_M16 | ELF::EF_MIPS_MICROMIPS))
return make_error<JITLinkError>(
FileName.str() +
": MIPS16 and microMIPS objects are unsupported by JITLink");
uint32_t ABIFlag = Flags & ELF::EF_MIPS_ABI;
if (ELFClass == ELF::ELFCLASS64) {
if ((Flags & ELF::EF_MIPS_ABI2) || ABIFlag != 0)
return make_error<JITLinkError>(
FileName.str() + ": inconsistent MIPS N64 ELF class/ABI flags");
return MipsABI::N64;
}
if (Flags & ELF::EF_MIPS_ABI2) {
if (ABIFlag != 0)
return make_error<JITLinkError>(
FileName.str() + ": inconsistent MIPS N32 ELF class/ABI flags");
return MipsABI::N32;
}
if (ABIFlag != 0 && ABIFlag != ELF::EF_MIPS_ABI_O32)
return make_error<JITLinkError>(FileName.str() +
": unsupported 32-bit MIPS ABI flags");
return MipsABI::O32;
}
} // namespace
namespace llvm {
namespace jitlink {
Expected<std::unique_ptr<LinkGraph>>
createLinkGraphFromELFObject_mips(MemoryBufferRef ObjectBuffer,
std::shared_ptr<orc::SymbolStringPool> SSP) {
StringRef FileName = ObjectBuffer.getBufferIdentifier();
auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
if (!ELFObj)
return ELFObj.takeError();
auto &Base = cast<object::ELFObjectFileBase>(**ELFObj);
assert(Base.getEMachine() == ELF::EM_MIPS && "expected an ELF/MIPS object");
StringRef Buf = ObjectBuffer.getBuffer();
uint8_t Class = Buf[ELF::EI_CLASS];
uint8_t Data = Buf[ELF::EI_DATA];
auto ABI = validateHeader(Base, Class, FileName);
if (!ABI)
return ABI.takeError();
auto Features = Base.getFeatures();
if (!Features)
return Features.takeError();
Triple TT = Base.makeTriple();
if (*ABI == MipsABI::N32) {
TT.setArch(Data == ELF::ELFDATA2LSB ? Triple::mips64el : Triple::mips64);
TT.setEnvironment(Triple::GNUABIN32);
}
if (Class == ELF::ELFCLASS32 && Data == ELF::ELFDATA2LSB)
return buildGraph(cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj),
std::move(SSP), *ABI, TT, std::move(*Features));
if (Class == ELF::ELFCLASS32 && Data == ELF::ELFDATA2MSB)
return buildGraph(cast<object::ELFObjectFile<object::ELF32BE>>(**ELFObj),
std::move(SSP), *ABI, TT, std::move(*Features));
if (Class == ELF::ELFCLASS64 && Data == ELF::ELFDATA2LSB)
return buildGraph(cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj),
std::move(SSP), *ABI, TT, std::move(*Features));
if (Class == ELF::ELFCLASS64 && Data == ELF::ELFDATA2MSB)
return buildGraph(cast<object::ELFObjectFile<object::ELF64BE>>(**ELFObj),
std::move(SSP), *ABI, TT, std::move(*Features));
llvm_unreachable("invalid ELF/MIPS class or byte order");
}
void link_ELF_mips(std::unique_ptr<LinkGraph> G,
std::unique_ptr<JITLinkContext> Ctx) {
PassConfiguration Config;
const Triple &TT = G->getTargetTriple();
if (Ctx->shouldAddDefaultTargetPasses(TT)) {
Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
Config.PrePrunePasses.push_back(
EHFrameEdgeFixer(".eh_frame", G->getPointerSize(), Pointer32, Pointer64,
Delta32, Delta64, NegDelta32));
Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
if (auto MarkLive = Ctx->getMarkLivePass(TT))
Config.PrePrunePasses.push_back(std::move(MarkLive));
else
Config.PrePrunePasses.push_back(markAllSymbolsLive);
Config.PostPrunePasses.push_back(buildTables);
}
if (auto Err = Ctx->modifyPassConfig(*G, Config))
return Ctx->notifyFailed(std::move(Err));
if (Ctx->shouldAddDefaultTargetPasses(TT))
Config.PostPrunePasses.push_back(orderGPRegion);
ELFJITLinker_mips::link(std::move(Ctx), std::move(G), std::move(Config));
}
} // namespace jitlink
} // namespace llvm