From 0ad562b48bfd7a487eb38ceaa309cdd9a1ad87e7 Mon Sep 17 00:00:00 2001 From: luxufan <932494295@qq.com> Date: Mon, 5 Jul 2021 20:04:17 +0800 Subject: [PATCH] [JITLink][RISCV] Initial Support RISCV64 in JITLink This patch is the initial support, it implements translation from object file to JIT link graph, and very few relocations were supported. Currently, the test file ELF_pc_indirect.s is passed, the HelloWorld program(compiled with mno-relax flag) can be linked correctly and run on instruction emulator correctly. In the downstream implementation, I have implemented the GOT, PLT function, and EHFrame and some optimization will be implement soon. I will organize the code in to patches, then gradually send it to upstream. Differential Revision: https://reviews.llvm.org/D105429 --- .../llvm/ExecutionEngine/JITLink/ELF_riscv.h | 38 +++ llvm/include/llvm/ExecutionEngine/JITLink/riscv.h | 84 ++++++ llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt | 2 + llvm/lib/ExecutionEngine/JITLink/ELF.cpp | 7 + llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp | 315 +++++++++++++++++++++ .../JITLink/RISCV/ELF_pc_indirect.s | 39 +++ 6 files changed, 485 insertions(+) create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/riscv.h create mode 100644 llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp create mode 100644 llvm/test/ExecutionEngine/JITLink/RISCV/ELF_pc_indirect.s diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h new file mode 100644 index 0000000..1339ab5 --- /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 index 0000000..a4509f3 --- /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 index ab4b24a..eedcdb9 100644 --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -16,9 +16,11 @@ add_llvm_component_library(LLVMJITLink 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 index 3a270d8..252e44f 100644 --- 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 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) { 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 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) { 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 index 0000000..d0e65ef --- /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 index 0000000..875c8c8 --- /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 -- 2.7.4