diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h @@ -0,0 +1,38 @@ +//===----- ELF_riscv.h - JIT link functions for ELF/riscv ----*- 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/riscv. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an ELF/riscv 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_riscv(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be a ELF riscv object file. +void link_ELF_riscv(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h @@ -0,0 +1,84 @@ +//===-- riscv.h - Generic JITLink riscv edge kinds, utilities -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Generic utilities for graphs representing riscv objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_RISCV_H +#define LLVM_EXECUTIONENGINE_JITLINK_RISCV_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { +namespace riscv { + +/// Represets riscv fixups +enum EdgeKind_riscv : Edge::Kind { + + // TODO: Capture and replace to generic fixups + /// A plain 32-bit pointer value relocation + /// + /// Fixup expression: + /// Fixup <= Target + Addend : uint32 + /// + R_RISCV_32 = Edge::FirstRelocation, + + /// A plain 64-bit pointer value relocation + /// + /// Fixup expression: + /// Fixup <- Target + Addend : uint32 + /// + R_RISCV_64, + + /// High 20 bits of 32-bit pointer value relocation + /// + /// Fixup expression + /// Fixup <- (Target + Addend + 0x800) >> 12 + R_RISCV_HI20, + + /// Low 12 bits of 32-bit pointer value relocation + /// + /// Fixup expression + /// Fixup <- (Target + Addend) & 0xFFF + R_RISCV_LO12_I, + /// High 20 bits of PC relative relocation + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend + 0x800) >> 12 + R_RISCV_PCREL_HI20, + + /// Low 12 bits of PC relative relocation, used by I type instruction format + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend) & 0xFFF + R_RISCV_PCREL_LO12_I, + + /// Low 12 bits of PC relative relocation, used by S type instruction format + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend) & 0xFFF + R_RISCV_PCREL_LO12_S, + + /// PC relative call + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend) + R_RISCV_CALL + +}; + +/// Returns a string name for the given riscv edge. For debugging purposes +/// only +const char *getEdgeKindName(Edge::Kind K); +} // namespace riscv +} // namespace jitlink +} // namespace llvm + +#endif 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 @@ -16,9 +16,11 @@ ELF.cpp ELFLinkGraphBuilder.cpp + ELF_riscv.cpp ELF_x86_64.cpp # Architectures: + riscv.cpp x86_64.cpp ADDITIONAL_HEADER_DIRS 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_riscv.h" #include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" @@ -64,6 +65,8 @@ return TargetMachineArch.takeError(); switch (*TargetMachineArch) { + case ELF::EM_RISCV: + return createLinkGraphFromELFObject_riscv(ObjectBuffer); case ELF::EM_X86_64: return createLinkGraphFromELFObject_x86_64(ObjectBuffer); default: @@ -76,6 +79,10 @@ void link_ELF(std::unique_ptr G, std::unique_ptr Ctx) { switch (G->getTargetTriple().getArch()) { + case Triple::riscv32: + case Triple::riscv64: + link_ELF_riscv(std::move(G), std::move(Ctx)); + return; case Triple::x86_64: link_ELF_x86_64(std::move(G), std::move(Ctx)); return; diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp @@ -0,0 +1,315 @@ +//===------- ELF_riscv.cpp -JIT linker implementation for ELF/riscv -------===// +// +// 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/riscv jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF_riscv.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; + +namespace llvm { +namespace jitlink { + +static Expected getRISCVPCRelHi20(const Edge &E) { + using namespace riscv; + assert((E.getKind() == R_RISCV_PCREL_LO12_I || + E.getKind() == R_RISCV_PCREL_LO12_S) && + "Can only have high relocation for R_RISCV_PCREL_LO12_I or " + "R_RISCV_PCREL_LO12_S"); + + const Symbol &Sym = E.getTarget(); + const Block &B = Sym.getBlock(); + JITTargetAddress Offset = Sym.getOffset(); + + struct Comp { + bool operator()(const Edge &Lhs, JITTargetAddress Offset) { + return Lhs.getOffset() < Offset; + } + bool operator()(JITTargetAddress Offset, const Edge &Rhs) { + return Offset < Rhs.getOffset(); + } + }; + + auto Bound = + std::equal_range(B.edges().begin(), B.edges().end(), Offset, Comp{}); + + for (auto It = Bound.first; It != Bound.second; ++It) { + if (It->getKind() == R_RISCV_PCREL_HI20) + return *It; + } + + return make_error( + "No HI20 PCREL relocation type be found for LO12 PCREL relocation type"); +} + +static uint32_t extractBits(uint64_t Num, unsigned High, unsigned Low) { + return (Num & ((1ULL << (High + 1)) - 1)) >> Low; +} + +class ELFJITLinker_riscv : public JITLinker { + friend class JITLinker; + +public: + ELFJITLinker_riscv(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 riscv; + 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 & 0xFFF; + 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 & 0xFFF; + 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: { + auto RelHI20 = getRISCVPCRelHi20(E); + if (!RelHI20) + return RelHI20.takeError(); + int64_t Value = RelHI20->getTarget().getAddress() + + RelHI20->getAddend() - E.getTarget().getAddress(); + int64_t Lo = Value & 0xFFF; + uint32_t RawInstr = *(little32_t *)FixupPtr; + *(little32_t *)FixupPtr = + (RawInstr & 0xFFFFF) | (static_cast(Lo & 0xFFF) << 20); + break; + } + case R_RISCV_PCREL_LO12_S: { + auto RelHI20 = getRISCVPCRelHi20(E); + int64_t Value = RelHI20->getTarget().getAddress() + + RelHI20->getAddend() - E.getTarget().getAddress(); + int64_t Lo = Value & 0xFFF; + uint32_t Imm31_25 = extractBits(Lo, 11, 5) << 25; + uint32_t Imm11_7 = extractBits(Lo, 4, 0) << 7; + uint32_t RawInstr = *(little32_t *)FixupPtr; + + *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7; + break; + } + } + return Error::success(); + } +}; + +template +class ELFLinkGraphBuilder_riscv : public ELFLinkGraphBuilder { +private: + static Expected + getRelocationKind(const uint32_t Type) { + using namespace riscv; + switch (Type) { + case ELF::R_RISCV_32: + return EdgeKind_riscv::R_RISCV_32; + case ELF::R_RISCV_64: + return EdgeKind_riscv::R_RISCV_64; + case ELF::R_RISCV_HI20: + return EdgeKind_riscv::R_RISCV_HI20; + case ELF::R_RISCV_LO12_I: + return EdgeKind_riscv::R_RISCV_LO12_I; + case ELF::R_RISCV_CALL: + return EdgeKind_riscv::R_RISCV_CALL; + case ELF::R_RISCV_PCREL_HI20: + return EdgeKind_riscv::R_RISCV_PCREL_HI20; + case ELF::R_RISCV_PCREL_LO12_I: + return EdgeKind_riscv::R_RISCV_PCREL_LO12_I; + case ELF::R_RISCV_PCREL_LO12_S: + return EdgeKind_riscv::R_RISCV_PCREL_LO12_S; + } + + return make_error("Unsupported riscv relocation:" + + formatv("{0:d}", Type)); + } + + Error addRelocations() override { + using Base = ELFLinkGraphBuilder; + 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 : Base::Sections) { + if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL) + continue; + auto RelSectName = Base::Obj.getSectionName(SecRef); + if (!RelSectName) + return RelSectName.takeError(); + + LLVM_DEBUG({ + dbgs() << "Adding relocations from section " << *RelSectName << "\n"; + }); + + auto UpdateSection = Base::Obj.getSection(SecRef.sh_info); + if (!UpdateSection) + return UpdateSection.takeError(); + + auto UpdateSectionName = Base::Obj.getSectionName(**UpdateSection); + if (!UpdateSectionName) + return UpdateSectionName.takeError(); + // Don't process relocations for debug sections. + if (Base::isDwarfSection(*UpdateSectionName)) { + LLVM_DEBUG({ + dbgs() << " Target is dwarf section " << *UpdateSectionName + << ". Skipping.\n"; + }); + continue; + } else + LLVM_DEBUG({ + dbgs() << " For target section " << *UpdateSectionName << "\n"; + }); + + auto *JITSection = Base::G->findSectionByName(*UpdateSectionName); + if (!JITSection) + return make_error( + "Refencing a section that wasn't added to graph" + + *UpdateSectionName, + llvm::inconvertibleErrorCode()); + + auto Relocations = Base::Obj.relas(SecRef); + if (!Relocations) + return Relocations.takeError(); + + for (const auto &Rela : *Relocations) { + auto Type = Rela.getType(false); + + LLVM_DEBUG({ + dbgs() << "Relocation Type: " << Type << "\n" + << "Name: " << Base::Obj.getRelocationTypeName(Type) << "\n"; + }); + + auto SymbolIndex = Rela.getSymbol(false); + auto Symbol = Base::Obj.getRelocationSymbol(Rela, Base::SymTabSec); + if (!Symbol) + return Symbol.takeError(); + + auto BlockToFix = *(JITSection->blocks().begin()); + auto *TargetSymbol = Base::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(Base::GraphSymbols.size()), + llvm::inconvertibleErrorCode()); + } + int64_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_riscv(StringRef FileName, + const object::ELFFile &Obj, const Triple T) + : ELFLinkGraphBuilder(Obj, std::move(T), FileName, + riscv::getEdgeKindName) {} +}; + +Expected> +createLinkGraphFromELFObject_riscv(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(); + + if ((*ELFObj)->getArch() == Triple::riscv64) { + auto &ELFObjFile = cast>(**ELFObj); + return ELFLinkGraphBuilder_riscv( + (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), + (*ELFObj)->makeTriple()) + .buildGraph(); + } else { + assert((*ELFObj)->getArch() == Triple::riscv32 && + "Invalid triple for RISCV ELF object file"); + auto &ELFObjFile = cast>(**ELFObj); + return ELFLinkGraphBuilder_riscv( + (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), + (*ELFObj)->makeTriple()) + .buildGraph(); + } +} + +void link_ELF_riscv(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_riscv::link(std::move(Ctx), std::move(G), std::move(Config)); +} + +} // 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,39 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=riscv64 -position-independent -filetype=obj -o %t/elf_riscv64_sm_pic_reloc.o %s +# RUN: llvm-mc -triple=riscv32 -position-independent -filetype=obj -o %t/elf_riscv32_sm_pic_reloc.o %s +# RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \ +# RUN: -check %s %t/elf_riscv64_sm_pic_reloc.o +# RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \ +# RUN: -check %s %t/elf_riscv32_sm_pic_reloc.o +# +# Test ELF small/PIC relocations + + .text + .file "testcase.c" + +# Empty main entry point. + .globl main + .p2align 1 + .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)[31:12] + .globl test_pcrel32 + .p2align 1 + .type test_pcrel32,@function +test_pcrel32: + auipc a0, %pcrel_hi(named_data) + lw a0, %pcrel_lo(test_pcrel32)(a0) + + .size test_pcrel32, .-test_pcrel32 + + .data + .type named_data,@object + .p2align 1 +named_data: + .quad 42 + .size named_data, 4