blob: d2d1fa29cdb6634e64b110ca4fde8867eff76b22 [file] [edit]
//===- bolt/Target/RISCV/RISCVMCSymbolizer.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
//
//===----------------------------------------------------------------------===//
#include "RISCVMCSymbolizer.h"
#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "bolt/Core/BinaryContext.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "bolt/Core/Relocation.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#define DEBUG_TYPE "bolt-symbolizer"
namespace llvm {
namespace bolt {
static bool isValidControlTransferAUIPC(const MCInst &Inst) {
if (Inst.getOpcode() != RISCV::AUIPC ||
MCPlus::getNumPrimeOperands(Inst) != 2)
return false;
const MCOperand &Base = Inst.getOperand(0);
return Base.isReg() && Base.getReg() != RISCV::X0 &&
Inst.getOperand(1).isImm();
}
static bool isValidControlTransferJALR(const MCInst &Inst) {
if (Inst.getOpcode() != RISCV::JALR || MCPlus::getNumPrimeOperands(Inst) != 3)
return false;
return Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() &&
Inst.getOperand(2).isImm();
}
static bool isLinkerResolvedControlTransfer(const MCInst &AUIPC,
const MCInst &JALR) {
if (!isValidControlTransferAUIPC(AUIPC) || !isValidControlTransferJALR(JALR))
return false;
const MCRegister Base = AUIPC.getOperand(0).getReg();
if (JALR.getOperand(1).getReg() != Base)
return false;
const MCRegister Link = JALR.getOperand(0).getReg();
return Link == RISCV::X0 || Link == Base;
}
static int64_t getLinkerResolvedControlTransferOffset(const MCInst &AUIPC,
const MCInst &JALR) {
// The symbolic AUIPC decoder passes the sign-extended 20-bit U-immediate
// already shifted left by 12. JALR adds its sign-extended 12-bit immediate
// and clears target bit zero.
return (AUIPC.getOperand(1).getImm() + JALR.getOperand(2).getImm()) & ~1LL;
}
RISCVMCSymbolizer::RISCVMCSymbolizer(BinaryFunction &Function,
bool CreateNewSymbols)
: MCSymbolizer(*Function.getBinaryContext().Ctx, nullptr),
Function(Function), CreateNewSymbols(CreateNewSymbols) {
BinaryContext &BC = Function.getBinaryContext();
const RelocationHandler &RH = BC.getRelocationHandler();
// Discover instruction references before decoding starts. This lets us
// attach a label while decoding the referenced %pcrel_hi instruction even
// though its %pcrel_lo user is normally decoded later.
for (uint64_t SearchOffset = 0; SearchOffset < Function.getSize();) {
const Relocation *Rel =
Function.getRelocationInRange(SearchOffset, Function.getSize());
if (!Rel)
break;
if (RH.isInstructionReference(Rel->Type)) {
assert(Rel->Value >= Function.getAddress() &&
Rel->Value < Function.getAddress() + Function.getSize() &&
"RISC-V instruction reference outside of function");
const uint64_t ReferencedOffset = Rel->Value - Function.getAddress();
InstructionReferences.try_emplace(ReferencedOffset,
InstructionReferenceInfo{Rel, nullptr});
}
SearchOffset = Rel->Offset + 1;
}
}
RISCVMCSymbolizer::~RISCVMCSymbolizer() {}
MCSymbol *RISCVMCSymbolizer::getOrCreateInstructionLabel(
InstructionReferenceInfo &ReferenceInfo) {
if (!ReferenceInfo.Label)
ReferenceInfo.Label = Ctx.createNamedTempSymbol();
return ReferenceInfo.Label;
}
uint64_t RISCVMCSymbolizer::getGOTValue(const Relocation &Rel) const {
BinaryContext &BC = Function.getBinaryContext();
const RelocationHandler &RH = BC.getRelocationHandler();
const uint64_t HiAddress = Function.getAddress() + Rel.Offset;
// A GOT high relocation records a combined high/low value. Locate the low
// relocation by its reference back to this AUIPC instead of assuming that
// the low instruction is adjacent.
auto It = InstructionReferences.find(Rel.Offset);
if (It != InstructionReferences.end() && It->second.LowRelocation) {
const Relocation *LoRel = It->second.LowRelocation;
ErrorOr<uint64_t> HiContents = BC.getUnsignedValueAtAddress(HiAddress, 4);
ErrorOr<uint64_t> LoContents = BC.getUnsignedValueAtAddress(
Function.getAddress() + LoRel->Offset, RH.getSizeForType(LoRel->Type));
assert(HiContents && LoContents &&
"cannot read RISC-V GOT relocation pair");
return RH.extractValue(ELF::R_RISCV_PCREL_HI20, *HiContents, HiAddress) +
RH.extractValue(LoRel->Type, *LoContents,
Function.getAddress() + LoRel->Offset);
}
return Rel.Value;
}
bool RISCVMCSymbolizer::trySymbolizeLinkerResolvedControlTransfer(
MCInst &Inst, int64_t Value, uint64_t InstAddress) {
BinaryContext &BC = Function.getBinaryContext();
MCContext *Ctx = BC.Ctx.get();
const uint64_t InstOffset = InstAddress - Function.getAddress();
// Recover RV64 linker-resolved intra-section calls and tail calls without
// relocations. Decode the following JALR and attach a call expression to the
// AUIPC before function reordering can move the caller relative to the
// callee.
if (Inst.getOpcode() != RISCV::AUIPC || !CreateNewSymbols ||
!BC.TheTriple->isRISCV64() || InstOffset + 8 > Function.getSize() ||
Function.getRelocationInRange(InstOffset, InstOffset + 8))
return false;
ErrorOr<ArrayRef<uint8_t>> FunctionData = Function.getData();
if (!FunctionData)
return false;
MCInst JALR;
uint64_t JALRSize = 0;
if (!BC.DisAsm->getInstruction(JALR, JALRSize,
FunctionData->slice(InstOffset + 4),
InstAddress + 4, nulls()) ||
JALRSize != 4)
return false;
MCInst AUIPC = Inst;
AUIPC.addOperand(MCOperand::createImm(Value));
if (!isLinkerResolvedControlTransfer(AUIPC, JALR))
return false;
const uint64_t Target =
InstAddress + getLinkerResolvedControlTransferOffset(AUIPC, JALR);
BinaryFunction *TargetBF = BC.getBinaryFunctionContainingAddress(Target);
if (!TargetBF)
return false;
// JALR with rd=x0 is a no-link jump. It is a tail call only when it
// transfers control to another function; a target in the current function
// is an intraprocedural long jump and must not be represented as a call.
if (JALR.getOperand(0).getReg() == RISCV::X0 && TargetBF == &Function)
return false;
BC.addInterproceduralReference(&Function, Target);
MCSymbol *TargetSymbol =
BC.handleExternalBranchTarget(Target, Function, *TargetBF);
if (!TargetSymbol)
return false;
const MCExpr *Expr = MCSymbolRefExpr::create(TargetSymbol, *Ctx);
Inst.addOperand(MCOperand::createExpr(
BC.MIB->getTargetExprFor(Inst, Expr, *Ctx, ELF::R_RISCV_CALL_PLT)));
return true;
}
bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
MCInst &Inst, raw_ostream &CStream, int64_t Value, uint64_t InstAddress,
bool IsBranch, uint64_t ImmOffset, uint64_t ImmSize, uint64_t InstSize) {
BinaryContext &BC = Function.getBinaryContext();
const RelocationHandler &RH = BC.getRelocationHandler();
MCContext *Ctx = BC.Ctx.get();
const uint64_t InstOffset = InstAddress - Function.getAddress();
// Branches and calls are resolved by BinaryFunction's target-independent
// control-flow handling.
if (BC.MIB->isBranch(Inst) || BC.MIB->isCall(Inst))
return false;
// Linker processing of R_RISCV_ALIGN can leave emitted relocations at an
// offset inside the instruction they apply to. Match the whole instruction
// range, as BinaryFunction::disassemble() did before this target-specific
// handling moved into the symbolizer.
const Relocation *Rel =
Function.getRelocationInRange(InstOffset, InstOffset + InstSize);
if (!Rel)
return trySymbolizeLinkerResolvedControlTransfer(Inst, Value, InstAddress);
MCSymbol *Symbol = Rel->Symbol;
uint64_t Addend = Rel->Addend;
if (RH.isInstructionReference(Rel->Type)) {
if (!CreateNewSymbols)
return false;
auto [It, _] =
InstructionReferences.try_emplace(Rel->Value - Function.getAddress());
Symbol = getOrCreateInstructionLabel(It->second);
// The input addend reflects the original AUIPC location. The label now
// follows the instruction, so the assembler must derive the low bits from
// its new location.
Addend = 0;
}
// GOT high relocations name the object stored in the GOT, not the GOT entry
// addressed by AUIPC. Preserve the actual entry address using a zero-based
// symbol, as the RISC-V emitter reuses the input GOT.
if (RH.isGOT(Rel->Type)) {
assert(RH.isPCRelative(Rel->Type) &&
"GOT relocation must be PC-relative on RISC-V");
Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
Addend = getGOTValue(*Rel) + InstAddress;
}
assert(Symbol && "RISC-V relocation without a symbol");
const MCExpr *Expr = MCSymbolRefExpr::create(Symbol, *Ctx);
if (Addend)
Expr = MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Addend, *Ctx),
*Ctx);
Inst.addOperand(MCOperand::createExpr(
BC.MIB->getTargetExprFor(Inst, Expr, *Ctx, Rel->Type)));
// MC annotations must follow every real operand. Attach the instruction
// label only after the symbolized immediate has been appended.
if (CreateNewSymbols) {
auto It = InstructionReferences.find(InstOffset);
if (It != InstructionReferences.end())
BC.MIB->setInstLabel(Inst, getOrCreateInstructionLabel(It->second));
}
return true;
}
void RISCVMCSymbolizer::tryAddingPcLoadReferenceComment(raw_ostream &CStream,
int64_t Value,
uint64_t Address) {}
} // namespace bolt
} // namespace llvm