blob: a1afae765a1cf108760102b50778ab695f1ffe1e [file] [edit]
//===- RISCVRelocationHandler.cpp - RISC-V relocation handling -----------===//
//
// 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 implements the RISCV relocation handler.
//
//===----------------------------------------------------------------------===//
#include "bolt/Core/Relocation.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace bolt;
namespace ELFReserved {
enum {
R_RISCV_TPREL_I = 49,
R_RISCV_TPREL_S = 50,
};
} // namespace ELFReserved
namespace {
class RISCVRelocationHandler final : public RelocationHandler {
bool Is64Bit;
public:
explicit RISCVRelocationHandler(bool Is64Bit) : Is64Bit(Is64Bit) {}
bool isSupported(uint32_t Type) const override;
size_t getSizeForType(uint32_t Type) const override;
bool skipRelocationType(uint32_t Type) const override;
uint64_t encodeValue(uint32_t Type, uint64_t Value, uint64_t PC,
uint32_t OriginalInst) const override;
bool canEncodeValue(uint32_t Type, uint64_t Value,
uint64_t PC) const override;
uint64_t extractValue(uint32_t Type, uint64_t Contents,
uint64_t PC) const override;
bool isGOT(uint32_t Type) const override;
bool isRelative(uint32_t Type) const override;
bool isIRelative(uint32_t Type) const override;
bool isTLS(uint32_t Type) const override;
bool isInstructionReference(uint32_t Type) const override;
bool isPCRelative(uint32_t Type) const override;
uint32_t getNone() const override;
uint32_t getPC32() const override;
uint32_t getPC64() const override;
uint32_t getAbs64() const override;
uint32_t getRelative() const override;
MCBinaryExpr::Opcode getComposeOpcodeFor(uint32_t Type) const override;
void printType(raw_ostream &OS, uint32_t Type) const override;
};
} // namespace
bool RISCVRelocationHandler::isRelative(uint32_t Type) const {
return Type == ELF::R_RISCV_RELATIVE;
}
bool RISCVRelocationHandler::isIRelative(uint32_t Type) const {
if (!Is64Bit)
llvm_unreachable("not implemented");
return Type == ELF::R_RISCV_IRELATIVE;
}
bool RISCVRelocationHandler::isInstructionReference(uint32_t Type) const {
return Type == ELF::R_RISCV_PCREL_LO12_I || Type == ELF::R_RISCV_PCREL_LO12_S;
}
uint32_t RISCVRelocationHandler::getNone() const { return ELF::R_RISCV_NONE; }
uint32_t RISCVRelocationHandler::getPC32() const {
return ELF::R_RISCV_32_PCREL;
}
uint32_t RISCVRelocationHandler::getPC64() const {
llvm_unreachable("not implemented");
}
uint32_t RISCVRelocationHandler::getAbs64() const { return ELF::R_RISCV_64; }
uint32_t RISCVRelocationHandler::getRelative() const {
if (!Is64Bit)
llvm_unreachable("not implemented");
return ELF::R_RISCV_RELATIVE;
}
MCBinaryExpr::Opcode
RISCVRelocationHandler::getComposeOpcodeFor(uint32_t Type) const {
switch (Type) {
default:
llvm_unreachable("not implemented");
case ELF::R_RISCV_ADD32:
return MCBinaryExpr::Add;
case ELF::R_RISCV_SUB32:
return MCBinaryExpr::Sub;
}
}
void RISCVRelocationHandler::printType(raw_ostream &OS, uint32_t Type) const {
OS << object::getELFRelocationTypeName(ELF::EM_RISCV, Type);
}
bool RISCVRelocationHandler::isSupported(uint32_t Type) const {
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_PCREL_LO12_S:
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_RVC_BRANCH:
case ELF::R_RISCV_ADD32:
case ELF::R_RISCV_SUB32:
case ELF::R_RISCV_HI20:
case ELF::R_RISCV_LO12_I:
case ELF::R_RISCV_LO12_S:
case ELF::R_RISCV_32:
case ELF::R_RISCV_64:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
case ELF::R_RISCV_TPREL_HI20:
case ELF::R_RISCV_TPREL_ADD:
case ELF::R_RISCV_TPREL_LO12_I:
case ELF::R_RISCV_TPREL_LO12_S:
case ELFReserved::R_RISCV_TPREL_I:
case ELFReserved::R_RISCV_TPREL_S:
return true;
}
}
size_t RISCVRelocationHandler::getSizeForType(uint32_t Type) const {
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_PCREL_LO12_S:
case ELF::R_RISCV_32_PCREL:
case ELF::R_RISCV_ADD32:
case ELF::R_RISCV_SUB32:
case ELF::R_RISCV_HI20:
case ELF::R_RISCV_LO12_I:
case ELF::R_RISCV_LO12_S:
case ELF::R_RISCV_32:
return 4;
case ELF::R_RISCV_64:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
// See extractValue for why this is necessary.
return 8;
}
}
bool RISCVRelocationHandler::skipRelocationType(uint32_t Type) const {
switch (Type) {
default:
return false;
case ELF::R_RISCV_NONE:
case ELF::R_RISCV_RELAX:
case ELF::R_RISCV_ALIGN:
return true;
}
}
bool RISCVRelocationHandler::canEncodeValue(uint32_t Type, uint64_t Value,
uint64_t PC) const {
switch (Type) {
default:
llvm_unreachable("unsupported relocation");
case ELF::R_RISCV_32:
case ELF::R_RISCV_64:
return true;
}
}
uint64_t RISCVRelocationHandler::encodeValue(uint32_t Type, uint64_t Value,
uint64_t PC, uint32_t) const {
switch (Type) {
default:
llvm_unreachable("unsupported relocation");
case ELF::R_RISCV_32:
case ELF::R_RISCV_64:
break;
}
return Value;
}
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 extractSImmRISCV(uint32_t Contents) {
return SignExtend64<12>(((Contents >> 7) & 0x1f) | ((Contents >> 25) << 5));
}
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));
}
uint64_t RISCVRelocationHandler::extractValue(uint32_t Type, uint64_t Contents,
uint64_t PC) const {
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:
// The psABI "Relocations" chapter's "Procedure Calls" section defines
// R_RISCV_CALL and R_RISCV_CALL_PLT over an AUIPC/JALR pair. Decode both
// instructions so the addend includes the low 12 bits carried by JALR.
return extractUImmRISCV(Contents & 0xffffffff) +
extractIImmRISCV(Contents >> 32);
case ELF::R_RISCV_BRANCH:
return extractBImmRISCV(Contents);
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_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:
case ELF::R_RISCV_HI20:
return extractUImmRISCV(Contents);
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_LO12_I:
return extractIImmRISCV(Contents);
case ELF::R_RISCV_PCREL_LO12_S:
case ELF::R_RISCV_LO12_S:
return extractSImmRISCV(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));
case ELF::R_RISCV_ADD32:
case ELF::R_RISCV_SUB32:
case ELF::R_RISCV_32:
case ELF::R_RISCV_64:
return Contents;
}
}
bool RISCVRelocationHandler::isGOT(uint32_t Type) const {
switch (Type) {
default:
return false;
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
return true;
}
}
bool RISCVRelocationHandler::isTLS(uint32_t Type) const {
switch (Type) {
default:
return false;
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
case ELF::R_RISCV_TPREL_HI20:
case ELF::R_RISCV_TPREL_ADD:
case ELF::R_RISCV_TPREL_LO12_I:
case ELF::R_RISCV_TPREL_LO12_S:
case ELFReserved::R_RISCV_TPREL_I:
case ELFReserved::R_RISCV_TPREL_S:
return true;
}
}
bool RISCVRelocationHandler::isPCRelative(uint32_t Type) const {
switch (Type) {
default:
llvm_unreachable("Unknown relocation type");
case ELF::R_RISCV_ADD32:
case ELF::R_RISCV_SUB32:
case ELF::R_RISCV_HI20:
case ELF::R_RISCV_LO12_I:
case ELF::R_RISCV_LO12_S:
case ELF::R_RISCV_32:
case ELF::R_RISCV_64:
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_GOT_HI20:
case ELF::R_RISCV_PCREL_HI20:
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_PCREL_LO12_S:
case ELF::R_RISCV_RVC_JUMP:
case ELF::R_RISCV_RVC_BRANCH:
case ELF::R_RISCV_32_PCREL:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
return true;
}
}
namespace llvm::bolt {
std::unique_ptr<RelocationHandler> createRISCVRelocationHandler(bool Is64Bit) {
return std::make_unique<RISCVRelocationHandler>(Is64Bit);
}
} // namespace llvm::bolt