diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv64.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv64.h @@ -0,0 +1,52 @@ +//===--- ELF_riscv64.h - JIT link functions for ELF/x86-64 ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for ELF/riscv64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +namespace ELF_riscv64_Edges { +enum ELFRISCVRelocationKind : Edge::Kind { + R_RISCV_32 = Edge::FirstRelocation, + R_RISCV_64, + R_RISCV_HI20, + R_RISCV_LO12_I, + R_RISCV_CALL, + R_RISCV_PCREL_HI20, + R_RISCV_PCREL_LO12_I +}; +} // end namespace ELF_riscv64_Edges + +/// Create a LinkGraph from an ELF/riscv64 relocatable object +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +Expected> +createLinkGraphFromELFObject_riscv64(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be a ELF riscv64 object file. +void link_ELF_riscv64(std::unique_ptr G, + std::unique_ptr Ctx); + +/// Return the string name of the given ELF riscv64 edge kind. +const char *getELFRISCVRelocationKindName(Edge::Kind R); +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -17,6 +17,7 @@ ELF.cpp ELFLinkGraphBuilder.cpp ELF_x86_64.cpp + ELF_riscv64.cpp # Architectures: x86_64.cpp diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp --- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp @@ -14,6 +14,7 @@ #include "llvm/ExecutionEngine/JITLink/ELF.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/ExecutionEngine/JITLink/ELF_riscv64.h" #include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" @@ -66,6 +67,8 @@ switch (*TargetMachineArch) { case ELF::EM_X86_64: return createLinkGraphFromELFObject_x86_64(ObjectBuffer); + case ELF::EM_RISCV: + return createLinkGraphFromELFObject_riscv64(ObjectBuffer); default: return make_error( "Unsupported target machine architecture in ELF object " + @@ -79,6 +82,9 @@ case Triple::x86_64: link_ELF_x86_64(std::move(G), std::move(Ctx)); return; + case Triple::riscv64: + link_ELF_riscv64(std::move(G), std::move(Ctx)); + return; default: Ctx->notifyFailed(make_error( "Unsupported target machine architecture in ELF link graph " + diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv64.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv64.cpp @@ -0,0 +1,304 @@ +//===---- ELF_riscv64.cpp -JIT linker implementation for ELF/riscv64 ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// ELF/riscv64 jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF_riscv64.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" + +#include "ELFLinkGraphBuilder.h" +#include "JITLinkGeneric.h" + +#define DEBUG_TYPE "jitlink" +using namespace llvm; + +const char *const DwarfSectionNames[] = { +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ + ELF_NAME, +#include "llvm/BinaryFormat/Dwarf.def" +#undef HANDLE_DWARF_SECTION +}; + +static bool isDwarfSection(StringRef SectionName) { + return llvm::is_contained(DwarfSectionNames, SectionName); +} + +namespace llvm { +namespace jitlink { + +static const Edge &getRISCVPCRelHi20(const Block &B, const Edge &E) { + using namespace ELF_riscv64_Edges; + assert( + E.getKind() == R_RISCV_PCREL_LO12_I && + "Not a R_RISCV_PCREL_LO12_I relocation that want find the Hi relocation"); + + // It seems that the edges in block were sorted by offset in a assending order + auto Bound = std::lower_bound(B.edges().begin(), B.edges().end(), E, + [](const Edge &lhs, const Edge &rhs) { + return lhs.getOffset() < rhs.getOffset(); + }); + + for (auto it = Bound; it != B.edges().begin(); --it) + if (it->getKind() == R_RISCV_PCREL_HI20) + return *it; + assert(B.edges().begin()->getKind() == R_RISCV_PCREL_HI20 && + "No R_RISCV_PCREL_HI20 be found for LO12 PC relocation type"); + return *B.edges().begin(); +} + +class ELFJITLinker_riscv64 : public JITLinker { + friend class JITLinker; + +public: + ELFJITLinker_riscv64(std::unique_ptr Ctx, + std::unique_ptr G, + PassConfiguration PassConfig) + : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {} + +private: + Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { + using namespace ELF_riscv64_Edges; + using namespace llvm::support; + + char *BlockWorkingMem = B.getAlreadyMutableContent().data(); + char *FixupPtr = BlockWorkingMem + E.getOffset(); + JITTargetAddress FixupAddress = B.getAddress() + E.getOffset(); + switch (E.getKind()) { + case R_RISCV_HI20: { + int64_t Value = E.getTarget().getAddress() + E.getAddend(); + int32_t Hi = (Value + 0x800) & 0xFFFFF000; + uint32_t RawInstr = *(little32_t *)FixupPtr; + *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast(Hi); + break; + } + case R_RISCV_LO12_I: { + int64_t Value = E.getTarget().getAddress() + E.getAddend(); + int32_t Lo = Value - ((Value + 0x800) & 0xFFFFF000); + uint32_t RawInstr = *(little32_t *)FixupPtr; + *(little32_t *)FixupPtr = + (RawInstr & 0xFFFFF) | (static_cast(Lo & 0xFFF) << 20); + break; + } + case R_RISCV_CALL: { + int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress; + int32_t Hi = (Value + 0x800) & 0xFFFFF000; + int32_t Lo = Value - Hi; + uint32_t RawInstrAuipc = *(little32_t *)FixupPtr; + uint32_t RawInstrJalr = *(little32_t *)(FixupPtr + 4); + *(little32_t *)FixupPtr = RawInstrAuipc | static_cast(Hi); + *(little32_t *)(FixupPtr + 4) = + RawInstrJalr | (static_cast(Lo) << 20); + break; + } + case R_RISCV_PCREL_HI20: { + int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress; + int32_t Hi = (Value + 0x800) & 0xFFFFF000; + uint32_t RawInstr = *(little32_t *)FixupPtr; + *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast(Hi); + break; + } + case R_RISCV_PCREL_LO12_I: { + const Edge &RelHI20 = getRISCVPCRelHi20(B, E); + int64_t Value = RelHI20.getTarget().getAddress() + RelHI20.getAddend() - + E.getTarget().getAddress(); + int64_t Lo = Value - ((Value + 0x800) & 0xFFFFF000); + uint32_t RawInstr = *(little32_t *)FixupPtr; + *(little32_t *)FixupPtr = + (RawInstr & 0xFFFFF) | (static_cast(Lo & 0xFFF) << 20); + break; + } + } + return Error::success(); + } +}; + +class ELFLinkGraphBuilder_riscv64 + : public ELFLinkGraphBuilder { +private: + static Expected + getRelocationKind(const uint32_t Type) { + switch (Type) { + case ELF::R_RISCV_32: + return ELF_riscv64_Edges::R_RISCV_32; + case ELF::R_RISCV_64: + return ELF_riscv64_Edges::R_RISCV_64; + case ELF::R_RISCV_HI20: + return ELF_riscv64_Edges::R_RISCV_HI20; + case ELF::R_RISCV_LO12_I: + return ELF_riscv64_Edges::R_RISCV_LO12_I; + case ELF::R_RISCV_CALL: + return ELF_riscv64_Edges::R_RISCV_CALL; + case ELF::R_RISCV_PCREL_HI20: + return ELF_riscv64_Edges::R_RISCV_PCREL_HI20; + case ELF::R_RISCV_PCREL_LO12_I: + return ELF_riscv64_Edges::R_RISCV_PCREL_LO12_I; + } + + return make_error("Unsupported x86-64 relocation:" + + formatv("{0:d}", Type)); + } + + Error addRelocations() override { + LLVM_DEBUG(dbgs() << "Adding relocations\n"); + + // TODO a partern is forming of iterate some sections but only give me + // ones I am interested, I should abstract that concept some where + for (auto &SecRef : Sections) { + if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL) + continue; + // TODO can the elf obj file do this for me? + auto RelSectName = Obj.getSectionName(SecRef); + if (!RelSectName) + return RelSectName.takeError(); + + LLVM_DEBUG({ + dbgs() << "Adding relocations from section " << *RelSectName << "\n"; + }); + + auto UpdateSection = Obj.getSection(SecRef.sh_info); + if (!UpdateSection) + return UpdateSection.takeError(); + + auto UpdateSectionName = Obj.getSectionName(**UpdateSection); + if (!UpdateSectionName) + return UpdateSectionName.takeError(); + // Don't process relocations for debug sections. + if (isDwarfSection(*UpdateSectionName)) { + LLVM_DEBUG({ + dbgs() << " Target is dwarf section " << *UpdateSectionName + << ". Skipping.\n"; + }); + continue; + } else + LLVM_DEBUG({ + dbgs() << " For target section " << *UpdateSectionName << "\n"; + }); + + auto JITSection = G->findSectionByName(*UpdateSectionName); + if (!JITSection) + return make_error( + "Refencing a section that wasn't added to graph" + + *UpdateSectionName, + llvm::inconvertibleErrorCode()); + + auto Relocations = Obj.relas(SecRef); + if (!Relocations) + Relocations.takeError(); + + for (const auto &Rela : *Relocations) { + auto Type = Rela.getType(false); + + LLVM_DEBUG({ + dbgs() << "Relocation Type: " << Type << "\n" + << "Name: " << Obj.getRelocationTypeName(Type) << "\n"; + }); + + auto SymbolIndex = Rela.getSymbol(false); + auto Symbol = Obj.getRelocationSymbol(Rela, SymTabSec); + if (!Symbol) + return Symbol.takeError(); + + auto BlockToFix = *(JITSection->blocks().begin()); + auto *TargetSymbol = getGraphSymbol(SymbolIndex); + + if (!TargetSymbol) { + return make_error( + "Could not find symbol at given index, did you add it to " + "JITSymbolTable? index: " + + std::to_string(SymbolIndex) + + ", shndx: " + std::to_string((*Symbol)->st_shndx) + + " Size of table: " + std::to_string(GraphSymbols.size()), + llvm::inconvertibleErrorCode()); + } + uint64_t Addend = Rela.r_addend; + JITTargetAddress FixupAddress = + (*UpdateSection)->sh_addr + Rela.r_offset; + + LLVM_DEBUG({ + dbgs() << "Processing relocation at " + << format("0x%016" PRIx64, FixupAddress) << "\n"; + }); + auto Kind = getRelocationKind(Type); + if (!Kind) + return Kind.takeError(); + + BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(), + *TargetSymbol, Addend); + } + } + return Error::success(); + } + +public: + ELFLinkGraphBuilder_riscv64(StringRef FileName, + const object::ELFFile &Obj) + : ELFLinkGraphBuilder(Obj, Triple("riscv64-unknown-linux"), FileName, + getELFRISCVRelocationKindName) {} +}; + +Expected> +createLinkGraphFromELFObject_riscv64(MemoryBufferRef ObjectBuffer) { + LLVM_DEBUG({ + dbgs() << "Building jitlink graph for new input " + << ObjectBuffer.getBufferIdentifier() << "...\n"; + }); + + auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer); + if (!ELFObj) + return ELFObj.takeError(); + + auto &ELFObjFile = cast>(**ELFObj); + assert(ELFObjFile.getArch() == Triple::riscv64 && + "RISCV 32 is not supportted in JITLink"); + return ELFLinkGraphBuilder_riscv64((*ELFObj)->getFileName(), + ELFObjFile.getELFFile()) + .buildGraph(); +} + +void link_ELF_riscv64(std::unique_ptr G, + std::unique_ptr Ctx) { + PassConfiguration Config; + const Triple &TT = G->getTargetTriple(); + if (Ctx->shouldAddDefaultTargetPasses(TT)) { + if (auto MarkLive = Ctx->getMarkLivePass(TT)) + Config.PrePrunePasses.push_back(std::move(MarkLive)); + else + Config.PrePrunePasses.push_back(markAllSymbolsLive); + } + if (auto Err = Ctx->modifyPassConfig(*G, Config)) + return Ctx->notifyFailed(std::move(Err)); + + ELFJITLinker_riscv64::link(std::move(Ctx), std::move(G), std::move(Config)); +} + +const char *getELFRISCVRelocationKindName(Edge::Kind R) { + using namespace ELF_riscv64_Edges; + switch (R) { + case R_RISCV_32: + return "R_RISCV_32"; + case R_RISCV_64: + return "R_RISCV_64"; + case R_RISCV_CALL: + return "R_RISCV_CALL"; + case R_RISCV_HI20: + return "R_RISCV_HI20"; + case R_RISCV_LO12_I: + return "R_RISCV_LO12_I"; + case R_RISCV_PCREL_HI20: + return "R_RISCV_PCREL_HI20"; + case R_RISCV_PCREL_LO12_I: + return "R_RISCV_PCREL_LO12_I"; + } + return getGenericEdgeKindName(R); +} +} // namespace jitlink +} // namespace llvm diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_pc_indirect.s b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_pc_indirect.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_pc_indirect.s @@ -0,0 +1,36 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=riscv64 -position-independent -filetype=obj -o %t/elf_sm_pic_reloc.o %s +# RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \ +# RUN: -check %s %t/elf_sm_pic_reloc.o +# +# Test ELF small/PIC relocations + + .text + .file "testcase.c" + +# Empty main entry point. + .globl main + .p2align 4, 0x90 + .type main,@function +main: + ret + + .size main, .-main + +# Test R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO +# jitlink-check: decode_operand(test_pcrel32, 1) = ((named_data - test_pcrel32) + 0x800) >> 12 + .globl test_pcrel32 + .p2align 4, 0x90 + .type test_pcrel32,@function +test_pcrel32: + auipc a0, %pcrel_hi(named_data) + ld a0, %pcrel_lo(test_pcrel32)(a0) + + .size test_pcrel32, .-test_pcrel32 + + .data + .type named_data,@object + .p2align 4, 0x90 +named_data: + .quad 42 + .size named_data, 8