[JITLink][RISCV] Support GOT/PLT relocations
This patch add the R_RISCV_GOT_HI20 and R_RISCV_CALL_PLT relocation support. And the basic got/plt was implemented. Because of riscv32 and riscv64 has different pointer size, the got entry size and instructions of plt entry is different. This patch is the basic support, the optimization pass at preFixup stage has not been implemented. Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D107688
This commit is contained in:
@@ -70,7 +70,19 @@ enum EdgeKind_riscv : Edge::Kind {
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- (Target - Fixup + Addend)
|
||||
R_RISCV_CALL
|
||||
R_RISCV_CALL,
|
||||
|
||||
/// PC relative GOT offset
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- (GOT - Fixup + Addend) >> 12
|
||||
R_RISCV_GOT_HI20,
|
||||
|
||||
/// PC relative call by PLT
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- (Target - Fixup + Addend)
|
||||
R_RISCV_CALL_PLT
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -11,17 +11,119 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
|
||||
#include "ELFLinkGraphBuilder.h"
|
||||
#include "JITLinkGeneric.h"
|
||||
#include "PerGraphGOTAndPLTStubsBuilder.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/riscv.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
|
||||
#include "ELFLinkGraphBuilder.h"
|
||||
#include "JITLinkGeneric.h"
|
||||
|
||||
#define DEBUG_TYPE "jitlink"
|
||||
using namespace llvm;
|
||||
using namespace llvm::jitlink;
|
||||
using namespace llvm::jitlink::riscv;
|
||||
|
||||
namespace {
|
||||
|
||||
class PerGraphGOTAndPLTStubsBuilder_ELF_riscv
|
||||
: public PerGraphGOTAndPLTStubsBuilder<
|
||||
PerGraphGOTAndPLTStubsBuilder_ELF_riscv> {
|
||||
public:
|
||||
static constexpr size_t StubEntrySize = 16;
|
||||
static const uint8_t NullGOTEntryContent[8];
|
||||
static const uint8_t RV64StubContent[StubEntrySize];
|
||||
static const uint8_t RV32StubContent[StubEntrySize];
|
||||
|
||||
using PerGraphGOTAndPLTStubsBuilder<
|
||||
PerGraphGOTAndPLTStubsBuilder_ELF_riscv>::PerGraphGOTAndPLTStubsBuilder;
|
||||
|
||||
bool isRV64() const { return G.getPointerSize() == 8; }
|
||||
|
||||
bool isGOTEdgeToFix(Edge &E) const { return E.getKind() == R_RISCV_GOT_HI20; }
|
||||
|
||||
Symbol &createGOTEntry(Symbol &Target) {
|
||||
Block &GOTBlock = G.createContentBlock(
|
||||
getGOTSection(), getGOTEntryBlockContent(), 0, G.getPointerSize(), 0);
|
||||
GOTBlock.addEdge(isRV64() ? R_RISCV_64 : R_RISCV_32, 0, Target, 0);
|
||||
return G.addAnonymousSymbol(GOTBlock, 0, G.getPointerSize(), false, false);
|
||||
}
|
||||
|
||||
Symbol &createPLTStub(Symbol &Target) {
|
||||
Block &StubContentBlock =
|
||||
G.createContentBlock(getStubsSection(), getStubBlockContent(), 0, 4, 0);
|
||||
auto &GOTEntrySymbol = getGOTEntry(Target);
|
||||
StubContentBlock.addEdge(R_RISCV_CALL, 0, GOTEntrySymbol, 0);
|
||||
return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true,
|
||||
false);
|
||||
}
|
||||
|
||||
void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
|
||||
// Replace the relocation pair (R_RISCV_GOT_HI20, R_RISCV_PCREL_LO12)
|
||||
// with (R_RISCV_PCREL_HI20, R_RISCV_PCREL_LO12)
|
||||
// Therefore, here just change the R_RISCV_GOT_HI20 to R_RISCV_PCREL_HI20
|
||||
E.setKind(R_RISCV_PCREL_HI20);
|
||||
E.setTarget(GOTEntry);
|
||||
}
|
||||
|
||||
void fixPLTEdge(Edge &E, Symbol &PLTStubs) {
|
||||
assert(E.getKind() == R_RISCV_CALL_PLT && "Not a R_RISCV_CALL_PLT edge?");
|
||||
E.setKind(R_RISCV_CALL);
|
||||
E.setTarget(PLTStubs);
|
||||
}
|
||||
|
||||
bool isExternalBranchEdge(Edge &E) const {
|
||||
return E.getKind() == R_RISCV_CALL_PLT;
|
||||
}
|
||||
|
||||
private:
|
||||
Section &getGOTSection() const {
|
||||
if (!GOTSection)
|
||||
GOTSection = &G.createSection("$__GOT", sys::Memory::MF_READ);
|
||||
return *GOTSection;
|
||||
}
|
||||
|
||||
Section &getStubsSection() const {
|
||||
if (!StubsSection) {
|
||||
auto StubsProt = static_cast<sys::Memory::ProtectionFlags>(
|
||||
sys::Memory::MF_READ | sys::Memory::MF_EXEC);
|
||||
StubsSection = &G.createSection("$__STUBS", StubsProt);
|
||||
}
|
||||
return *StubsSection;
|
||||
}
|
||||
|
||||
ArrayRef<char> getGOTEntryBlockContent() {
|
||||
return {reinterpret_cast<const char *>(NullGOTEntryContent),
|
||||
G.getPointerSize()};
|
||||
}
|
||||
|
||||
ArrayRef<char> getStubBlockContent() {
|
||||
auto StubContent = isRV64() ? RV64StubContent : RV32StubContent;
|
||||
return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
|
||||
}
|
||||
|
||||
mutable Section *GOTSection = nullptr;
|
||||
mutable Section *StubsSection = nullptr;
|
||||
};
|
||||
|
||||
const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_riscv::NullGOTEntryContent[8] =
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const uint8_t
|
||||
PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV64StubContent[StubEntrySize] = {
|
||||
0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
|
||||
0x03, 0x3e, 0x0e, 0x00, // ld t3, literal(t3)
|
||||
0x67, 0x00, 0x0e, 0x00, // jr t3
|
||||
0x13, 0x00, 0x00, 0x00}; // nop
|
||||
|
||||
const uint8_t
|
||||
PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV32StubContent[StubEntrySize] = {
|
||||
0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
|
||||
0x03, 0x2e, 0x0e, 0x00, // lw t3, literal(t3)
|
||||
0x67, 0x00, 0x0e, 0x00, // jr t3
|
||||
0x13, 0x00, 0x00, 0x00}; // nop
|
||||
} // namespace
|
||||
namespace llvm {
|
||||
namespace jitlink {
|
||||
|
||||
@@ -78,6 +180,16 @@ private:
|
||||
char *FixupPtr = BlockWorkingMem + E.getOffset();
|
||||
JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
|
||||
switch (E.getKind()) {
|
||||
case R_RISCV_32: {
|
||||
int64_t Value = E.getTarget().getAddress() + E.getAddend();
|
||||
*(little32_t *)FixupPtr = static_cast<uint32_t>(Value);
|
||||
break;
|
||||
}
|
||||
case R_RISCV_64: {
|
||||
int64_t Value = E.getTarget().getAddress() + E.getAddend();
|
||||
*(little64_t *)FixupPtr = static_cast<uint64_t>(Value);
|
||||
break;
|
||||
}
|
||||
case R_RISCV_HI20: {
|
||||
int64_t Value = E.getTarget().getAddress() + E.getAddend();
|
||||
int32_t Hi = (Value + 0x800) & 0xFFFFF000;
|
||||
@@ -163,6 +275,10 @@ private:
|
||||
return EdgeKind_riscv::R_RISCV_PCREL_LO12_I;
|
||||
case ELF::R_RISCV_PCREL_LO12_S:
|
||||
return EdgeKind_riscv::R_RISCV_PCREL_LO12_S;
|
||||
case ELF::R_RISCV_GOT_HI20:
|
||||
return EdgeKind_riscv::R_RISCV_GOT_HI20;
|
||||
case ELF::R_RISCV_CALL_PLT:
|
||||
return EdgeKind_riscv::R_RISCV_CALL_PLT;
|
||||
}
|
||||
|
||||
return make_error<JITLinkError>("Unsupported riscv relocation:" +
|
||||
@@ -304,6 +420,8 @@ void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
|
||||
Config.PrePrunePasses.push_back(std::move(MarkLive));
|
||||
else
|
||||
Config.PrePrunePasses.push_back(markAllSymbolsLive);
|
||||
Config.PostPrunePasses.push_back(
|
||||
PerGraphGOTAndPLTStubsBuilder_ELF_riscv::asPass);
|
||||
}
|
||||
if (auto Err = Ctx->modifyPassConfig(*G, Config))
|
||||
return Ctx->notifyFailed(std::move(Err));
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# RUN: rm -rf %t && mkdir -p %t
|
||||
# RUN: llvm-mc -triple=riscv32 -position-independent -filetype=obj -o %t/elf_riscv32_got_plt_reloc.o %s
|
||||
# RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \
|
||||
# RUN: -define-abs external_func=0x1 -define-abs external_data=0x2 \
|
||||
# RUN: -check %s %t/elf_riscv32_got_plt_reloc.o
|
||||
|
||||
|
||||
.text
|
||||
.file "testcase.c"
|
||||
|
||||
# Empty main entry point.
|
||||
.globl main
|
||||
.p2align 1
|
||||
.type main,@function
|
||||
main:
|
||||
ret
|
||||
|
||||
.size main, .-main
|
||||
# Test R_RISCV_GOT_HI20. The low 12 relocation is R_RISCV_PC_REL_LO12. This test case will
|
||||
# check both the offset to the GOT entry and its content.
|
||||
# jitlink-check: decode_operand(test_got, 1) = (got_addr(elf_riscv32_got_plt_reloc.o, external_data) - test_got + 0x800)[31:12]
|
||||
# jitlink-check: decode_operand(test_got+4, 2)[11:0] = (got_addr(elf_riscv32_got_plt_reloc.o, external_data) - test_got)[11:0]
|
||||
# jitlink-check: *{4}(got_addr(elf_riscv32_got_plt_reloc.o, external_data)) = external_data
|
||||
.globl test_got
|
||||
.p2align 1
|
||||
.type test_got,@function
|
||||
test_got:
|
||||
auipc a0, %got_pcrel_hi(external_data)
|
||||
lw a0, %pcrel_lo(test_got)(a0)
|
||||
|
||||
.size test_got, .-test_got
|
||||
|
||||
# Test R_RISCV_CALL_PLT.
|
||||
# jitlink-check: decode_operand(test_plt, 1) = (stub_addr(elf_riscv32_got_plt_reloc.o, external_func) - test_plt + 0x800)[31:12]
|
||||
# jitlink-check: decode_operand(test_plt+4, 2) = (stub_addr(elf_riscv32_got_plt_reloc.o, external_func) - test_plt)[11:0]
|
||||
# jitlink-check: *{4}(got_addr(elf_riscv32_got_plt_reloc.o, external_func)) = external_func
|
||||
.globl test_plt
|
||||
.p2align 1
|
||||
.type test_got,@function
|
||||
test_plt:
|
||||
call external_func@plt
|
||||
|
||||
.size test_plt, .-test_plt
|
||||
@@ -0,0 +1,44 @@
|
||||
# RUN: rm -rf %t && mkdir -p %t
|
||||
# RUN: llvm-mc -triple=riscv64 -position-independent -filetype=obj -o %t/elf_riscv64_got_plt_reloc.o %s
|
||||
# RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \
|
||||
# RUN: -define-abs external_func=0x1 -define-abs external_data=0x2 \
|
||||
# RUN: -check %s %t/elf_riscv64_got_plt_reloc.o
|
||||
|
||||
|
||||
.text
|
||||
.file "testcase.c"
|
||||
|
||||
# Empty main entry point.
|
||||
.globl main
|
||||
.p2align 1
|
||||
.type main,@function
|
||||
main:
|
||||
ret
|
||||
|
||||
.size main, .-main
|
||||
# Test R_RISCV_GOT_HI20. The low 12 relocation is R_RISCV_PC_REL_LO12. This test case will
|
||||
# check both the offset to the GOT entry and its content.
|
||||
# jitlink-check: decode_operand(test_got, 1) = (got_addr(elf_riscv64_got_plt_reloc.o, external_data) - test_got + 0x800)[31:12]
|
||||
# jitlink-check: decode_operand(test_got+4, 2)[11:0] = (got_addr(elf_riscv64_got_plt_reloc.o, external_data) - test_got)[11:0]
|
||||
# jitlink-check: *{8}(got_addr(elf_riscv64_got_plt_reloc.o, external_data)) = external_data
|
||||
.globl test_got
|
||||
.p2align 1
|
||||
.type test_got,@function
|
||||
test_got:
|
||||
auipc a0, %got_pcrel_hi(external_data)
|
||||
ld a0, %pcrel_lo(test_got)(a0)
|
||||
|
||||
.size test_got, .-test_got
|
||||
|
||||
# Test R_RISCV_CALL_PLT.
|
||||
# jitlink-check: decode_operand(test_plt, 1) = (stub_addr(elf_riscv64_got_plt_reloc.o, external_func) - test_plt + 0x800)[31:12]
|
||||
# jitlink-check: decode_operand(test_plt+4, 2) = (stub_addr(elf_riscv64_got_plt_reloc.o, external_func) - test_plt)[11:0]
|
||||
# jitlink-check: *{8}(got_addr(elf_riscv64_got_plt_reloc.o, external_func)) = external_func
|
||||
.globl test_plt
|
||||
.p2align 1
|
||||
.type test_got,@function
|
||||
test_plt:
|
||||
call external_func@plt
|
||||
|
||||
.size test_plt, .-test_plt
|
||||
|
||||
Reference in New Issue
Block a user