[JITLink][AArch64] Unify table managers of ELF and MachO.

Unifies GOT/PLT table managers of ELF and MachO on aarch64 architecture. Additionally, it migrates table managers from PerGraphGOTAndPLTStubsBuilder to generic crtp TableManager.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D127558
This commit is contained in:
Sunho Kim 2022-06-14 10:58:05 +09:00
parent 3c68d58841
commit db37225803
4 changed files with 166 additions and 201 deletions

View file

@ -13,7 +13,9 @@
#ifndef LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H
#define LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H
#include "TableManager.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/ExecutionEngine/JITLink/MemoryFlags.h"
namespace llvm {
namespace jitlink {
@ -49,6 +51,125 @@ unsigned getPageOffset12Shift(uint32_t Instr);
Error applyFixup(LinkGraph &G, Block &B, const Edge &E);
/// AArch64 null pointer content.
extern const uint8_t NullGOTEntryContent[8];
/// AArch64 PLT stub content.
extern const uint8_t StubContent[8];
/// Global Offset Table Builder.
class GOTTableManager : public TableManager<GOTTableManager> {
public:
static StringRef getSectionName() { return "$__GOT"; }
bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
Edge::Kind KindToSet = Edge::Invalid;
const char *BlockWorkingMem = B->getContent().data();
const char *FixupPtr = BlockWorkingMem + E.getOffset();
switch (E.getKind()) {
case aarch64::GOTPage21:
case aarch64::TLVPage21: {
KindToSet = aarch64::Page21;
break;
}
case aarch64::GOTPageOffset12:
case aarch64::TLVPageOffset12: {
KindToSet = aarch64::PageOffset12;
uint32_t RawInstr = *(const support::ulittle32_t *)FixupPtr;
assert(E.getAddend() == 0 &&
"GOTPageOffset12/TLVPageOffset12 with non-zero addend");
assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
"RawInstr isn't a 64-bit LDR immediate");
break;
}
case aarch64::PointerToGOT: {
KindToSet = aarch64::Delta64;
break;
}
default:
return false;
}
assert(KindToSet != Edge::Invalid &&
"Fell through switch, but no new kind to set");
DEBUG_WITH_TYPE("jitlink", {
dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
<< B->getFixupAddress(E) << " (" << B->getAddress() << " + "
<< formatv("{0:x}", E.getOffset()) << ")\n";
});
E.setKind(KindToSet);
E.setTarget(getEntryForTarget(G, E.getTarget()));
return true;
}
Symbol &createEntry(LinkGraph &G, Symbol &Target) {
auto &GOTEntryBlock = G.createContentBlock(
getGOTSection(G), getGOTEntryBlockContent(), orc::ExecutorAddr(), 8, 0);
GOTEntryBlock.addEdge(aarch64::Pointer64, 0, Target, 0);
return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false);
}
private:
Section &getGOTSection(LinkGraph &G) {
if (!GOTSection)
GOTSection =
&G.createSection(getSectionName(), MemProt::Read | MemProt::Exec);
return *GOTSection;
}
ArrayRef<char> getGOTEntryBlockContent() {
return {reinterpret_cast<const char *>(NullGOTEntryContent),
sizeof(NullGOTEntryContent)};
}
Section *GOTSection = nullptr;
};
/// Procedure Linkage Table Builder.
class PLTTableManager : public TableManager<PLTTableManager> {
public:
PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {}
static StringRef getSectionName() { return "$__STUBS"; }
bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
if (E.getKind() == aarch64::Branch26 && !E.getTarget().isDefined()) {
DEBUG_WITH_TYPE("jitlink", {
dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
<< B->getFixupAddress(E) << " (" << B->getAddress() << " + "
<< formatv("{0:x}", E.getOffset()) << ")\n";
});
E.setTarget(getEntryForTarget(G, E.getTarget()));
return true;
}
return false;
}
Symbol &createEntry(LinkGraph &G, Symbol &Target) {
auto &StubContentBlock = G.createContentBlock(
getStubsSection(G), getStubBlockContent(), orc::ExecutorAddr(), 1, 0);
// Re-use GOT entries for stub targets.
auto &GOTEntrySymbol = GOT.getEntryForTarget(G, Target);
StubContentBlock.addEdge(aarch64::LDRLiteral19, 0, GOTEntrySymbol, 0);
return G.addAnonymousSymbol(StubContentBlock, 0, 8, true, false);
}
public:
Section &getStubsSection(LinkGraph &G) {
if (!StubsSection)
StubsSection =
&G.createSection(getSectionName(), MemProt::Read | MemProt::Exec);
return *StubsSection;
}
ArrayRef<char> getStubBlockContent() {
return {reinterpret_cast<const char *>(StubContent), sizeof(StubContent)};
}
GOTTableManager &GOT;
Section *StubsSection = nullptr;
};
} // namespace aarch64
} // namespace jitlink
} // namespace llvm

