[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
This commit is contained in:
Job Noorman
2023-06-16 11:49:19 +02:00
parent f9f92f13f6
commit f873029386
26 changed files with 1740 additions and 11 deletions

View File

@@ -9,7 +9,7 @@ list(INSERT CMAKE_MODULE_PATH 0 "${BOLT_SOURCE_DIR}/cmake/modules")
# Determine default set of targets to build -- the intersection of
# those BOLT supports and those LLVM is targeting.
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86")
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86;RISCV")
set(BOLT_TARGETS_TO_BUILD_default)
foreach (tgt ${BOLT_TARGETS_TO_BUILD_all})
if (tgt IN_LIST LLVM_TARGETS_TO_BUILD)

View File

@@ -20,3 +20,7 @@ D: DWARF support
N: Vladislav Khmelevsky
E: och95@yandex.ru
D: AArch64 backend
N: Job Noorman
E: jnoorman@igalia.com
D: RISC-V backend

View File

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

View File

@@ -633,6 +633,12 @@ public:
return false;
}
/// Returns true if First/Second is a AUIPC/JALR call pair.
virtual bool isRISCVCall(const MCInst &First, const MCInst &Second) const {
llvm_unreachable("not implemented");
return false;
}
/// If non-zero, this is used to fill the executable space with instructions
/// that will trap. Defaults to 0.
virtual unsigned getTrapFillValue() const { return 0; }
@@ -2032,6 +2038,10 @@ MCPlusBuilder *createAArch64MCPlusBuilder(const MCInstrAnalysis *,
const MCInstrInfo *,
const MCRegisterInfo *);
MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *,
const MCInstrInfo *,
const MCRegisterInfo *);
} // namespace bolt
} // namespace llvm

View File

@@ -0,0 +1,38 @@
//===- bolt/Passes/FixRISCVCallsPass.h --------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file declares the FixRISCVCallsPass class, which sets the JALR immediate
// to 0 for AUIPC/JALR pairs with a R_RISCV_CALL(_PLT) relocation. This is
// necessary since MC expects it to be zero in order to or-in fixups.
//===----------------------------------------------------------------------===//
#ifndef BOLT_PASSES_FIXRISCVCALLSPASS_H
#define BOLT_PASSES_FIXRISCVCALLSPASS_H
#include "bolt/Passes/BinaryPasses.h"
namespace llvm {
namespace bolt {
class FixRISCVCallsPass : public BinaryFunctionPass {
void runOnFunction(BinaryFunction &Function);
public:
explicit FixRISCVCallsPass(const cl::opt<bool> &PrintPass)
: BinaryFunctionPass(PrintPass) {}
const char *getName() const override { return "fix-riscv-calls"; }
/// Pass entry point
void runOnFunctions(BinaryContext &BC) override;
};
} // namespace bolt
} // namespace llvm
#endif

View File

@@ -279,6 +279,9 @@ private:
/// is the expected .plt \p Section entry function size.
void disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize);
/// Disassemble riscv-specific .plt \p Section auxiliary function
void disassemblePLTSectionRISCV(BinarySection &Section);
/// ELF-specific part. TODO: refactor into new class.
#define ELF_FUNCTION(TYPE, FUNC) \
template <typename ELFT> TYPE FUNC(object::ELFObjectFile<ELFT> *Obj); \
@@ -536,6 +539,9 @@ private:
const PLTSectionInfo AArch64_PLTSections[3] = {
{".plt"}, {".iplt"}, {nullptr}};
/// RISCV PLT sections.
const PLTSectionInfo RISCV_PLTSections[3] = {{".plt"}, {nullptr}};
/// Return PLT information for a section with \p SectionName or nullptr
/// if the section is not PLT.
const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) {
@@ -549,6 +555,9 @@ private:
case Triple::aarch64:
PLTSI = AArch64_PLTSections;
break;
case Triple::riscv64:
PLTSI = RISCV_PLTSections;
break;
}
for (; PLTSI && PLTSI->Name; ++PLTSI)
if (SectionName == PLTSI->Name)

View File

@@ -128,6 +128,11 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
ArchName = "aarch64";
FeaturesStr = "+all";
break;
case llvm::Triple::riscv64:
ArchName = "riscv64";
// RV64GC
FeaturesStr = "+m,+a,+f,+d,+zicsr,+zifencei,+c";
break;
default:
return createStringError(std::errc::not_supported,
"BOLT-ERROR: Unrecognized machine in ELF file");

View File

