Index: bolt/CODE_OWNERS.TXT =================================================================== --- bolt/CODE_OWNERS.TXT +++ bolt/CODE_OWNERS.TXT @@ -20,3 +20,7 @@ N: Vladislav Khmelevsky E: och95@yandex.ru D: AArch64 backend + +N: Job Noorman +E: jnoorman@igalia.com +D: RISC-V backend Index: bolt/include/bolt/Core/BinaryContext.h =================================================================== --- bolt/include/bolt/Core/BinaryContext.h +++ bolt/include/bolt/Core/BinaryContext.h @@ -727,6 +727,8 @@ 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; Index: bolt/include/bolt/Core/MCPlusBuilder.h =================================================================== --- bolt/include/bolt/Core/MCPlusBuilder.h +++ bolt/include/bolt/Core/MCPlusBuilder.h @@ -602,6 +602,12 @@ 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; } @@ -2001,6 +2007,10 @@ const MCInstrInfo *, const MCRegisterInfo *); +MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *, + const MCInstrInfo *, + const MCRegisterInfo *); + } // namespace bolt } // namespace llvm Index: bolt/include/bolt/Passes/FixRISCVCallsPass.h =================================================================== --- /dev/null +++ bolt/include/bolt/Passes/FixRISCVCallsPass.h @@ -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 &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 Index: bolt/include/bolt/Rewrite/RewriteInstance.h =================================================================== --- bolt/include/bolt/Rewrite/RewriteInstance.h +++ bolt/include/bolt/Rewrite/RewriteInstance.h @@ -277,6 +277,9 @@ /// 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 TYPE FUNC(object::ELFObjectFile *Obj); \ @@ -526,6 +529,9 @@ 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) { @@ -539,6 +545,9 @@ case Triple::aarch64: PLTSI = AArch64_PLTSections; break; + case Triple::riscv64: + PLTSI = RISCV_PLTSections; + break; } for (; PLTSI && PLTSI->Name; ++PLTSI) if (SectionName == PLTSI->Name) Index: bolt/lib/Core/BinaryContext.cpp =================================================================== --- bolt/lib/Core/BinaryContext.cpp +++ bolt/lib/Core/BinaryContext.cpp @@ -128,6 +128,11 @@ 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"); Index: bolt/lib/Core/BinaryFunction.cpp =================================================================== --- bolt/lib/Core/BinaryFunction.cpp +++ bolt/lib/Core/BinaryFunction.cpp @@ -1321,7 +1321,7 @@ 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), @@ -1341,7 +1341,7 @@ UsedReloc = true; } - if (MIB->hasPCRelOperand(Instruction) && !UsedReloc) + if (!BC.isRISCV() && MIB->hasPCRelOperand(Instruction) && !UsedReloc) handlePCRelOperand(Instruction, AbsoluteInstrAddr, Size); } Index: bolt/lib/Core/Relocation.cpp =================================================================== --- bolt/lib/Core/Relocation.cpp +++ bolt/lib/Core/Relocation.cpp @@ -89,6 +89,24 @@ } } +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 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 @@ 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 @@ return false; } +static bool skipRelocationProcessRISCV(uint64_t &Type, uint64_t Contents) { + return false; +} + static uint64_t adjustValueX86(uint64_t Type, uint64_t Value, uint64_t PC) { switch (Type) { default: @@ -406,6 +460,57 @@ } } +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: @@ -443,6 +548,15 @@ } } +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: @@ -471,6 +585,13 @@ } } +static bool isTLSRISCV(uint64_t Type) { + switch (Type) { + default: + return false; + } +} + static bool isPCRelativeX86(uint64_t Type) { switch (Type) { default: @@ -542,27 +663,53 @@ } } +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); } @@ -570,6 +717,8 @@ uint64_t PC) { if (Arch == Triple::aarch64) return adjustValueAArch64(Type, Value, PC); + if (Arch == Triple::riscv64) + llvm_unreachable("not implemented"); return adjustValueX86(Type, Value, PC); } @@ -577,12 +726,16 @@ 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::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); } @@ -690,7 +857,20 @@ }; 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) { Index: bolt/lib/Passes/CMakeLists.txt =================================================================== --- bolt/lib/Passes/CMakeLists.txt +++ bolt/lib/Passes/CMakeLists.txt @@ -14,6 +14,7 @@ FrameAnalysis.cpp FrameOptimizer.cpp FixRelaxationPass.cpp + FixRISCVCallsPass.cpp HFSort.cpp HFSortPlus.cpp Hugify.cpp Index: bolt/lib/Passes/FixRISCVCallsPass.cpp =================================================================== --- /dev/null +++ bolt/lib/Passes/FixRISCVCallsPass.cpp @@ -0,0 +1,48 @@ +#include "bolt/Passes/FixRISCVCallsPass.h" +#include "bolt/Core/ParallelUtilities.h" + +#include + +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 Index: bolt/lib/Rewrite/BinaryPassManager.cpp =================================================================== --- bolt/lib/Rewrite/BinaryPassManager.cpp +++ bolt/lib/Rewrite/BinaryPassManager.cpp @@ -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 @@ cl::desc("print functions after fix relaxations pass"), cl::cat(BoltOptCategory)); +static cl::opt + PrintFixRISCVCalls("print-fix-riscv-calls", + cl::desc("print functions after fix RISCV calls pass"), + cl::cat(BoltOptCategory)); + static cl::opt PrintVeneerElimination( "print-veneer-elimination", cl::desc("print functions after veneer elimination pass"), @@ -328,6 +334,11 @@ std::make_unique(PrintVeneerElimination)); } + if (BC.isRISCV()) { + Manager.registerPass( + std::make_unique(PrintFixRISCVCalls)); + } + // Here we manage dependencies/order manually, since passes are run in the // order they're registered. Index: bolt/lib/Rewrite/CMakeLists.txt =================================================================== --- bolt/lib/Rewrite/CMakeLists.txt +++ bolt/lib/Rewrite/CMakeLists.txt @@ -26,6 +26,11 @@ set(BOLT_X64 On) endif() +if ("RISCV" IN_LIST LLVM_TARGETS_TO_BUILD) + list(APPEND TARGET_LINK_LIBRARIES LLVMBOLTTargetRISCV) + set(BOLT_RISCV On) +endif() + add_llvm_library(LLVMBOLTRewrite BinaryPassManager.cpp BoltDiff.cpp @@ -52,3 +57,7 @@ if (DEFINED BOLT_X64) target_compile_definitions(LLVMBOLTRewrite PRIVATE X86_AVAILABLE) endif() + +if (DEFINED BOLT_RISCV) + target_compile_definitions(LLVMBOLTRewrite PRIVATE RISCV_AVAILABLE) +endif() Index: bolt/lib/Rewrite/RewriteInstance.cpp =================================================================== --- bolt/lib/Rewrite/RewriteInstance.cpp +++ bolt/lib/Rewrite/RewriteInstance.cpp @@ -299,6 +299,11 @@ 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"); } @@ -1451,6 +1456,50 @@ } } +void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) { + const uint64_t SectionAddress = Section.getAddress(); + const uint64_t SectionSize = Section.getSize(); + StringRef PLTContents = Section.getContents(); + ArrayRef PLTData( + reinterpret_cast(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(); @@ -1510,6 +1559,8 @@ auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) { if (BC->isAArch64()) return disassemblePLTSectionAArch64(Section); + if (BC->isRISCV()) + return disassemblePLTSectionRISCV(Section); return disassemblePLTSectionX86(Section, EntrySize); }; @@ -1991,7 +2042,7 @@ // 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; } @@ -2054,7 +2105,7 @@ if (SkipVerification) return true; - if (IsAArch64) + if (IsAArch64 || BC->isRISCV()) return true; if (SymbolName == "__hot_start" || SymbolName == "__hot_end") @@ -2095,7 +2146,11 @@ 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(), @@ -2541,7 +2596,7 @@ 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 @@ -2574,10 +2629,22 @@ } bool ForceRelocation = BC->forceSymbolRelocations(SymbolName); - if (BC->isAArch64() && Relocation::isGOT(RType)) + if ((BC->isAArch64() || BC->isRISCV()) && Relocation::isGOT(RType)) ForceRelocation = true; if (!ReferencedSection && !ForceRelocation) { + // Check for ABS symbols. Used e.g., for __global_pointer$ on RISC-V + auto RelSymbol = Rel.getSymbol(); + if (ContainingBF && RelSymbol != InputFile->symbol_end() && + // TODO is this good enough? Could also pass for UND symbols. + cantFail(RelSymbol->getSection()) == + RelSymbol->getObject()->section_end()) { + auto *Symbol = BC->getOrCreateUndefinedGlobalSymbol(SymbolName); + ContainingBF->addRelocation(Rel.getOffset(), Symbol, Rel.getType(), 0, + cantFail(RelSymbol->getValue())); + return; + } + LLVM_DEBUG(dbgs() << "BOLT-DEBUG: cannot determine referenced section.\n"); return; } @@ -2722,7 +2789,7 @@ // 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"); Index: bolt/lib/Target/CMakeLists.txt =================================================================== --- bolt/lib/Target/CMakeLists.txt +++ bolt/lib/Target/CMakeLists.txt @@ -7,3 +7,8 @@ message(STATUS "Targeting AArch64 in llvm-bolt") add_subdirectory(AArch64) endif() + +if ("RISCV" IN_LIST LLVM_TARGETS_TO_BUILD) + message(STATUS "Targeting RISCV in llvm-bolt") + add_subdirectory(RISCV) +endif() Index: bolt/lib/Target/RISCV/CMakeLists.txt =================================================================== --- /dev/null +++ bolt/lib/Target/RISCV/CMakeLists.txt @@ -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 + ) Index: bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp =================================================================== --- /dev/null +++ bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp @@ -0,0 +1,326 @@ +//===- 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::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(ImmExpr)) + return false; + + switch (cast(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 Index: bolt/test/RISCV/Inputs/plt-gnu-ld.yaml =================================================================== --- /dev/null +++ bolt/test/RISCV/Inputs/plt-gnu-ld.yaml @@ -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 +... Index: bolt/test/RISCV/lit.local.cfg =================================================================== --- /dev/null +++ bolt/test/RISCV/lit.local.cfg @@ -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}')) Index: bolt/test/RISCV/plt-gnu-ld.test =================================================================== --- /dev/null +++ bolt/test/RISCV/plt-gnu-ld.test @@ -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 Index: bolt/test/RISCV/reloc-branch.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-branch.s @@ -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 Index: bolt/test/RISCV/reloc-call.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-call.s @@ -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, 0(ra) + call f + ret + .size _start, .-_start Index: bolt/test/RISCV/reloc-got.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-got.s @@ -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 Index: bolt/test/RISCV/reloc-jal.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-jal.s @@ -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 Index: bolt/test/RISCV/reloc-pcrel.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-pcrel.s @@ -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 Index: bolt/test/RISCV/reloc-rvc-branch.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-rvc-branch.s @@ -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 Index: bolt/test/RISCV/reloc-rvc-jump.s =================================================================== --- /dev/null +++ bolt/test/RISCV/reloc-rvc-jump.s @@ -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