View file

@ -21,15 +21,12 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/MathExtras.h"
#include "PerGraphGOTAndPLTStubsBuilder.h"
#define DEBUG_TYPE "jitlink"
using namespace llvm;
using namespace llvm::jitlink;
namespace llvm {
namespace jitlink {
namespace {
class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
friend class JITLinker<ELFJITLinker_aarch64>;
@ -293,90 +290,19 @@ public:
aarch64::getEdgeKindName) {}
};
class PerGraphGOTAndPLTStubsBuilder_ELF_arm64
: public PerGraphGOTAndPLTStubsBuilder<
PerGraphGOTAndPLTStubsBuilder_ELF_arm64> {
public:
using PerGraphGOTAndPLTStubsBuilder<
PerGraphGOTAndPLTStubsBuilder_ELF_arm64>::PerGraphGOTAndPLTStubsBuilder;
Error buildTables_ELF_aarch64(LinkGraph &G) {
LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
bool isGOTEdgeToFix(Edge &E) const {
return E.getKind() == aarch64::GOTPage21 ||
E.getKind() == aarch64::GOTPageOffset12;
}
aarch64::GOTTableManager GOT;
aarch64::PLTTableManager PLT(GOT);
visitExistingEdges(G, GOT, PLT);
return Error::success();
}
Symbol &createGOTEntry(Symbol &Target) {
auto &GOTEntryBlock = G.createContentBlock(
getGOTSection(), getGOTEntryBlockContent(), orc::ExecutorAddr(), 8, 0);
GOTEntryBlock.addEdge(aarch64::Pointer64, 0, Target, 0);
return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false);
}
} // namespace
void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
if (E.getKind() == aarch64::GOTPage21) {
E.setKind(aarch64::Page21);
E.setTarget(GOTEntry);
} else if (E.getKind() == aarch64::GOTPageOffset12) {
E.setKind(aarch64::PageOffset12);
E.setTarget(GOTEntry);
} else
llvm_unreachable("Not a GOT edge?");
}
bool isExternalBranchEdge(Edge &E) {
return E.getKind() == aarch64::Branch26 && !E.getTarget().isDefined();
}
Symbol &createPLTStub(Symbol &Target) {
auto &StubContentBlock = G.createContentBlock(
getStubsSection(), getStubBlockContent(), orc::ExecutorAddr(), 1, 0);
// Re-use GOT entries for stub targets.
auto &GOTEntrySymbol = getGOTEntry(Target);
StubContentBlock.addEdge(aarch64::LDRLiteral19, 0, GOTEntrySymbol, 0);
return G.addAnonymousSymbol(StubContentBlock, 0, 8, true, false);
}
void fixPLTEdge(Edge &E, Symbol &Stub) {
assert(E.getKind() == aarch64::Branch26 && "Not a Branch26 edge?");
assert(E.getAddend() == 0 && "Branch26 edge has non-zero addend?");
E.setTarget(Stub);
}
private:
Section &getGOTSection() {
if (!GOTSection)
GOTSection = &G.createSection("$__GOT", MemProt::Read | MemProt::Exec);
return *GOTSection;
}
Section &getStubsSection() {
if (!StubsSection)
StubsSection =
&G.createSection("$__STUBS", MemProt::Read | MemProt::Exec);
return *StubsSection;
}
ArrayRef<char> getGOTEntryBlockContent() {
return {reinterpret_cast<const char *>(NullGOTEntryContent),
sizeof(NullGOTEntryContent)};
}
ArrayRef<char> getStubBlockContent() {
return {reinterpret_cast<const char *>(StubContent), sizeof(StubContent)};
}
static const uint8_t NullGOTEntryContent[8];
static const uint8_t StubContent[8];
Section *GOTSection = nullptr;
Section *StubsSection = nullptr;
};
const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_arm64::NullGOTEntryContent[8] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_arm64::StubContent[8] = {
0x10, 0x00, 0x00, 0x58, // LDR x16, <literal>
0x00, 0x02, 0x1f, 0xd6 // BR x16
};
namespace llvm {
namespace jitlink {
Expected<std::unique_ptr<LinkGraph>>
createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer) {
@ -404,18 +330,21 @@ void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
PassConfiguration Config;
const Triple &TT = G->getTargetTriple();
if (Ctx->shouldAddDefaultTargetPasses(TT)) {
// Add eh-frame passses.
Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
".eh_frame", 8, aarch64::Pointer32, aarch64::Pointer64,
aarch64::Delta32, aarch64::Delta64, aarch64::NegDelta32));
// Add a mark-live pass.
if (auto MarkLive = Ctx->getMarkLivePass(TT))
Config.PrePrunePasses.push_back(std::move(MarkLive));
else
Config.PrePrunePasses.push_back(markAllSymbolsLive);
}
Config.PostPrunePasses.push_back(
PerGraphGOTAndPLTStubsBuilder_ELF_arm64::asPass);
// Add an in-place GOT/Stubs build pass.
Config.PostPrunePasses.push_back(buildTables_ELF_aarch64);
}
if (auto Err = Ctx->modifyPassConfig(*G, Config))
return Ctx->notifyFailed(std::move(Err));