@@ -1323,7 +1323,7 @@ bool BinaryFunction::disassemble() {
if (BC.isAArch64())
handleAArch64IndirectCall(Instruction, Offset);
}
} else if (BC.isAArch64()) {
} else if (BC.isAArch64() || BC.isRISCV()) {
// Check if there's a relocation associated with this instruction.
bool UsedReloc = false;
for (auto Itr = Relocations.lower_bound(Offset),
@@ -1343,7 +1343,7 @@ bool BinaryFunction::disassemble() {
UsedReloc = true;
}
if (MIB->hasPCRelOperand(Instruction) && !UsedReloc)
if (!BC.isRISCV() && MIB->hasPCRelOperand(Instruction) && !UsedReloc)
handlePCRelOperand(Instruction, AbsoluteInstrAddr, Size);
}

View File

@@ -89,6 +89,24 @@ static bool isSupportedAArch64(uint64_t Type) {
}
}
static bool isSupportedRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_RISCV_JAL:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
case ELF::R_RISCV_BRANCH:
case ELF::R_RISCV_RELAX:
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_PCREL_HI20:
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_RVC_BRANCH:
return true;
}
}
static size_t getSizeForTypeX86(uint64_t Type) {
switch (Type) {
default:
@@ -163,6 +181,28 @@ static size_t getSizeForTypeAArch64(uint64_t Type) {
}
}
static size_t getSizeForTypeRISCV(uint64_t Type) {
switch (Type) {
default:
errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
llvm_unreachable("unsupported relocation type");
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_RVC_BRANCH:
return 2;
case ELF::R_RISCV_JAL:
case ELF::R_RISCV_BRANCH:
case ELF::R_RISCV_PCREL_HI20:
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_32_PCREL:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
return 4;
case ELF::R_RISCV_GOT_HI20:
// See extractValueRISCV for why this is necessary.
return 8;
}
}
static bool skipRelocationTypeX86(uint64_t Type) {
return Type == ELF::R_X86_64_NONE;
}
@@ -171,6 +211,16 @@ static bool skipRelocationTypeAArch64(uint64_t Type) {
return Type == ELF::R_AARCH64_NONE || Type == ELF::R_AARCH64_LD_PREL_LO19;
}
static bool skipRelocationTypeRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_RISCV_NONE:
case ELF::R_RISCV_RELAX:
return true;
}
}
static bool skipRelocationProcessX86(uint64_t &Type, uint64_t Contents) {
return false;
}
@@ -262,6 +312,10 @@ static bool skipRelocationProcessAArch64(uint64_t &Type, uint64_t Contents) {
return false;
}
static bool skipRelocationProcessRISCV(uint64_t &Type, uint64_t Contents) {
return false;
}
static uint64_t encodeValueX86(uint64_t Type, uint64_t Value, uint64_t PC) {
switch (Type) {
default:
@@ -407,6 +461,57 @@ static uint64_t extractValueAArch64(uint64_t Type, uint64_t Contents,
}
}
static uint64_t extractUImmRISCV(uint32_t Contents) {
return SignExtend64<32>(Contents & 0xfffff000);
}
static uint64_t extractIImmRISCV(uint32_t Contents) {
return SignExtend64<12>(Contents >> 20);
}
static uint64_t extractJImmRISCV(uint32_t Contents) {
return SignExtend64<21>(
(((Contents >> 21) & 0x3ff) << 1) | (((Contents >> 20) & 0x1) << 11) |
(((Contents >> 12) & 0xff) << 12) | (((Contents >> 31) & 0x1) << 20));
}
static uint64_t extractBImmRISCV(uint32_t Contents) {
return SignExtend64<13>(
(((Contents >> 8) & 0xf) << 1) | (((Contents >> 25) & 0x3f) << 5) |
(((Contents >> 7) & 0x1) << 11) | (((Contents >> 31) & 0x1) << 12));
}
static uint64_t extractValueRISCV(uint64_t Type, uint64_t Contents,
uint64_t PC) {
switch (Type) {
default:
errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
llvm_unreachable("unsupported relocation type");
case ELF::R_RISCV_JAL:
return extractJImmRISCV(Contents);
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
return extractUImmRISCV(Contents);
case ELF::R_RISCV_BRANCH:
return extractBImmRISCV(Contents);
case ELF::R_RISCV_GOT_HI20:
// We need to know the exact address of the GOT entry so we extract the
// value from both the AUIPC and L[D|W]. We cannot rely on the symbol in the
// relocation for this since it simply refers to the object that is stored
// in the GOT entry, not to the entry itself.
return extractUImmRISCV(Contents & 0xffffffff) +
extractIImmRISCV(Contents >> 32);
case ELF::R_RISCV_PCREL_HI20:
return extractUImmRISCV(Contents);
case ELF::R_RISCV_PCREL_LO12_I:
return extractIImmRISCV(Contents);
case ELF::R_RISCV_RVC_JUMP:
return SignExtend64<11>(Contents >> 2);
case ELF::R_RISCV_RVC_BRANCH:
return SignExtend64<8>(((Contents >> 2) & 0x1f) | ((Contents >> 5) & 0xe0));
}
}
static bool isGOTX86(uint64_t Type) {
switch (Type) {
default:
@@ -444,6 +549,15 @@ static bool isGOTAArch64(uint64_t Type) {
}
}
static bool isGOTRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_RISCV_GOT_HI20:
return true;
}
}
static bool isTLSX86(uint64_t Type) {
switch (Type) {
default:
@@ -472,6 +586,13 @@ static bool isTLSAArch64(uint64_t Type) {
}
}
static bool isTLSRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
}
}
static bool isPCRelativeX86(uint64_t Type) {
switch (Type) {
default:
@@ -543,33 +664,61 @@ static bool isPCRelativeAArch64(uint64_t Type) {
}
}
static bool isPCRelativeRISCV(uint64_t Type) {
switch (Type) {
default:
llvm_unreachable("Unknown relocation type");
case ELF::R_RISCV_JAL:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
case ELF::R_RISCV_BRANCH:
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_PCREL_HI20:
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_RVC_BRANCH:
case ELF::R_RISCV_32_PCREL:
return true;
}
}
bool Relocation::isSupported(uint64_t Type) {
if (Arch == Triple::aarch64)
return isSupportedAArch64(Type);
if (Arch == Triple::riscv64)
return isSupportedRISCV(Type);
return isSupportedX86(Type);
}
size_t Relocation::getSizeForType(uint64_t Type) {
if (Arch == Triple::aarch64)
return getSizeForTypeAArch64(Type);
if (Arch == Triple::riscv64)
return getSizeForTypeRISCV(Type);
return getSizeForTypeX86(Type);
}
bool Relocation::skipRelocationType(uint64_t Type) {
if (Arch == Triple::aarch64)
return skipRelocationTypeAArch64(Type);
if (Arch == Triple::riscv64)
return skipRelocationTypeRISCV(Type);
return skipRelocationTypeX86(Type);
}
bool Relocation::skipRelocationProcess(uint64_t &Type, uint64_t Contents) {
if (Arch == Triple::aarch64)
return skipRelocationProcessAArch64(Type, Contents);
if (Arch == Triple::riscv64)
skipRelocationProcessRISCV(Type, Contents);
return skipRelocationProcessX86(Type, Contents);
}
uint64_t Relocation::encodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
if (Arch == Triple::aarch64)
return encodeValueAArch64(Type, Value, PC);
if (Arch == Triple::riscv64)
llvm_unreachable("not implemented");
return encodeValueX86(Type, Value, PC);
}
@@ -577,12 +726,16 @@ uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents,
uint64_t PC) {
if (Arch == Triple::aarch64)
return extractValueAArch64(Type, Contents, PC);
if (Arch == Triple::riscv64)
return extractValueRISCV(Type, Contents, PC);
return extractValueX86(Type, Contents, PC);
}
bool Relocation::isGOT(uint64_t Type) {
if (Arch == Triple::aarch64)
return isGOTAArch64(Type);
if (Arch == Triple::riscv64)
return isGOTRISCV(Type);
return isGOTX86(Type);
}
@@ -597,42 +750,56 @@ bool Relocation::isNone(uint64_t Type) { return Type == getNone(); }
bool Relocation::isRelative(uint64_t Type) {
if (Arch == Triple::aarch64)
return Type == ELF::R_AARCH64_RELATIVE;
if (Arch == Triple::riscv64)
return Type == ELF::R_RISCV_RELATIVE;
return Type == ELF::R_X86_64_RELATIVE;
}
bool Relocation::isIRelative(uint64_t Type) {
if (Arch == Triple::aarch64)
return Type == ELF::R_AARCH64_IRELATIVE;
if (Arch == Triple::riscv64)
llvm_unreachable("not implemented");
return Type == ELF::R_X86_64_IRELATIVE;
}
bool Relocation::isTLS(uint64_t Type) {
if (Arch == Triple::aarch64)
return isTLSAArch64(Type);
if (Arch == Triple::riscv64)
return isTLSRISCV(Type);
return isTLSX86(Type);
}
uint64_t Relocation::getNone() {
if (Arch == Triple::aarch64)
return ELF::R_AARCH64_NONE;
if (Arch == Triple::riscv64)
return ELF::R_RISCV_NONE;
return ELF::R_X86_64_NONE;
}
uint64_t Relocation::getPC32() {
if (Arch == Triple::aarch64)
return ELF::R_AARCH64_PREL32;
if (Arch == Triple::riscv64)
return ELF::R_RISCV_32_PCREL;
return ELF::R_X86_64_PC32;
}
uint64_t Relocation::getPC64() {
if (Arch == Triple::aarch64)
return ELF::R_AARCH64_PREL64;
if (Arch == Triple::riscv64)
llvm_unreachable("not implemented");
return ELF::R_X86_64_PC64;
}
bool Relocation::isPCRelative(uint64_t Type) {
if (Arch == Triple::aarch64)
return isPCRelativeAArch64(Type);
if (Arch == Triple::riscv64)
return isPCRelativeRISCV(Type);
return isPCRelativeX86(Type);
}
@@ -696,7 +863,20 @@ void Relocation::print(raw_ostream &OS) const {
};
if (Arch == Triple::aarch64)
OS << AArch64RelocNames[Type];
else
else if (Arch == Triple::riscv64) {
// RISC-V relocations are not sequentially numbered so we cannot use an
// array
switch (Type) {
default:
llvm_unreachable("illegal RISC-V relocation");
#undef ELF_RELOC
#define ELF_RELOC(name, value) \
case value: \
OS << #name; \
break;
#include "llvm/BinaryFormat/ELFRelocs/RISCV.def"
}
} else
OS << X86RelocNames[Type];
OS << ", 0x" << Twine::utohexstr(Offset);
if (Symbol) {

View File

@@ -14,6 +14,7 @@ add_llvm_library(LLVMBOLTPasses
FrameAnalysis.cpp
FrameOptimizer.cpp
FixRelaxationPass.cpp
FixRISCVCallsPass.cpp
HFSort.cpp
HFSortPlus.cpp
Hugify.cpp

View File

@@ -0,0 +1,48 @@
#include "bolt/Passes/FixRISCVCallsPass.h"
#include "bolt/Core/ParallelUtilities.h"
#include <iterator>
using namespace llvm;
namespace llvm {
namespace bolt {
void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) {
auto &BC = BF.getBinaryContext();
for (auto &BB : BF) {
for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) {
auto NextII = std::next(II);
if (NextII == IE)
break;
if (!BC.MIB->isRISCVCall(*II, *NextII))
continue;
auto L = BC.scopeLock();
// The MC layer handles R_RISCV_CALL_PLT but assumes that the immediate
// in the JALR is zero (fixups are or'ed into instructions). Note that
// NextII is guaranteed to point to a JALR by isRISCVCall.
NextII->getOperand(2).setImm(0);
}
}
}
void FixRISCVCallsPass::runOnFunctions(BinaryContext &BC) {
if (!BC.isRISCV() || !BC.HasRelocations)
return;
ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
runOnFunction(BF);
};
ParallelUtilities::runOnEachFunction(
BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun, nullptr,
"FixRISCVCalls");
}
} // namespace bolt
} // namespace llvm

