Files
clang-p2996/bolt/lib/Core/Relocation.cpp
Job Noorman ff5e2babcb [BOLT] Improve handling of relocations targeting specific instructions (#66395)
On RISC-V, there are certain relocations that target a specific
instruction instead of a more abstract location like a function or basic
block. Take the following example that loads a value from symbol `foo`:

```
nop
1: auipc t0, %pcrel_hi(foo)
ld t0, %pcrel_lo(1b)(t0)
```

This results in two relocation:
- auipc: `R_RISCV_PCREL_HI20` referencing `foo`;
- ld: `R_RISCV_PCREL_LO12_I` referencing to local label `1` which points
to the auipc instruction.

It is of utmost importance that the `R_RISCV_PCREL_LO12_I` keeps
referring to the auipc instruction; if not, the program will fail to
assemble. However, BOLT currently does not guarantee this.

BOLT currently assumes that all local symbols are jump targets and
always starts a new basic block at symbol locations. The example above
results in a CFG the looks like this:

```
.BB0:
    nop
.BB1:
    auipc t0, %pcrel_hi(foo)
    ld t0, %pcrel_lo(.BB1)(t0)
```

While this currently works (i.e., the `R_RISCV_PCREL_LO12_I` relocation
points to the correct instruction), it has two downsides:
- Too many basic blocks are created (the example above is logically only
  one yet two are created);
- If instructions are inserted in `.BB1` (e.g., by instrumentation),
  things will break since the label will not point to the auipc anymore.

This patch proposes to fix this issue by teaching BOLT to track labels
that should always point to a specific instruction. This is implemented
as follows:
- Add a new annotation type (`kLabel`) that allows us to annotate
  instructions with an `MCSymbol *`;
- Whenever we encounter a relocation type that is used to refer to a
  specific instruction (`Relocation::isInstructionReference`), we
  register it without a symbol;
- During disassembly, whenever we encounter an instruction with such a
  relocation, create a symbol for its target and store it in an offset
  to symbol map (to ensure multiple relocations referencing the same
  instruction use the same label);
- After disassembly, iterate this map to attach labels to instructions
  via the new annotation type;
- During emission, emit these labels right before the instruction.

I believe the use of annotations works quite well for this use case as
it allows us to reliably track instruction labels. If we were to store
them as offsets in basic blocks, it would be error prone to keep them
updated whenever instructions are inserted or removed.

I have chosen to add labels as first-class annotations (as opposed to a
generic one) because the documentation of `MCAnnotation` suggests that
generic annotations are to be used for optional metadata that can be
discarded without affecting correctness. As this is not the case for
labels, a first-class annotation seemed more appropriate.
2023-10-06 06:46:16 +00:00

1011 lines
30 KiB
C++

//===- bolt/Core/Relocation.cpp - Object file relocations -----------------===//
//
// 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 Relocation class.
//
//===----------------------------------------------------------------------===//
#include "bolt/Core/Relocation.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ELF.h"
using namespace llvm;
using namespace bolt;
namespace ELFReserved {
enum {
R_RISCV_TPREL_I = 49,
R_RISCV_TPREL_S = 50,
};
} // namespace ELFReserved
Triple::ArchType Relocation::Arch;
static bool isSupportedX86(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_X86_64_8:
case ELF::R_X86_64_16:
case ELF::R_X86_64_32:
case ELF::R_X86_64_32S:
case ELF::R_X86_64_64:
case ELF::R_X86_64_PC8:
case ELF::R_X86_64_PC32:
case ELF::R_X86_64_PC64:
case ELF::R_X86_64_PLT32:
case ELF::R_X86_64_GOTPC64:
case ELF::R_X86_64_GOTPCREL:
case ELF::R_X86_64_GOTTPOFF:
case ELF::R_X86_64_TPOFF32:
case ELF::R_X86_64_GOTPCRELX:
case ELF::R_X86_64_REX_GOTPCRELX:
return true;
}
}
static bool isSupportedAArch64(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_AARCH64_CALL26:
case ELF::R_AARCH64_JUMP26:
case ELF::R_AARCH64_TSTBR14:
case ELF::R_AARCH64_CONDBR19:
case ELF::R_AARCH64_ADR_PREL_LO21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_TLSDESC_CALL:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_PREL16:
case ELF::R_AARCH64_PREL32:
case ELF::R_AARCH64_PREL64:
case ELF::R_AARCH64_ABS16:
case ELF::R_AARCH64_ABS32:
case ELF::R_AARCH64_ABS64:
case ELF::R_AARCH64_MOVW_UABS_G0:
case ELF::R_AARCH64_MOVW_UABS_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G1:
case ELF::R_AARCH64_MOVW_UABS_G1_NC:
case ELF::R_AARCH64_MOVW_UABS_G2:
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
case ELF::R_AARCH64_MOVW_UABS_G3:
return true;
}
}
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_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_64:
case ELF::R_RISCV_TLS_GOT_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;
}
}
static size_t getSizeForTypeX86(uint64_t Type) {
switch (Type) {
default:
errs() << object::getELFRelocationTypeName(ELF::EM_X86_64, Type) << '\n';
llvm_unreachable("unsupported relocation type");
case ELF::R_X86_64_8:
case ELF::R_X86_64_PC8:
return 1;
case ELF::R_X86_64_16:
return 2;
case ELF::R_X86_64_PLT32:
case ELF::R_X86_64_PC32:
case ELF::R_X86_64_32S:
case ELF::R_X86_64_32:
case ELF::R_X86_64_GOTPCREL:
case ELF::R_X86_64_GOTTPOFF:
case ELF::R_X86_64_TPOFF32:
case ELF::R_X86_64_GOTPCRELX:
case ELF::R_X86_64_REX_GOTPCRELX:
return 4;
case ELF::R_X86_64_PC64:
case ELF::R_X86_64_64:
case ELF::R_X86_64_GOTPC64:
return 8;
}
}
static size_t getSizeForTypeAArch64(uint64_t Type) {
switch (Type) {
default:
errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
llvm_unreachable("unsupported relocation type");
case ELF::R_AARCH64_ABS16:
case ELF::R_AARCH64_PREL16:
return 2;
case ELF::R_AARCH64_CALL26:
case ELF::R_AARCH64_JUMP26:
case ELF::R_AARCH64_TSTBR14:
case ELF::R_AARCH64_CONDBR19:
case ELF::R_AARCH64_ADR_PREL_LO21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_TLSDESC_CALL:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_PREL32:
case ELF::R_AARCH64_MOVW_UABS_G0:
case ELF::R_AARCH64_MOVW_UABS_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G1:
case ELF::R_AARCH64_MOVW_UABS_G1_NC:
case ELF::R_AARCH64_MOVW_UABS_G2:
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
case ELF::R_AARCH64_MOVW_UABS_G3:
case ELF::R_AARCH64_ABS32:
return 4;
case ELF::R_AARCH64_ABS64:
case ELF::R_AARCH64_PREL64:
return 8;
}
}
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_PCREL_LO12_S:
case ELF::R_RISCV_32_PCREL:
case ELF::R_RISCV_CALL:
case ELF::R_RISCV_CALL_PLT:
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:
return 4;
case ELF::R_RISCV_64:
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
// See extractValueRISCV for why this is necessary.
return 8;
}
}
static bool skipRelocationTypeX86(uint64_t Type) {
return Type == ELF::R_X86_64_NONE;
}
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;
}
static bool skipRelocationProcessAArch64(uint64_t &Type, uint64_t Contents) {
auto IsMov = [](uint64_t Contents) -> bool {
// The bits 28-23 are 0b100101
return (Contents & 0x1f800000) == 0x12800000;
};
auto IsB = [](uint64_t Contents) -> bool {
// The bits 31-26 are 0b000101
return (Contents & 0xfc000000) == 0x14000000;
};
auto IsAdr = [](uint64_t Contents) -> bool {
// The bits 31-24 are 0b0xx10000
return (Contents & 0x9f000000) == 0x10000000;
};
auto IsAddImm = [](uint64_t Contents) -> bool {
// The bits 30-23 are 0b00100010
return (Contents & 0x7F800000) == 0x11000000;
};
auto IsNop = [](uint64_t Contents) -> bool { return Contents == 0xd503201f; };
// The linker might eliminate the instruction and replace it with NOP, ignore
if (IsNop(Contents))
return true;
// The linker might relax ADRP+LDR instruction sequence for loading symbol
// address from GOT table to ADRP+ADD sequence that would point to the
// binary-local symbol. Change relocation type in order to process it right.
if (Type == ELF::R_AARCH64_LD64_GOT_LO12_NC && IsAddImm(Contents)) {
Type = ELF::R_AARCH64_ADD_ABS_LO12_NC;
return false;
}
// The linker might perform TLS relocations relaxations, such as
// changed TLS access model (e.g. changed global dynamic model
// to initial exec), thus changing the instructions. The static
// relocations might be invalid at this point and we might no
// need to proccess these relocations anymore.
// More information could be found by searching
// elfNN_aarch64_tls_relax in bfd
switch (Type) {
default:
break;
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
if (IsMov(Contents))
return true;
}
}
// The linker might replace load/store instruction with jump and
// veneer due to errata 843419
// https://documentation-service.arm.com/static/5fa29fddb209f547eebd361d
// Thus load/store relocations for these instructions must be ignored
// NOTE: We only process GOT and TLS relocations this way since the
// addend used in load/store instructions won't change after bolt
// (it is important since the instruction in veneer won't have relocation)
switch (Type) {
default:
break;
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12: {
if (IsB(Contents))
return true;
}
}
// The linker might relax ADRP+ADD or ADRP+LDR sequences to the ADR+NOP
switch (Type) {
default:
break;
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
if (IsAdr(Contents))
return true;
}
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:
llvm_unreachable("unsupported relocation");
case ELF::R_X86_64_64:
case ELF::R_X86_64_32:
break;
case ELF::R_X86_64_PC32:
Value -= PC;
break;
}
return Value;
}
static uint64_t encodeValueAArch64(uint64_t Type, uint64_t Value, uint64_t PC) {
switch (Type) {
default:
llvm_unreachable("unsupported relocation");
case ELF::R_AARCH64_ABS32:
break;
case ELF::R_AARCH64_PREL16:
case ELF::R_AARCH64_PREL32:
case ELF::R_AARCH64_PREL64:
Value -= PC;
break;
case ELF::R_AARCH64_CALL26:
Value -= PC;
assert(isInt<28>(Value) && "only PC +/- 128MB is allowed for direct call");
// Immediate goes in bits 25:0 of BL.
// OP 1001_01 goes in bits 31:26 of BL.
Value = ((Value >> 2) & 0x3ffffff) | 0x94000000ULL;
break;
}
return Value;
}
static uint64_t encodeValueRISCV(uint64_t Type, uint64_t Value, uint64_t PC) {
switch (Type) {
default:
llvm_unreachable("unsupported relocation");
case ELF::R_RISCV_64:
break;
}
return Value;
}
static uint64_t extractValueX86(uint64_t Type, uint64_t Contents, uint64_t PC) {
if (Type == ELF::R_X86_64_32S)
return SignExtend64<32>(Contents);
if (Relocation::isPCRelative(Type))
return SignExtend64(Contents, 8 * Relocation::getSizeForType(Type));
return Contents;
}
static uint64_t extractValueAArch64(uint64_t Type, uint64_t Contents,
uint64_t PC) {
switch (Type) {
default:
errs() << object::getELFRelocationTypeName(ELF::EM_AARCH64, Type) << '\n';
llvm_unreachable("unsupported relocation type");
case ELF::R_AARCH64_ABS16:
case ELF::R_AARCH64_ABS32:
case ELF::R_AARCH64_ABS64:
return Contents;
case ELF::R_AARCH64_PREL16:
return static_cast<int64_t>(PC) + SignExtend64<16>(Contents & 0xffff);
case ELF::R_AARCH64_PREL32:
return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
case ELF::R_AARCH64_PREL64:
return static_cast<int64_t>(PC) + Contents;
case ELF::R_AARCH64_TLSDESC_CALL:
case ELF::R_AARCH64_JUMP26:
case ELF::R_AARCH64_CALL26:
// Immediate goes in bits 25:0 of B and BL.
Contents &= ~0xfffffffffc000000ULL;
return static_cast<int64_t>(PC) + SignExtend64<28>(Contents << 2);
case ELF::R_AARCH64_TSTBR14:
// Immediate:15:2 goes in bits 18:5 of TBZ, TBNZ
Contents &= ~0xfffffffffff8001fULL;
return static_cast<int64_t>(PC) + SignExtend64<16>(Contents >> 3);
case ELF::R_AARCH64_CONDBR19:
// Immediate:20:2 goes in bits 23:5 of Bcc, CBZ, CBNZ
Contents &= ~0xffffffffff00001fULL;
return static_cast<int64_t>(PC) + SignExtend64<21>(Contents >> 3);
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_ADR_PREL_LO21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC: {
// Bits 32:12 of Symbol address goes in bits 30:29 + 23:5 of ADRP
// and ADR instructions
bool IsAdr = !!(((Contents >> 31) & 0x1) == 0);
Contents &= ~0xffffffff9f00001fUll;
uint64_t LowBits = (Contents >> 29) & 0x3;
uint64_t HighBits = (Contents >> 5) & 0x7ffff;
Contents = LowBits | (HighBits << 2);
if (IsAdr)
return static_cast<int64_t>(PC) + SignExtend64<21>(Contents);
// ADRP instruction
Contents = static_cast<int64_t>(PC) + SignExtend64<33>(Contents << 12);
Contents &= ~0xfffUll;
return Contents;
}
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_LDST64_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of LD/ST instruction, taken
// from bits 11:3 of Symbol address
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 3);
}
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_ADD_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of ADD instruction
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 0);
}
case ELF::R_AARCH64_LDST128_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of ADD instruction, taken
// from bits 11:4 of Symbol address
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 4);
}
case ELF::R_AARCH64_LDST32_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of ADD instruction, taken
// from bits 11:2 of Symbol address
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 2);
}
case ELF::R_AARCH64_LDST16_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of ADD instruction, taken
// from bits 11:1 of Symbol address
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 1);
}
case ELF::R_AARCH64_LDST8_ABS_LO12_NC: {
// Immediate goes in bits 21:10 of ADD instruction, taken
// from bits 11:0 of Symbol address
Contents &= ~0xffffffffffc003ffU;
return Contents >> (10 - 0);
}
case ELF::R_AARCH64_MOVW_UABS_G3:
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
case ELF::R_AARCH64_MOVW_UABS_G2:
case ELF::R_AARCH64_MOVW_UABS_G1_NC:
case ELF::R_AARCH64_MOVW_UABS_G1:
case ELF::R_AARCH64_MOVW_UABS_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G0:
// The shift goest in bits 22:21 of MOV* instructions
uint8_t Shift = (Contents >> 21) & 0x3;
// Immediate goes in bits 20:5
Contents = (Contents >> 5) & 0xffff;
return Contents << (16 * Shift);
}
}
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));
}
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:
case ELF::R_RISCV_TLS_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:
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_64:
return Contents;
}
}
static bool isGOTX86(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_X86_64_GOT32:
case ELF::R_X86_64_GOTPCREL:
case ELF::R_X86_64_GOTTPOFF:
case ELF::R_X86_64_GOTOFF64:
case ELF::R_X86_64_GOTPC32:
case ELF::R_X86_64_GOT64:
case ELF::R_X86_64_GOTPCREL64:
case ELF::R_X86_64_GOTPC64:
case ELF::R_X86_64_GOTPLT64:
case ELF::R_X86_64_GOTPC32_TLSDESC:
case ELF::R_X86_64_GOTPCRELX:
case ELF::R_X86_64_REX_GOTPCRELX:
return true;
}
}
static bool isGOTAArch64(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_TLSDESC_CALL:
return true;
}
}
static bool isGOTRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
return true;
}
}
static bool isTLSX86(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_X86_64_TPOFF32:
case ELF::R_X86_64_TPOFF64:
case ELF::R_X86_64_GOTTPOFF:
return true;
}
}
static bool isTLSAArch64(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_TLSDESC_CALL:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
return true;
}
}
static bool isTLSRISCV(uint64_t Type) {
switch (Type) {
default:
return false;
case ELF::R_RISCV_TLS_GOT_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;
}
}
static bool isPCRelativeX86(uint64_t Type) {
switch (Type) {
default:
llvm_unreachable("Unknown relocation type");
case ELF::R_X86_64_64:
case ELF::R_X86_64_32:
case ELF::R_X86_64_32S:
case ELF::R_X86_64_16:
case ELF::R_X86_64_8:
case ELF::R_X86_64_TPOFF32:
return false;
case ELF::R_X86_64_PC8:
case ELF::R_X86_64_PC32:
case ELF::R_X86_64_PC64:
case ELF::R_X86_64_GOTPCREL:
case ELF::R_X86_64_PLT32:
case ELF::R_X86_64_GOTOFF64:
case ELF::R_X86_64_GOTPC32:
case ELF::R_X86_64_GOTPC64:
case ELF::R_X86_64_GOTTPOFF:
case ELF::R_X86_64_GOTPCRELX:
case ELF::R_X86_64_REX_GOTPCRELX:
return true;
}
}
static bool isPCRelativeAArch64(uint64_t Type) {
switch (Type) {
default:
llvm_unreachable("Unknown relocation type");
case ELF::R_AARCH64_ABS16:
case ELF::R_AARCH64_ABS32:
case ELF::R_AARCH64_ABS64:
case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
case ELF::R_AARCH64_LDST128_ABS_LO12_NC:
case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
case ELF::R_AARCH64_LDST16_ABS_LO12_NC:
case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_MOVW_UABS_G0:
case ELF::R_AARCH64_MOVW_UABS_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G1:
case ELF::R_AARCH64_MOVW_UABS_G1_NC:
case ELF::R_AARCH64_MOVW_UABS_G2:
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
case ELF::R_AARCH64_MOVW_UABS_G3:
return false;
case ELF::R_AARCH64_TLSDESC_CALL:
case ELF::R_AARCH64_CALL26:
case ELF::R_AARCH64_JUMP26:
case ELF::R_AARCH64_TSTBR14:
case ELF::R_AARCH64_CONDBR19:
case ELF::R_AARCH64_ADR_PREL_LO21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
case ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
case ELF::R_AARCH64_ADR_GOT_PAGE:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_TLSDESC_ADR_PREL21:
case ELF::R_AARCH64_TLSDESC_ADR_PAGE21:
case ELF::R_AARCH64_PREL16:
case ELF::R_AARCH64_PREL32:
case ELF::R_AARCH64_PREL64:
return true;
}
}
static bool isPCRelativeRISCV(uint64_t Type) {
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_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:
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)
return encodeValueRISCV(Type, Value, PC);
return encodeValueX86(Type, Value, PC);
}
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);
}
bool Relocation::isX86GOTPCRELX(uint64_t Type) {
if (Arch != Triple::x86_64)
return false;
return Type == ELF::R_X86_64_GOTPCRELX || Type == ELF::R_X86_64_REX_GOTPCRELX;
}
bool Relocation::isX86GOTPC64(uint64_t Type) {
if (Arch != Triple::x86_64)
return false;
return Type == ELF::R_X86_64_GOTPC64;
}
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);
}
bool Relocation::isInstructionReference(uint64_t Type) {
if (Arch != Triple::riscv64)
return false;
switch (Type) {
default:
return false;
case ELF::R_RISCV_PCREL_LO12_I:
case ELF::R_RISCV_PCREL_LO12_S:
return true;
}
}
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);
}
uint64_t Relocation::getAbs64() {
if (Arch == Triple::aarch64)
return ELF::R_AARCH64_ABS64;
if (Arch == Triple::riscv64)
return ELF::R_RISCV_64;
return ELF::R_X86_64_64;
}
uint64_t Relocation::getRelative() {
if (Arch == Triple::aarch64)
return ELF::R_AARCH64_RELATIVE;
return ELF::R_X86_64_RELATIVE;
}
size_t Relocation::emit(MCStreamer *Streamer) const {
const size_t Size = getSizeForType(Type);
const auto *Value = createExpr(Streamer);
Streamer->emitValue(Value, Size);
return Size;
}
const MCExpr *Relocation::createExpr(MCStreamer *Streamer) const {
MCContext &Ctx = Streamer->getContext();
const MCExpr *Value = nullptr;
if (Symbol && Addend) {
Value = MCBinaryExpr::createAdd(MCSymbolRefExpr::create(Symbol, Ctx),
MCConstantExpr::create(Addend, Ctx), Ctx);
} else if (Symbol) {
Value = MCSymbolRefExpr::create(Symbol, Ctx);
} else {
Value = MCConstantExpr::create(Addend, Ctx);
}
if (isPCRelative(Type)) {
MCSymbol *TempLabel = Ctx.createNamedTempSymbol();
Streamer->emitLabel(TempLabel);
Value = MCBinaryExpr::createSub(
Value, MCSymbolRefExpr::create(TempLabel, Ctx), Ctx);
}
return Value;
}
const MCExpr *Relocation::createExpr(MCStreamer *Streamer,
const MCExpr *RetainedValue) const {
const auto *Value = createExpr(Streamer);
if (RetainedValue) {
Value = MCBinaryExpr::create(getComposeOpcodeFor(Type), RetainedValue,
Value, Streamer->getContext());
}
return Value;
}
MCBinaryExpr::Opcode Relocation::getComposeOpcodeFor(uint64_t Type) {
assert(Arch == Triple::riscv64 && "only implemented for RISC-V");
switch (Type) {
default:
llvm_unreachable("not implemented");
case ELF::R_RISCV_ADD32:
return MCBinaryExpr::Add;
case ELF::R_RISCV_SUB32:
return MCBinaryExpr::Sub;
}
}
#define ELF_RELOC(name, value) #name,
void Relocation::print(raw_ostream &OS) const {
static const char *X86RelocNames[] = {
#include "llvm/BinaryFormat/ELFRelocs/x86_64.def"
};
static const char *AArch64RelocNames[] = {
#include "llvm/BinaryFormat/ELFRelocs/AArch64.def"
};
if (Arch == Triple::aarch64)
OS << AArch64RelocNames[Type];
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) {
OS << ", " << Symbol->getName();
}
if (int64_t(Addend) < 0)
OS << ", -0x" << Twine::utohexstr(-int64_t(Addend));
else
OS << ", 0x" << Twine::utohexstr(Addend);
OS << ", 0x" << Twine::utohexstr(Value);
}