Skip to content

Commit f873029

Browse files
committed
[BOLT] Add minimal RISC-V 64-bit support
Just enough features are implemented to process a simple "hello world" executable and produce something that still runs (including libc calls). This was mainly a matter of implementing support for various relocations. Currently, the following are handled: - R_RISCV_JAL - R_RISCV_CALL - R_RISCV_CALL_PLT - R_RISCV_BRANCH - R_RISCV_RVC_BRANCH - R_RISCV_RVC_JUMP - R_RISCV_GOT_HI20 - R_RISCV_PCREL_HI20 - R_RISCV_PCREL_LO12_I - R_RISCV_RELAX - R_RISCV_NONE Executables linked with linker relaxation will probably fail to be processed. BOLT relocates .text to a high address while leaving .plt at its original (low) address. This causes PC-relative PLT calls that were relaxed to a JAL to not fit their offset in an I-immediate anymore. This is something that will be addressed in a later patch. Changes to the BOLT core are relatively minor. Two things were tricky to implement and needed slightly larger changes. I'll explain those below. The R_RISCV_CALL(_PLT) relocation is put on the first instruction of a AUIPC/JALR pair, the second does not get any relocation (unlike other PCREL pairs). This causes issues with the combinations of the way BOLT processes binaries and the RISC-V MC-layer handles relocations: - BOLT reassembles instructions one by one and since the JALR doesn't have a relocation, it simply gets copied without modification; - Even though the MC-layer handles R_RISCV_CALL properly (adjusts both the AUIPC and the JALR), it assumes the immediates of both instructions are 0 (to be able to or-in a new value). This will most likely not be the case for the JALR that got copied over. To handle this difficulty without resorting to RISC-V-specific hacks in the BOLT core, a new binary pass was added that searches for AUIPC/JALR pairs and zeroes-out the immediate of the JALR. A second difficulty was supporting ABS symbols. As far as I can tell, ABS symbols were not handled at all, causing __global_pointer$ to break. RewriteInstance::analyzeRelocation was updated to handle these generically. Tests are provided for all supported relocations. Note that in order to test the correct handling of PLT entries, an ELF file produced by GCC had to be used. While I tried to strip the YAML representation, it's still quite large. Any suggestions on how to improve this would be appreciated. Reviewed By: rafauler Differential Revision: https://reviews.llvm.org/D145687
1 parent f9f92f1 commit f873029

26 files changed

+1740
-11
lines changed

bolt/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ list(INSERT CMAKE_MODULE_PATH 0 "${BOLT_SOURCE_DIR}/cmake/modules")
99

1010
# Determine default set of targets to build -- the intersection of
1111
# those BOLT supports and those LLVM is targeting.
12-
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86")
12+
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86;RISCV")
1313
set(BOLT_TARGETS_TO_BUILD_default)
1414
foreach (tgt ${BOLT_TARGETS_TO_BUILD_all})
1515
if (tgt IN_LIST LLVM_TARGETS_TO_BUILD)

bolt/CODE_OWNERS.TXT

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ D: DWARF support
2020
N: Vladislav Khmelevsky
2121
E: och95@yandex.ru
2222
D: AArch64 backend
23+
24+
N: Job Noorman
25+
E: jnoorman@igalia.com
26+
D: RISC-V backend

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ class BinaryContext {
722722
TheTriple->getArch() == llvm::Triple::x86_64;
723723
}
724724

725+
bool isRISCV() const { return TheTriple->getArch() == llvm::Triple::riscv64; }
726+
725727
// AArch64-specific functions to check if symbol is used to delimit
726728
// code/data in .text. Code is marked by $x, data by $d.
727729
MarkerSymType getMarkerType(const SymbolRef &Symbol) const;

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,12 @@ class MCPlusBuilder {
633633
return false;
634634
}
635635

636+
/// Returns true if First/Second is a AUIPC/JALR call pair.
637+
virtual bool isRISCVCall(const MCInst &First, const MCInst &Second) const {
638+
llvm_unreachable("not implemented");
639+
return false;
640+
}
641+
636642
/// If non-zero, this is used to fill the executable space with instructions
637643
/// that will trap. Defaults to 0.
638644
virtual unsigned getTrapFillValue() const { return 0; }
@@ -2032,6 +2038,10 @@ MCPlusBuilder *createAArch64MCPlusBuilder(const MCInstrAnalysis *,
20322038
const MCInstrInfo *,
20332039
const MCRegisterInfo *);
20342040

