1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00

[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
This commit is contained in:
luxufan 2021-07-05 20:04:17 +08:00
parent 43ffee6b95
commit 85def5bf4e
6 changed files with 485 additions and 0 deletions

View File

@ -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<std::unique_ptr<LinkGraph>>
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<LinkGraph> G,
std::unique_ptr<JITLinkContext> Ctx);
} // end namespace jitlink
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H

View File

@ -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

View File

@ -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

View File

@ -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<LinkGraph> G,
std::unique_ptr<JITLinkContext> 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;

View File

@ -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<const Edge &> 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<JITLinkError>(
"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<ELFJITLinker_riscv> {
friend class JITLinker<ELFJITLinker_riscv>;
public:
ELFJITLinker_riscv(std::unique_ptr<JITLinkContext> Ctx,
std::unique_ptr<LinkGraph> 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<uint32_t>(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<uint32_t>(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<uint32_t>(Hi);
*(little32_t *)(FixupPtr + 4) =
RawInstrJalr | (static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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 <typename ELFT>
class ELFLinkGraphBuilder_riscv : public ELFLinkGraphBuilder<ELFT> {
private:
static Expected<riscv::EdgeKind_riscv>
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<JITLinkError>("Unsupported riscv relocation:" +
formatv("{0:d}", Type));
}
Error addRelocations() override {
using Base = ELFLinkGraphBuilder<ELFT>;
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<llvm::StringError>(
"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<llvm::StringError>(
"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<ELFT> &Obj, const Triple T)
: ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
riscv::getEdgeKindName) {}
};
Expected<std::unique_ptr<LinkGraph>>
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<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
return ELFLinkGraphBuilder_riscv<object::ELF64LE>(
(*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
(*ELFObj)->makeTriple())
.buildGraph();
} else {
assert((*ELFObj)->getArch() == Triple::riscv32 &&
"Invalid triple for RISCV ELF object file");
auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
return ELFLinkGraphBuilder_riscv<object::ELF32LE>(
(*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
(*ELFObj)->makeTriple())
.buildGraph();
}
}
void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
std::unique_ptr<JITLinkContext> 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

View File

@ -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