View File

@@ -12,6 +12,7 @@
#include "bolt/Passes/AllocCombiner.h"
#include "bolt/Passes/AsmDump.h"
#include "bolt/Passes/CMOVConversion.h"
#include "bolt/Passes/FixRISCVCallsPass.h"
#include "bolt/Passes/FixRelaxationPass.h"
#include "bolt/Passes/FrameOptimizer.h"
#include "bolt/Passes/Hugify.h"
@@ -185,6 +186,11 @@ static cl::opt<bool>
cl::desc("print functions after fix relaxations pass"),
cl::cat(BoltOptCategory));
static cl::opt<bool>
PrintFixRISCVCalls("print-fix-riscv-calls",
cl::desc("print functions after fix RISCV calls pass"),
cl::cat(BoltOptCategory));
static cl::opt<bool> PrintVeneerElimination(
"print-veneer-elimination",
cl::desc("print functions after veneer elimination pass"),
@@ -328,6 +334,11 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
std::make_unique<VeneerElimination>(PrintVeneerElimination));
}
if (BC.isRISCV()) {
Manager.registerPass(
std::make_unique<FixRISCVCallsPass>(PrintFixRISCVCalls));
}
// Here we manage dependencies/order manually, since passes are run in the
// order they're registered.

View File

@@ -300,6 +300,11 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
return createAArch64MCPlusBuilder(Analysis, Info, RegInfo);
#endif
#ifdef RISCV_AVAILABLE
if (Arch == Triple::riscv64)
return createRISCVMCPlusBuilder(Analysis, Info, RegInfo);
#endif
llvm_unreachable("architecture unsupported by MCPlusBuilder");
}
@@ -1042,7 +1047,9 @@ void RewriteInstance::discoverFileObjects() {
section_iterator Section =
cantFail(Symbol.getSection(), "cannot get symbol section");
if (Section == InputFile->section_end()) {
// Could be an absolute symbol. Could record for pretty printing.
// Could be an absolute symbol. Used on RISC-V for __global_pointer$ so we
// need to record it to handle relocations against it. For other instances
// of absolute symbols, we record for pretty printing.
LLVM_DEBUG(if (opts::Verbosity > 1) {
dbgs() << "BOLT-INFO: absolute sym " << UniqueName << "\n";
});
@@ -1464,6 +1471,50 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
}
}
void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
const uint64_t SectionAddress = Section.getAddress();
const uint64_t SectionSize = Section.getSize();
StringRef PLTContents = Section.getContents();
ArrayRef<uint8_t> PLTData(
reinterpret_cast<const uint8_t *>(PLTContents.data()), SectionSize);
auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction,
uint64_t &InstrSize) {
const uint64_t InstrAddr = SectionAddress + InstrOffset;
if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
PLTData.slice(InstrOffset), InstrAddr,
nulls())) {
errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
<< Section.getName() << " at offset 0x"
<< Twine::utohexstr(InstrOffset) << '\n';
exit(1);
}
};
// Skip the first special entry since no relocation points to it.
uint64_t InstrOffset = 32;
while (InstrOffset < SectionSize) {
InstructionListType Instructions;
MCInst Instruction;
const uint64_t EntryOffset = InstrOffset;
const uint64_t EntrySize = 16;
uint64_t InstrSize;
while (InstrOffset < EntryOffset + EntrySize) {
disassembleInstruction(InstrOffset, Instruction, InstrSize);
Instructions.emplace_back(Instruction);
InstrOffset += InstrSize;
}
const uint64_t EntryAddress = SectionAddress + EntryOffset;
const uint64_t TargetAddress = BC->MIB->analyzePLTEntry(
Instruction, Instructions.begin(), Instructions.end(), EntryAddress);
createPLTBinaryFunction(TargetAddress, EntryAddress, EntrySize);
}
}
void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section,
uint64_t EntrySize) {
const uint64_t SectionAddress = Section.getAddress();
@@ -1523,6 +1574,8 @@ void RewriteInstance::disassemblePLT() {
auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) {
if (BC->isAArch64())
return disassemblePLTSectionAArch64(Section);
if (BC->isRISCV())
return disassemblePLTSectionRISCV(Section);
return disassemblePLTSectionX86(Section, EntrySize);
};
@@ -1990,7 +2043,7 @@ bool RewriteInstance::analyzeRelocation(
// Section symbols are marked as ST_Debug.
IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug);
// Check for PLT entry registered with symbol name
if (!SymbolAddress && IsAArch64) {
if (!SymbolAddress && (IsAArch64 || BC->isRISCV())) {
const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName);
SymbolAddress = BD ? BD->getAddress() : 0;
}
@@ -2053,7 +2106,7 @@ bool RewriteInstance::analyzeRelocation(
if (SkipVerification)
return true;
if (IsAArch64)
if (IsAArch64 || BC->isRISCV())
return true;
if (SymbolName == "__hot_start" || SymbolName == "__hot_end")
@@ -2107,7 +2160,11 @@ void RewriteInstance::processDynamicRelocations() {
if (!DynamicRelSectionOrErr)
report_error("unable to find section corresponding to DT_RELA",
DynamicRelSectionOrErr.getError());
if (DynamicRelSectionOrErr->getSize() != DynamicRelocationsSize)
auto DynamicRelSectionSize = DynamicRelSectionOrErr->getSize();
// On RISC-V DT_RELASZ seems to include both .rela.dyn and .rela.plt
if (DynamicRelocationsSize == DynamicRelSectionSize + PLTRelocationsSize)
DynamicRelocationsSize = DynamicRelSectionSize;
if (DynamicRelSectionSize != DynamicRelocationsSize)
report_error("section size mismatch for DT_RELASZ",
errc::executable_format_error);
readDynamicRelocations(DynamicRelSectionOrErr->getSectionRef(),
@@ -2598,6 +2655,14 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
Expected<StringRef> SectionName = Section->getName();
if (SectionName && !SectionName->empty())
ReferencedSection = BC->getUniqueSectionByName(*SectionName);
} else if (ReferencedSymbol &&
(cantFail(Symbol.getFlags()) & SymbolRef::SF_Absolute)) {
// This might be a relocation for an ABS symbols like __global_pointer$ on
// RISC-V
ContainingBF->addRelocation(Rel.getOffset(), ReferencedSymbol,
Rel.getType(), 0,
cantFail(Symbol.getValue()));
return;
}
}
@@ -2607,7 +2672,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
const bool IsToCode = ReferencedSection && ReferencedSection->isText();
// Special handling of PC-relative relocations.
if (!IsAArch64 && Relocation::isPCRelative(RType)) {
if (!IsAArch64 && !BC->isRISCV() && Relocation::isPCRelative(RType)) {
if (!IsFromCode && IsToCode) {
// PC-relative relocations from data to code are tricky since the
// original information is typically lost after linking, even with
@@ -2640,7 +2705,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
}
bool ForceRelocation = BC->forceSymbolRelocations(SymbolName);
if (BC->isAArch64() && Relocation::isGOT(RType))
if ((BC->isAArch64() || BC->isRISCV()) && Relocation::isGOT(RType))
ForceRelocation = true;
if (!ReferencedSection && !ForceRelocation) {
@@ -2788,7 +2853,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
// These are mostly local data symbols but undefined symbols
// in relocation sections can get through here too, from .plt.
assert(
(IsAArch64 || IsSectionRelocation ||
(IsAArch64 || BC->isRISCV() || IsSectionRelocation ||
BC->getSectionNameForAddress(SymbolAddress)->startswith(".plt")) &&
"known symbols should not resolve to anonymous locals");

View File

@@ -0,0 +1,21 @@
set(LLVM_LINK_COMPONENTS
MC
Support
RISCVDesc
)
add_llvm_library(LLVMBOLTTargetRISCV
RISCVMCPlusBuilder.cpp
DISABLE_LLVM_LINK_LLVM_DYLIB
DEPENDS
RISCVCommonTableGen
)
target_link_libraries(LLVMBOLTTargetRISCV PRIVATE LLVMBOLTCore)
include_directories(
${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV
${LLVM_BINARY_DIR}/lib/Target/RISCV
)

View File

@@ -0,0 +1,328 @@
//===- bolt/Target/RISCV/RISCVMCPlusBuilder.cpp -----------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file provides RISCV-specific MCPlus builder.
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/RISCVMCExpr.h"
#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "mcplus"
using namespace llvm;
using namespace bolt;
namespace {
class RISCVMCPlusBuilder : public MCPlusBuilder {
public:
using MCPlusBuilder::MCPlusBuilder;
bool shouldRecordCodeRelocation(uint64_t RelType) const override {
switch (RelType) {
case ELF::R_RISCV_JAL:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
case ELF::R_RISCV_BRANCH:
case ELF::R_RISCV_RVC_BRANCH:
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_PCREL_HI20:
case ELF::R_RISCV_PCREL_LO12_I:
return true;
default:
llvm_unreachable("Unexpected RISCV relocation type in code");
}
}
bool isNop(const MCInst &Inst) const {
return Inst.getOpcode() == RISCV::ADDI &&
Inst.getOperand(0).getReg() == RISCV::X0 &&
Inst.getOperand(1).getReg() == RISCV::X0 &&
Inst.getOperand(2).getImm() == 0;
}
bool isCNop(const MCInst &Inst) const {
return Inst.getOpcode() == RISCV::C_NOP;
}
bool isNoop(const MCInst &Inst) const override {
return isNop(Inst) || isCNop(Inst);
}
bool hasPCRelOperand(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
default:
return false;
case RISCV::JAL:
case RISCV::AUIPC:
return true;
}
}
bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
"Invalid instruction");
unsigned SymOpIndex;
switch (Inst.getOpcode()) {
default:
llvm_unreachable("not implemented");
case RISCV::C_J:
SymOpIndex = 0;
break;
case RISCV::JAL:
case RISCV::C_BEQZ:
case RISCV::C_BNEZ:
SymOpIndex = 1;
break;
case RISCV::BEQ:
case RISCV::BGE:
SymOpIndex = 2;
break;
}
Inst.getOperand(SymOpIndex) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}
IndirectBranchType analyzeIndirectBranch(
MCInst &Instruction, InstructionIterator Begin, InstructionIterator End,
const unsigned PtrSize, MCInst *&MemLocInstr, unsigned &BaseRegNum,
unsigned &IndexRegNum, int64_t &DispValue, const MCExpr *&DispExpr,
MCInst *&PCRelBaseOut) const override {
MemLocInstr = nullptr;
BaseRegNum = 0;
IndexRegNum = 0;
DispValue = 0;
DispExpr = nullptr;
PCRelBaseOut = nullptr;
return IndirectBranchType::UNKNOWN;
}
bool convertJmpToTailCall(MCInst &Inst) override {
if (isTailCall(Inst))
return false;
switch (Inst.getOpcode()) {
default:
llvm_unreachable("unsupported tail call opcode");
case RISCV::JAL:
case RISCV::JALR:
case RISCV::C_J:
case RISCV::C_JR:
break;
}
setTailCall(Inst);
return true;
}
bool analyzeBranch(InstructionIterator Begin, InstructionIterator End,
const MCSymbol *&TBB, const MCSymbol *&FBB,
MCInst *&CondBranch,
MCInst *&UncondBranch) const override {
auto I = End;
while (I != Begin) {
--I;
// Ignore nops and CFIs
if (isPseudo(*I) || isNoop(*I))
continue;
// Stop when we find the first non-terminator
if (!isTerminator(*I) || isTailCall(*I) || !isBranch(*I))
break;
// Handle unconditional branches.
if (isUnconditionalBranch(*I)) {
// If any code was seen after this unconditional branch, we've seen
// unreachable code. Ignore them.
CondBranch = nullptr;
UncondBranch = &*I;
const MCSymbol *Sym = getTargetSymbol(*I);
assert(Sym != nullptr &&
"Couldn't extract BB symbol from jump operand");
TBB = Sym;
continue;
}
// Handle conditional branches and ignore indirect branches
if (isIndirectBranch(*I))
return false;
if (CondBranch == nullptr) {
const MCSymbol *TargetBB = getTargetSymbol(*I);
if (TargetBB == nullptr) {
// Unrecognized branch target
return false;
}
FBB = TBB;
TBB = TargetBB;
CondBranch = &*I;
continue;
}
llvm_unreachable("multiple conditional branches in one BB");
}
return true;
}
const MCSymbol *getTargetSymbol(const MCInst &Inst,
unsigned OpNum = 0) const override {
const MCOperand &Op = Inst.getOperand(OpNum);
if (!Op.isExpr())
return nullptr;
return MCPlusBuilder::getTargetSymbol(Op.getExpr());
}
bool lowerTailCall(MCInst &Inst) override {
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
if (getConditionalTailCall(Inst))
unsetConditionalTailCall(Inst);
return true;
}
uint64_t analyzePLTEntry(MCInst &Instruction, InstructionIterator Begin,
InstructionIterator End,
uint64_t BeginPC) const override {
auto I = Begin;
assert(I != End);
auto &AUIPC = *I++;
assert(AUIPC.getOpcode() == RISCV::AUIPC);
assert(AUIPC.getOperand(0).getReg() == RISCV::X28);
assert(I != End);
auto &LD = *I++;
assert(LD.getOpcode() == RISCV::LD);
assert(LD.getOperand(0).getReg() == RISCV::X28);
assert(LD.getOperand(1).getReg() == RISCV::X28);
assert(I != End);
auto &JALR = *I++;
(void)JALR;
assert(JALR.getOpcode() == RISCV::JALR);
assert(JALR.getOperand(0).getReg() == RISCV::X6);
assert(JALR.getOperand(1).getReg() == RISCV::X28);
assert(I != End);
auto &NOP = *I++;
(void)NOP;
assert(isNoop(NOP));
assert(I == End);
auto AUIPCOffset = AUIPC.getOperand(1).getImm() << 12;
auto LDOffset = LD.getOperand(2).getImm();
return BeginPC + AUIPCOffset + LDOffset;
}
bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol,
int64_t Addend, MCContext *Ctx, int64_t &Value,
uint64_t RelType) const override {
unsigned ImmOpNo = -1U;
for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst);
++Index) {
if (Inst.getOperand(Index).isImm()) {
ImmOpNo = Index;
break;
}
}
if (ImmOpNo == -1U)
return false;
Value = Inst.getOperand(ImmOpNo).getImm();
setOperandToSymbolRef(Inst, ImmOpNo, Symbol, Addend, Ctx, RelType);
return true;
}
const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr,
MCContext &Ctx,
uint64_t RelType) const override {
switch (RelType) {
default:
return Expr;
case ELF::R_RISCV_GOT_HI20:
// The GOT is reused so no need to create GOT relocations
case ELF::R_RISCV_PCREL_HI20:
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx);
case ELF::R_RISCV_PCREL_LO12_I:
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx);
case ELF::R_RISCV_CALL:
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx);
case ELF::R_RISCV_CALL_PLT:
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL_PLT, Ctx);
}
}
bool evaluateMemOperandTarget(const MCInst &Inst, uint64_t &Target,
uint64_t Address,
uint64_t Size) const override {
return false;
}
bool isCallAuipc(const MCInst &Inst) const {
if (Inst.getOpcode() != RISCV::AUIPC)
return false;
const auto &ImmOp = Inst.getOperand(1);
if (!ImmOp.isExpr())
return false;
const auto *ImmExpr = ImmOp.getExpr();
if (!isa<RISCVMCExpr>(ImmExpr))
return false;
switch (cast<RISCVMCExpr>(ImmExpr)->getKind()) {
default:
return false;
case RISCVMCExpr::VK_RISCV_CALL:
case RISCVMCExpr::VK_RISCV_CALL_PLT:
return true;
}
}
bool isRISCVCall(const MCInst &First, const MCInst &Second) const override {
if (!isCallAuipc(First))
return false;
assert(Second.getOpcode() == RISCV::JALR);
return true;
}
};
} // end anonymous namespace
namespace llvm {
namespace bolt {
MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *Analysis,
const MCInstrInfo *Info,
const MCRegisterInfo *RegInfo) {
return new RISCVMCPlusBuilder(Analysis, Info, RegInfo);
}
} // namespace bolt
} // namespace llvm