2041+
MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *,
2042+
const MCInstrInfo *,
2043+
const MCRegisterInfo *);
2044+
20352045
} // namespace bolt
20362046
} // namespace llvm
20372047

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- bolt/Passes/FixRISCVCallsPass.h --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file declares the FixRISCVCallsPass class, which sets the JALR immediate
10+
// to 0 for AUIPC/JALR pairs with a R_RISCV_CALL(_PLT) relocation. This is
11+
// necessary since MC expects it to be zero in order to or-in fixups.
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef BOLT_PASSES_FIXRISCVCALLSPASS_H
15+
#define BOLT_PASSES_FIXRISCVCALLSPASS_H
16+
17+
#include "bolt/Passes/BinaryPasses.h"
18+
19+
namespace llvm {
20+
namespace bolt {
21+
22+
class FixRISCVCallsPass : public BinaryFunctionPass {
23+
void runOnFunction(BinaryFunction &Function);
24+
25+
public:
26+
explicit FixRISCVCallsPass(const cl::opt<bool> &PrintPass)
27+
: BinaryFunctionPass(PrintPass) {}
28+
29+
const char *getName() const override { return "fix-riscv-calls"; }
30+
31+
/// Pass entry point
32+
void runOnFunctions(BinaryContext &BC) override;
33+
};
34+
35+
} // namespace bolt
36+
} // namespace llvm
37+
38+
#endif

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ class RewriteInstance {
279279
/// is the expected .plt \p Section entry function size.
280280
void disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize);
281281

282+
/// Disassemble riscv-specific .plt \p Section auxiliary function
283+
void disassemblePLTSectionRISCV(BinarySection &Section);
284+
282285
/// ELF-specific part. TODO: refactor into new class.
283286
#define ELF_FUNCTION(TYPE, FUNC) \
284287
template <typename ELFT> TYPE FUNC(object::ELFObjectFile<ELFT> *Obj); \
@@ -536,6 +539,9 @@ class RewriteInstance {
536539
const PLTSectionInfo AArch64_PLTSections[3] = {
537540
{".plt"}, {".iplt"}, {nullptr}};
538541

542+
/// RISCV PLT sections.
543+
const PLTSectionInfo RISCV_PLTSections[3] = {{".plt"}, {nullptr}};
544+
539545
/// Return PLT information for a section with \p SectionName or nullptr
540546
/// if the section is not PLT.
541547
const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) {
@@ -549,6 +555,9 @@ class RewriteInstance {
549555
case Triple::aarch64:
550556
PLTSI = AArch64_PLTSections;
551557
break;
558+
case Triple::riscv64:
559+
PLTSI = RISCV_PLTSections;
560+
break;
552561
}
553562
for (; PLTSI && PLTSI->Name; ++PLTSI)
554563
if (SectionName == PLTSI->Name)

bolt/lib/Core/BinaryContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
128128
ArchName = "aarch64";
129129
FeaturesStr = "+all";
130130
break;
131+
case llvm::Triple::riscv64:
132+
ArchName = "riscv64";
133+
// RV64GC
134+
FeaturesStr = "+m,+a,+f,+d,+zicsr,+zifencei,+c";
135+
break;
131136
default:
132137
return createStringError(std::errc::not_supported,
133138
"BOLT-ERROR: Unrecognized machine in ELF file");

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,7 @@ bool BinaryFunction::disassemble() {
13231323
if (BC.isAArch64())
13241324
handleAArch64IndirectCall(Instruction, Offset);
13251325
}
1326-
} else if (BC.isAArch64()) {
1326+
} else if (BC.isAArch64() || BC.isRISCV()) {
13271327
// Check if there's a relocation associated with this instruction.
13281328
bool UsedReloc = false;
13291329
for (auto Itr = Relocations.lower_bound(Offset),
@@ -1343,7 +1343,7 @@ bool BinaryFunction::disassemble() {
13431343
UsedReloc = true;
13441344
}
13451345

1346-
if (MIB->hasPCRelOperand(Instruction) && !UsedReloc)
1346+
if (!BC.isRISCV() && MIB->hasPCRelOperand(Instruction) && !UsedReloc)
13471347
handlePCRelOperand(Instruction, AbsoluteInstrAddr, Size);
13481348
}
13491349

0 commit comments

Comments
 (0)