diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h @@ -70,7 +70,19 @@ /// /// 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 }; diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp --- a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp @@ -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::MF_READ | sys::Memory::MF_EXEC); + StubsSection = &G.createSection("$__STUBS", StubsProt); + } + return *StubsSection; + } + + ArrayRef getGOTEntryBlockContent() { + return {reinterpret_cast(NullGOTEntryContent), + G.getPointerSize()}; + } + + ArrayRef getStubBlockContent() { + auto StubContent = isRV64() ? RV64StubContent : RV32StubContent; + return {reinterpret_cast(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 @@ 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(Value); + break; + } + case R_RISCV_64: { + int64_t Value = E.getTarget().getAddress() + E.getAddend(); + *(little64_t *)FixupPtr = static_cast(Value); + break; + } case R_RISCV_HI20: { int64_t Value = E.getTarget().getAddress() + E.getAddend(); int32_t Hi = (Value + 0x800) & 0xFFFFF000; @@ -163,6 +275,10 @@ 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("Unsupported riscv relocation:" + @@ -304,6 +420,8 @@ 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)); diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv32_got_plt_reloc.s b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv32_got_plt_reloc.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv32_got_plt_reloc.s @@ -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 diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv64_got_plt_reloc.s b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv64_got_plt_reloc.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_riscv64_got_plt_reloc.s @@ -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 +