View File

@@ -0,0 +1,830 @@
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_RISCV
Flags: [ EF_RISCV_RVC, EF_RISCV_FLOAT_ABI_DOUBLE ]
Entry: 0x5B0
ProgramHeaders:
- Type: PT_PHDR
Flags: [ PF_R ]
VAddr: 0x40
Align: 0x8
- Type: PT_INTERP
Flags: [ PF_R ]
FirstSec: .interp
LastSec: .interp
VAddr: 0x270
- Type: 0x70000003
Flags: [ PF_R ]
FirstSec: .riscv.attributes
LastSec: .riscv.attributes
- Type: PT_LOAD
Flags: [ PF_X, PF_R ]
FirstSec: .interp
LastSec: .eh_frame
Align: 0x1000
Offset: 0x0
- Type: PT_LOAD
Flags: [ PF_W, PF_R ]
FirstSec: .preinit_array
LastSec: .bss
VAddr: 0x1E08
Align: 0x1000
- Type: PT_DYNAMIC
Flags: [ PF_W, PF_R ]
FirstSec: .dynamic
LastSec: .dynamic
VAddr: 0x1E20
Align: 0x8
- Type: PT_GNU_EH_FRAME
Flags: [ PF_R ]
FirstSec: .eh_frame_hdr
LastSec: .eh_frame_hdr
VAddr: 0x6AC
Align: 0x4
- Type: PT_GNU_STACK
Flags: [ PF_W, PF_R ]
Align: 0x10
- Type: PT_GNU_RELRO
Flags: [ PF_R ]
FirstSec: .preinit_array
LastSec: .dynamic
VAddr: 0x1E08
Sections:
- Name: .interp
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x270
AddressAlign: 0x1
Content: 2F6C69622F6C642D6C696E75782D726973637636342D6C703634642E736F2E3100
- Name: .dynsym
Type: SHT_DYNSYM
Flags: [ SHF_ALLOC ]
Address: 0x300
Link: .dynstr
AddressAlign: 0x8
- Name: .dynstr
Type: SHT_STRTAB
Flags: [ SHF_ALLOC ]
Address: 0x3C0
AddressAlign: 0x1
- Name: .rela.dyn
Type: SHT_RELA
Flags: [ SHF_ALLOC ]
Address: 0x480
Link: .dynsym
AddressAlign: 0x8
Relocations:
- Offset: 0x1E08
Type: R_RISCV_RELATIVE
Addend: 1498
- Offset: 0x1E10
Type: R_RISCV_RELATIVE
Addend: 1650
- Offset: 0x1E18
Type: R_RISCV_RELATIVE
Addend: 1588
- Offset: 0x2000
Type: R_RISCV_RELATIVE
Addend: 8192
- Offset: 0x2038
Type: R_RISCV_RELATIVE
Addend: 1658
- Offset: 0x2030
Symbol: _ITM_deregisterTMCloneTable
Type: R_RISCV_64
- Offset: 0x2040
Symbol: __cxa_finalize
Type: R_RISCV_64
- Offset: 0x2048
Symbol: _ITM_registerTMCloneTable
Type: R_RISCV_64
- Name: .rela.plt
Type: SHT_RELA
Flags: [ SHF_ALLOC, SHF_INFO_LINK ]
Address: 0x540
Link: .dynsym
AddressAlign: 0x8
Info: .got
Relocations:
- Offset: 0x2018
Symbol: __libc_start_main
Type: R_RISCV_JUMP_SLOT
- Offset: 0x2020
Symbol: puts
Type: R_RISCV_JUMP_SLOT
- Name: .plt
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x570
AddressAlign: 0x10
EntSize: 0x10
Content: 972300003303C34103BE83A9130343FD938283A91353130083B2820067000E00172E0000033E8EA867030E0013000000172E0000033E0EA867030E0013000000
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x5B0
AddressAlign: 0x4
Content: 97000000E780A002AA87172500000335E5A782653000137101FF814601470A8897000000E78000FC029097210000938161228280000017250000130525A2972700009387A7A16388A7009727000083B767A391C38287828017250000130505A0972500009385859F898D93D73540FD91BE95858599C59727000083B727A291C382878280411122E017240000130484A18347040006E495E39727000083B7879F91C7172500000335E59A829797000000E780A0F885472300F400A26002644101828017030000670063F9411106E422E00008170500001305650297000000E78060F181473E85A260026441018280
- Name: .rodata
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x6A0
AddressAlign: 0x8
Content: '0100020000000000686900'
- Name: .eh_frame_hdr
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x6AC
AddressAlign: 0x4
Content: 011B033B100000000100000004FFFFFF28000000
- Name: .eh_frame
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x6C0
AddressAlign: 0x8
Content: 1000000000000000037A5200017C01011B0D02001000000018000000D4FEFFFF2A0000000007010000000000
- Name: .preinit_array
Type: SHT_PREINIT_ARRAY
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x1E08
AddressAlign: 0x1
EntSize: 0x8
Offset: 0xE08
Content: '0000000000000000'
- Name: .init_array
Type: SHT_INIT_ARRAY
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x1E10
AddressAlign: 0x8
EntSize: 0x8
Content: '0000000000000000'
- Name: .fini_array
Type: SHT_FINI_ARRAY
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x1E18
AddressAlign: 0x8
EntSize: 0x8
Content: '0000000000000000'
- Name: .dynamic
Type: SHT_DYNAMIC
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x1E20
Link: .dynstr
AddressAlign: 0x8
Entries:
- Tag: DT_NEEDED
Value: 0x27
- Tag: DT_PREINIT_ARRAY
Value: 0x1E08
- Tag: DT_PREINIT_ARRAYSZ
Value: 0x8
- Tag: DT_INIT_ARRAY
Value: 0x1E10
- Tag: DT_INIT_ARRAYSZ
Value: 0x8
- Tag: DT_FINI_ARRAY
Value: 0x1E18
- Tag: DT_FINI_ARRAYSZ
Value: 0x8
- Tag: DT_GNU_HASH
Value: 0x2D8
- Tag: DT_STRTAB
Value: 0x3C0
- Tag: DT_SYMTAB
Value: 0x300
- Tag: DT_STRSZ
Value: 0x7D
- Tag: DT_SYMENT
Value: 0x18
- Tag: DT_DEBUG
Value: 0x0
- Tag: DT_PLTGOT
Value: 0x2008
- Tag: DT_PLTRELSZ
Value: 0x30
- Tag: DT_PLTREL
Value: 0x7
- Tag: DT_JMPREL
Value: 0x540
- Tag: DT_RELA
Value: 0x480
- Tag: DT_RELASZ
Value: 0xF0
- Tag: DT_RELAENT
Value: 0x18
- Tag: DT_FLAGS_1
Value: 0x8000000
- Tag: DT_VERNEED
Value: 0x450
- Tag: DT_VERNEEDNUM
Value: 0x1
- Tag: DT_VERSYM
Value: 0x43E
- Tag: DT_RELACOUNT
Value: 0x5
- Tag: DT_NULL
Value: 0x0
- Tag: DT_NULL
Value: 0x0
- Tag: DT_NULL
Value: 0x0
- Tag: DT_NULL
Value: 0x0
- Tag: DT_NULL
Value: 0x0
- Name: .data
Type: SHT_PROGBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x2000
AddressAlign: 0x8
Content: '0000000000000000'
- Name: .tm_clone_table
Type: SHT_PROGBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x2008
AddressAlign: 0x8
- Name: .got
Type: SHT_PROGBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x2008
AddressAlign: 0x8
EntSize: 0x8
Content: FFFFFFFFFFFFFFFF000000000000000070050000000000007005000000000000201E0000000000000000000000000000000000000000000000000000000000000000000000000000
- Name: .bss
Type: SHT_NOBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
Address: 0x2050
AddressAlign: 0x1
Size: 0x8
- Name: .riscv.attributes
Type: SHT_RISCV_ATTRIBUTES
AddressAlign: 0x1
Content: 4149000000726973637600013F00000004100572763634693270315F6D3270305F613270315F663270325F643270325F633270305F7A696373723270305F7A6966656E63656932703000
- Name: .rela.text
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .text
Relocations:
- Offset: 0x5B0
Type: R_RISCV_NONE
Addend: 2
- Offset: 0x5B0
Symbol: load_gp
Type: R_RISCV_CALL
- Offset: 0x5B0
Type: R_RISCV_RELAX
- Offset: 0x5BA
Symbol: main
Type: R_RISCV_GOT_HI20
- Offset: 0x5BE
Symbol: '.L0 '
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x5BE
Type: R_RISCV_RELAX
- Offset: 0x5D0
Symbol: '__libc_start_main@GLIBC_2.34'
Type: R_RISCV_CALL_PLT
- Offset: 0x5D0
Type: R_RISCV_RELAX
- Offset: 0x5DA
Symbol: '__global_pointer$'
Type: R_RISCV_PCREL_HI20
- Offset: 0x5DE
Symbol: '.L0 (1)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x5E6
Symbol: __TMC_LIST__
Type: R_RISCV_PCREL_HI20
- Offset: 0x5E6
Type: R_RISCV_RELAX
- Offset: 0x5EA
Symbol: '.L0 (4)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x5EA
Type: R_RISCV_RELAX
- Offset: 0x5EE
Symbol: __TMC_END__
Type: R_RISCV_PCREL_HI20
- Offset: 0x5EE
Type: R_RISCV_RELAX
- Offset: 0x5F2
Symbol: '.L0 (5)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x5F2
Type: R_RISCV_RELAX
- Offset: 0x5F6
Symbol: .L1
Type: R_RISCV_BRANCH
- Offset: 0x5FA
Symbol: _ITM_deregisterTMCloneTable
Type: R_RISCV_GOT_HI20
- Offset: 0x5FE
Symbol: '.L0 (6)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x5FE
Type: R_RISCV_RELAX
- Offset: 0x602
Symbol: .L1
Type: R_RISCV_RVC_BRANCH
- Offset: 0x608
Symbol: __TMC_LIST__
Type: R_RISCV_PCREL_HI20
- Offset: 0x608
Type: R_RISCV_RELAX
- Offset: 0x60C
Symbol: '.L0 (7)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x60C
Type: R_RISCV_RELAX
- Offset: 0x610
Symbol: __TMC_END__
Type: R_RISCV_PCREL_HI20
- Offset: 0x610
Type: R_RISCV_RELAX
- Offset: 0x614
Symbol: '.L0 (8)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x614
Type: R_RISCV_RELAX
- Offset: 0x624
Symbol: .L7
Type: R_RISCV_RVC_BRANCH
- Offset: 0x626
Symbol: _ITM_registerTMCloneTable
Type: R_RISCV_GOT_HI20
- Offset: 0x62A
Symbol: '.L0 (9)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x62A
Type: R_RISCV_RELAX
- Offset: 0x62E
Symbol: .L7
Type: R_RISCV_RVC_BRANCH
- Offset: 0x638
Symbol: completed.0
Type: R_RISCV_PCREL_HI20
- Offset: 0x638
Type: R_RISCV_RELAX
- Offset: 0x63C
Symbol: '.L0 (10)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x63C
Type: R_RISCV_RELAX
- Offset: 0x646
Symbol: .L15
Type: R_RISCV_RVC_BRANCH
- Offset: 0x648
Symbol: '__cxa_finalize@GLIBC_2.27'
Type: R_RISCV_GOT_HI20
- Offset: 0x64C
Symbol: '.L0 (11)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x64C
Type: R_RISCV_RELAX
- Offset: 0x650
Symbol: .L17
Type: R_RISCV_RVC_BRANCH
- Offset: 0x652
Symbol: __dso_handle
Type: R_RISCV_PCREL_HI20
- Offset: 0x652
Type: R_RISCV_RELAX
- Offset: 0x656
Symbol: '.L0 (12)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x656
Type: R_RISCV_RELAX
- Offset: 0x65C
Symbol: deregister_tm_clones
Type: R_RISCV_CALL
- Offset: 0x65C
Type: R_RISCV_RELAX
- Offset: 0x672
Symbol: register_tm_clones
Type: R_RISCV_CALL
- Offset: 0x672
Type: R_RISCV_RELAX
- Offset: 0x682
Symbol: .LC0
Type: R_RISCV_PCREL_HI20
- Offset: 0x682
Type: R_RISCV_RELAX
- Offset: 0x686
Symbol: '.L0 (13)'
Type: R_RISCV_PCREL_LO12_I
- Offset: 0x686
Type: R_RISCV_RELAX
- Offset: 0x68A
Symbol: 'puts@GLIBC_2.27'
Type: R_RISCV_CALL_PLT
- Offset: 0x68A
Type: R_RISCV_RELAX
- Name: .rela.eh_frame
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .eh_frame
Relocations:
- Offset: 0x6DC
Symbol: '.L0 (2)'
Type: R_RISCV_32_PCREL
- Offset: 0x6E0
Symbol: '.L0 (3)'
Type: R_RISCV_ADD32
- Offset: 0x6E0
Symbol: '.L0 (2)'
Type: R_RISCV_SUB32
- Name: .rela.preinit_array
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .preinit_array
Relocations:
- Offset: 0x1E08
Symbol: load_gp
Type: R_RISCV_64
- Name: .rela.init_array
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .init_array
Relocations:
- Offset: 0x1E10
Symbol: frame_dummy
Type: R_RISCV_64
- Name: .rela.fini_array
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .fini_array
Relocations:
- Offset: 0x1E18
Symbol: __do_global_dtors_aux
Type: R_RISCV_64
- Name: .rela.data
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .data
Relocations:
- Offset: 0x2000
Symbol: __dso_handle
Type: R_RISCV_64
- Type: SectionHeaderTable
Sections:
- Name: .interp
- Name: .dynsym
- Name: .dynstr
- Name: .rela.dyn
- Name: .rela.plt
- Name: .plt
- Name: .text
- Name: .rela.text
- Name: .rodata
- Name: .eh_frame_hdr
- Name: .eh_frame
- Name: .rela.eh_frame
- Name: .preinit_array
- Name: .rela.preinit_array
- Name: .init_array
- Name: .rela.init_array
- Name: .fini_array
- Name: .rela.fini_array
- Name: .dynamic
- Name: .data
- Name: .rela.data
- Name: .tm_clone_table
- Name: .got
- Name: .bss
- Name: .riscv.attributes
- Name: .symtab
- Name: .strtab
- Name: .shstrtab
Symbols:
- Name: .interp
Type: STT_SECTION
Section: .interp
Value: 0x270
- Name: .dynsym
Type: STT_SECTION
Section: .dynsym
Value: 0x300
- Name: .dynstr
Type: STT_SECTION
Section: .dynstr
Value: 0x3C0
- Name: .rela.dyn
Type: STT_SECTION
Section: .rela.dyn
Value: 0x480
- Name: .rela.plt
Type: STT_SECTION
Section: .rela.plt
Value: 0x540
- Name: .plt
Type: STT_SECTION
Section: .plt
Value: 0x570
- Name: .text
Type: STT_SECTION
Section: .text
Value: 0x5B0
- Name: .rodata
Type: STT_SECTION
Section: .rodata
Value: 0x6A0
- Name: .eh_frame_hdr
Type: STT_SECTION
Section: .eh_frame_hdr
Value: 0x6AC
- Name: .eh_frame
Type: STT_SECTION
Section: .eh_frame
Value: 0x6C0
- Name: .preinit_array
Type: STT_SECTION
Section: .preinit_array
Value: 0x1E08
- Name: .init_array
Type: STT_SECTION
Section: .init_array
Value: 0x1E10
- Name: .fini_array
Type: STT_SECTION
Section: .fini_array
Value: 0x1E18
- Name: .dynamic
Type: STT_SECTION
Section: .dynamic
Value: 0x1E20
- Name: .data
Type: STT_SECTION
Section: .data
Value: 0x2000
- Name: .tm_clone_table
Type: STT_SECTION
Section: .tm_clone_table
Value: 0x2008
- Name: .got
Type: STT_SECTION
Section: .got
Value: 0x2008
- Name: .bss
Type: STT_SECTION
Section: .bss
Value: 0x2050
- Name: .riscv.attributes
Type: STT_SECTION
Section: .riscv.attributes
- Name: start.os
Type: STT_FILE
Index: SHN_ABS
- Name: '$x'
Section: .text
Value: 0x5B0
- Name: load_gp
Section: .text
Value: 0x5DA
- Name: init.c
Type: STT_FILE
Index: SHN_ABS
- Name: '.L0 '
Section: .text
Value: 0x5BA
- Name: '.L0 (1)'
Section: .text
Value: 0x5DA
- Name: '.L0 (2)'
Section: .text
Value: 0x5B0
- Name: '.L0 (3)'
Section: .text
Value: 0x5DA
- Name: crtstuff.c
Type: STT_FILE
Index: SHN_ABS
- Name: __TMC_LIST__
Type: STT_OBJECT
Section: .tm_clone_table
Value: 0x2008
- Name: deregister_tm_clones
Type: STT_FUNC
Section: .text
Value: 0x5E6
- Name: '$x (1)'
Section: .text
Value: 0x5E6
- Name: register_tm_clones
Type: STT_FUNC
Section: .text
Value: 0x608
- Name: __do_global_dtors_aux
Type: STT_FUNC
Section: .text
Value: 0x634
- Name: completed.0
Type: STT_OBJECT
Section: .bss
Value: 0x2050
Size: 0x1
- Name: __do_global_dtors_aux_fini_array_entry
Type: STT_OBJECT
Section: .fini_array
Value: 0x1E18
- Name: frame_dummy
Type: STT_FUNC
Section: .text
Value: 0x672
- Name: __frame_dummy_init_array_entry
Type: STT_OBJECT
Section: .init_array
Value: 0x1E10
- Name: '.L0 (4)'
Section: .text
Value: 0x5E6
- Name: '.L0 (5)'
Section: .text
Value: 0x5EE
- Name: .L1
Section: .text
Value: 0x606
- Name: '.L0 (6)'
Section: .text
Value: 0x5FA
- Name: '.L0 (7)'
Section: .text
Value: 0x608
- Name: '.L0 (8)'
Section: .text
Value: 0x610
- Name: .L7
Section: .text
Value: 0x632
- Name: '.L0 (9)'
Section: .text
Value: 0x626
- Name: '.L0 (10)'
Section: .text
Value: 0x638
- Name: .L15
Section: .text
Value: 0x66A
- Name: '.L0 (11)'
Section: .text
Value: 0x648
- Name: .L17
Section: .text
Value: 0x65C
- Name: '.L0 (12)'
Section: .text
Value: 0x652
- Name: test.c
Type: STT_FILE
Index: SHN_ABS
- Name: '$x (2)'
Section: .text
Value: 0x67A
- Name: .LC0
Section: .rodata
Value: 0x6A8
- Name: '.L0 (13)'
Section: .text
Value: 0x682
- Name: 'crtstuff.c (1)'
Type: STT_FILE
Index: SHN_ABS
- Name: __FRAME_END__
Type: STT_OBJECT
Section: .eh_frame
Value: 0x6E8
- Type: STT_FILE
Index: SHN_ABS
- Name: _PROCEDURE_LINKAGE_TABLE_
Type: STT_OBJECT
Index: SHN_ABS
Value: 0x570
- Name: _DYNAMIC
Type: STT_OBJECT
Index: SHN_ABS
Value: 0x1E20
- Name: __GNU_EH_FRAME_HDR
Section: .eh_frame_hdr
Value: 0x6AC
- Name: _GLOBAL_OFFSET_TABLE_
Type: STT_OBJECT
Index: SHN_ABS
Value: 0x2028
- Name: '__libc_start_main@GLIBC_2.34'
Type: STT_FUNC
Binding: STB_GLOBAL
- Name: _ITM_deregisterTMCloneTable
Binding: STB_WEAK
- Name: data_start
Section: .data
Binding: STB_WEAK
Value: 0x2000
- Name: __BSS_END__
Section: .bss
Binding: STB_GLOBAL
Value: 0x2058
- Name: _edata
Section: .got
Binding: STB_GLOBAL
Value: 0x2050
- Name: __SDATA_BEGIN__
Section: .got
Binding: STB_GLOBAL
Value: 0x2050
- Name: __DATA_BEGIN__
Section: .data
Binding: STB_GLOBAL
Value: 0x2000
- Name: __data_start
Section: .data
Binding: STB_GLOBAL
Value: 0x2000
- Name: __dso_handle
Type: STT_OBJECT
Section: .data
Binding: STB_GLOBAL
Value: 0x2000
Other: [ STV_HIDDEN ]
- Name: _IO_stdin_used
Type: STT_OBJECT
Section: .rodata
Binding: STB_GLOBAL
Value: 0x6A0
Size: 0x4
- Name: _end
Section: .bss
Binding: STB_GLOBAL
Value: 0x2058
- Name: _start
Type: STT_FUNC
Section: .text
Binding: STB_GLOBAL
Value: 0x5B0
Size: 0x2A
- Name: '__global_pointer$'
Index: SHN_ABS
Binding: STB_GLOBAL
Value: 0x2800
- Name: 'puts@GLIBC_2.27'
Type: STT_FUNC
Binding: STB_GLOBAL
- Name: __bss_start
Section: .bss
Binding: STB_GLOBAL
Value: 0x2050
- Name: main
Type: STT_FUNC
Section: .text
Binding: STB_GLOBAL
Value: 0x67A
Size: 0x24
- Name: '__cxa_finalize@GLIBC_2.27'
Type: STT_FUNC
Binding: STB_WEAK
- Name: __TMC_END__
Type: STT_OBJECT
Section: .tm_clone_table
Binding: STB_GLOBAL
Value: 0x2008
Other: [ STV_HIDDEN ]
- Name: _ITM_registerTMCloneTable
Binding: STB_WEAK
DynamicSymbols:
- Name: .text
Type: STT_SECTION
Section: .text
Value: 0x5B0
- Name: __libc_start_main
Type: STT_FUNC
Binding: STB_GLOBAL
- Name: _ITM_deregisterTMCloneTable
Binding: STB_WEAK
- Name: puts
Type: STT_FUNC
Binding: STB_GLOBAL
- Name: __cxa_finalize
Type: STT_FUNC
Binding: STB_WEAK
- Name: _ITM_registerTMCloneTable
Binding: STB_WEAK
- Name: main
Type: STT_FUNC
Section: .text
Binding: STB_GLOBAL
Value: 0x67A
Size: 0x24
...

View File

@@ -0,0 +1,7 @@
if 'RISCV' not in config.root.targets:
config.unsupported = True
flags = '--target=riscv64 -nostdlib -ffreestanding -Wl,--no-relax,--emit-relocs'
config.substitutions.insert(0, ('%cflags', f'%cflags {flags}'))
config.substitutions.insert(0, ('%cxxflags', f'%cxxflags {flags}'))

View File

@@ -0,0 +1,9 @@
// This test checks that the PLT symbols are properly recognized
// by the BOLT tool.
// RUN: yaml2obj %p/Inputs/plt-gnu-ld.yaml &> %t.exe
// RUN: llvm-bolt %t.exe -o %t.bolt.exe --print-cfg --print-only=main \
// RUN: | FileCheck %s
// CHECK: Binary Function "main" after building cfg {
// CHECK: auipc ra, puts@PLT

View File

@@ -0,0 +1,26 @@
// RUN: %clang %cflags -Wl,--defsym='__global_pointer$'=0x2800 -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.data
.globl d
.p2align 3
d:
.dword 0
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
nop
.option push
.option norelax
1:
// CHECK: .Ltmp0
// CHECK: auipc gp, %pcrel_hi(__global_pointer$)
// CHECK-NEXT: addi gp, gp, %pcrel_lo(.Ltmp0)
auipc gp, %pcrel_hi(__global_pointer$)
addi gp, gp, %pcrel_lo(1b)
.option pop
.size _start, .-_start

View File

@@ -0,0 +1,16 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
// CHECK: beq zero, zero, .Ltmp0
beq zero, zero, 1f
nop
// CHECK: .Ltmp0
1:
ret
.size _start, .-_start

View File

@@ -0,0 +1,21 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.text
.global f
.p2align 1
f:
ret
.size f, .-f
// CHECK: Binary Function "_start" after fix-riscv-calls {
.globl _start
.p2align 1
_start:
// CHECK: auipc ra, f
// CHECK-NEXT: jalr ra
call f
ret
.size _start, .-_start

View File

@@ -0,0 +1,24 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.data
.globl d
.p2align 3
d:
.dword 0
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
nop // Here to not make the _start and .Ltmp0 symbols coincide
// CHECK: .Ltmp0
// CHECK: auipc t0, %pcrel_hi(__BOLT_got_zero+{{[0-9]+}})
// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
1:
auipc t0, %got_pcrel_hi(d)
ld t0, %pcrel_lo(1b)(t0)
ret
.size _start, .-_start

View File

@@ -0,0 +1,20 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.text
.global f
.p2align 1
f:
ret
.size f, .-f
// CHECK: Binary Function "_start" after building cfg {
.globl _start
.p2align 1
_start:
// CHECK: jal ra, f
jal ra, f
ret
.size _start, .-_start

View File

@@ -0,0 +1,22 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.data
.globl d
.p2align 3
d:
.dword 0
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
nop // Here to not make the _start and .Ltmp0 symbols coincide
// CHECK: .Ltmp0
// CHECK: auipc t0, %pcrel_hi(d)
// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
ld t0, d
ret
.size _start, .-_start

View File

@@ -0,0 +1,16 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
// CHECK: beqz a0, .Ltmp0
c.beqz a0, 1f
nop
// CHECK: .Ltmp0
1:
ret
.size _start, .-_start

View File

@@ -0,0 +1,16 @@
// RUN: %clang %cflags -o %t %s
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
// RUN: | FileCheck %s
.text
.globl _start
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
// CHECK: j .Ltmp0
c.j 1f
nop
// CHECK: .Ltmp0
1:
ret
.size _start, .-_start