View file

@ -15,7 +15,6 @@
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
#include "MachOLinkGraphBuilder.h"
#include "PerGraphGOTAndPLTStubsBuilder.h"
#define DEBUG_TYPE "jitlink"
@ -506,103 +505,20 @@ private:
unsigned NumSymbols = 0;
};
class PerGraphGOTAndPLTStubsBuilder_MachO_arm64
: public PerGraphGOTAndPLTStubsBuilder<
PerGraphGOTAndPLTStubsBuilder_MachO_arm64> {
public:
using PerGraphGOTAndPLTStubsBuilder<
PerGraphGOTAndPLTStubsBuilder_MachO_arm64>::PerGraphGOTAndPLTStubsBuilder;
bool isGOTEdgeToFix(Edge &E) const {
return E.getKind() == aarch64::GOTPage21 ||
E.getKind() == aarch64::GOTPageOffset12 ||
E.getKind() == aarch64::TLVPage21 ||
E.getKind() == aarch64::TLVPageOffset12 ||
E.getKind() == aarch64::PointerToGOT;
}
Symbol &createGOTEntry(Symbol &Target) {
auto &GOTEntryBlock = G.createContentBlock(
getGOTSection(), getGOTEntryBlockContent(), orc::ExecutorAddr(), 8, 0);
GOTEntryBlock.addEdge(aarch64::Pointer64, 0, Target, 0);
return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false);
}
void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
if (E.getKind() == aarch64::GOTPage21 ||
E.getKind() == aarch64::GOTPageOffset12 ||
E.getKind() == aarch64::TLVPage21 ||
E.getKind() == aarch64::TLVPageOffset12) {
// Update the target, but leave the edge addend as-is.
E.setTarget(GOTEntry);
} else if (E.getKind() == aarch64::PointerToGOT) {
E.setTarget(GOTEntry);
E.setKind(aarch64::Delta32);
} else
llvm_unreachable("Not a GOT edge?");
}
bool isExternalBranchEdge(Edge &E) {
return E.getKind() == aarch64::Branch26 && !E.getTarget().isDefined();
}
Symbol &createPLTStub(Symbol &Target) {
auto &StubContentBlock = G.createContentBlock(
getStubsSection(), getStubBlockContent(), orc::ExecutorAddr(), 1, 0);
// Re-use GOT entries for stub targets.
auto &GOTEntrySymbol = getGOTEntry(Target);
StubContentBlock.addEdge(aarch64::LDRLiteral19, 0, GOTEntrySymbol, 0);
return G.addAnonymousSymbol(StubContentBlock, 0, 8, true, false);
}
void fixPLTEdge(Edge &E, Symbol &Stub) {
assert(E.getKind() == aarch64::Branch26 && "Not a Branch32 edge?");
assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?");
E.setTarget(Stub);
}
private:
Section &getGOTSection() {
if (!GOTSection)
GOTSection = &G.createSection("$__GOT", MemProt::Read | MemProt::Exec);
return *GOTSection;
}
Section &getStubsSection() {
if (!StubsSection)
StubsSection =
&G.createSection("$__STUBS", MemProt::Read | MemProt::Exec);
return *StubsSection;
}
ArrayRef<char> getGOTEntryBlockContent() {
return {reinterpret_cast<const char *>(NullGOTEntryContent),
sizeof(NullGOTEntryContent)};
}
ArrayRef<char> getStubBlockContent() {
return {reinterpret_cast<const char *>(StubContent), sizeof(StubContent)};
}
static const uint8_t NullGOTEntryContent[8];
static const uint8_t StubContent[8];
Section *GOTSection = nullptr;
Section *StubsSection = nullptr;
};
const uint8_t
PerGraphGOTAndPLTStubsBuilder_MachO_arm64::NullGOTEntryContent[8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const uint8_t PerGraphGOTAndPLTStubsBuilder_MachO_arm64::StubContent[8] = {
0x10, 0x00, 0x00, 0x58, // LDR x16, <literal>
0x00, 0x02, 0x1f, 0xd6 // BR x16
};
} // namespace
namespace llvm {
namespace jitlink {
Error buildTables_MachO_arm64(LinkGraph &G) {
LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
aarch64::GOTTableManager GOT;
aarch64::PLTTableManager PLT(GOT);
visitExistingEdges(G, GOT, PLT);
return Error::success();
}
class MachOJITLinker_arm64 : public JITLinker<MachOJITLinker_arm64> {
friend class JITLinker<MachOJITLinker_arm64>;
@ -654,8 +570,7 @@ void link_MachO_arm64(std::unique_ptr<LinkGraph> G,
aarch64::Delta32, aarch64::Delta64, aarch64::NegDelta32));
// Add an in-place GOT/Stubs pass.
Config.PostPrunePasses.push_back(
PerGraphGOTAndPLTStubsBuilder_MachO_arm64::asPass);
Config.PostPrunePasses.push_back(buildTables_MachO_arm64);
}
if (auto Err = Ctx->modifyPassConfig(*G, Config))

View file

@ -80,9 +80,7 @@ Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
*(ulittle64_t *)FixupPtr = Value;
break;
}
case Page21:
case TLVPage21:
case GOTPage21: {
case Page21: {
assert((E.getKind() != GOTPage21 || E.getAddend() == 0) &&
"GOTPAGE21 with non-zero addend");
uint64_t TargetPage =
@ -119,21 +117,6 @@ Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
*(ulittle32_t *)FixupPtr = FixedInstr;
break;
}
case TLVPageOffset12:
case GOTPageOffset12: {
assert(E.getAddend() == 0 && "GOTPAGEOF12 with non-zero addend");
uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
assert((RawInstr & 0xfffffc00) == 0xf9400000 &&
"RawInstr isn't a 64-bit LDR immediate");
uint32_t TargetOffset = E.getTarget().getAddress().getValue() & 0xfff;
assert((TargetOffset & 0x7) == 0 && "GOT entry is not 8-byte aligned");
uint32_t EncodedImm = (TargetOffset >> 3) << 10;
uint32_t FixedInstr = RawInstr | EncodedImm;
*(ulittle32_t *)FixupPtr = FixedInstr;
break;
}
case LDRLiteral19: {
assert((FixupAddress.getValue() & 0x3) == 0 && "LDR is not 32-bit aligned");
assert(E.getAddend() == 0 && "LDRLiteral19 with non-zero addend");
@ -170,6 +153,15 @@ Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
*(little64_t *)FixupPtr = Value;
break;
}
case TLVPage21:
case GOTPage21:
case TLVPageOffset12:
case GOTPageOffset12:
case PointerToGOT: {
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
"GOT/TLV edge kinds not lowered: " + getEdgeKindName(E.getKind()));
}
default:
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@ -179,6 +171,14 @@ Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
return Error::success();
}
const uint8_t NullGOTEntryContent[8] = {0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
const uint8_t StubContent[8] = {
0x10, 0x00, 0x00, 0x58, // LDR x16, <literal>
0x00, 0x02, 0x1f, 0xd6 // BR x16
};
const char *getEdgeKindName(Edge::Kind R) {
switch (R) {
case Branch26: