This includes relocs needed to link against glibc: R_ARM_BASE_PREL R_ARM_GOT_BREL Every reloc is accompanied with a test case. llvm-svn: 233383
624 lines
23 KiB
C++
624 lines
23 KiB
C++
//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp ----------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ARMTargetHandler.h"
|
|
#include "ARMLinkingContext.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
|
|
using namespace lld;
|
|
using namespace lld::elf;
|
|
using namespace llvm::support::endian;
|
|
|
|
static Reference::Addend readAddend_THM_MOV(const uint8_t *location) {
|
|
const uint16_t halfHi = read16le(location);
|
|
const uint16_t halfLo = read16le(location + 2);
|
|
|
|
const uint16_t imm8 = halfLo & 0xFF;
|
|
const uint16_t imm3 = (halfLo >> 12) & 0x7;
|
|
|
|
const uint16_t imm4 = halfHi & 0xF;
|
|
const uint16_t bitI = (halfHi >> 10) & 0x1;
|
|
|
|
const auto result = int16_t((imm4 << 12) | (bitI << 11) | (imm3 << 8) | imm8);
|
|
return result;
|
|
}
|
|
|
|
static Reference::Addend readAddend_ARM_MOV(const uint8_t *location) {
|
|
const uint32_t value = read32le(location);
|
|
|
|
const uint32_t imm12 = value & 0xFFF;
|
|
const uint32_t imm4 = (value >> 16) & 0xF;
|
|
|
|
const auto result = int32_t((imm4 << 12) | imm12);
|
|
return result;
|
|
}
|
|
|
|
static Reference::Addend readAddend_THM_CALL(const uint8_t *location) {
|
|
const uint16_t halfHi = read16le(location);
|
|
const uint16_t halfLo = read16le(location + 2);
|
|
|
|
const uint16_t imm10 = halfHi & 0x3FF;
|
|
const uint16_t bitS = (halfHi >> 10) & 0x1;
|
|
|
|
const uint16_t imm11 = halfLo & 0x7FF;
|
|
const uint16_t bitJ2 = (halfLo >> 11) & 0x1;
|
|
const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1;
|
|
const uint16_t bitJ1 = (halfLo >> 13) & 0x1;
|
|
const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1;
|
|
|
|
const auto result = int32_t((bitS << 24) | (bitI1 << 23) | (bitI2 << 22) |
|
|
(imm10 << 12) | (imm11 << 1));
|
|
return llvm::SignExtend64<25>(result);
|
|
}
|
|
|
|
static Reference::Addend readAddend_ARM_CALL(const uint8_t *location) {
|
|
const uint32_t value = read32le(location);
|
|
|
|
const bool isBLX = (value & 0xF0000000) == 0xF0000000;
|
|
const uint32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0;
|
|
|
|
const auto result = int32_t(((value & 0xFFFFFF) << 2) | (bitH << 1));
|
|
return llvm::SignExtend64<26>(result);
|
|
}
|
|
|
|
static Reference::Addend readAddend_THM_JUMP11(const uint8_t *location) {
|
|
const auto value = read16le(location);
|
|
const uint16_t imm11 = value & 0x7FF;
|
|
|
|
return llvm::SignExtend32<12>(imm11 << 1);
|
|
}
|
|
|
|
static Reference::Addend readAddend(const uint8_t *location,
|
|
Reference::KindValue kindValue) {
|
|
switch (kindValue) {
|
|
case R_ARM_ABS32:
|
|
case R_ARM_REL32:
|
|
case R_ARM_GOT_BREL:
|
|
case R_ARM_BASE_PREL:
|
|
case R_ARM_TLS_IE32:
|
|
case R_ARM_TLS_LE32:
|
|
return (int32_t)read32le(location);
|
|
case R_ARM_PREL31:
|
|
return (int32_t)(read32le(location) & 0x7FFFFFFF);
|
|
case R_ARM_THM_CALL:
|
|
case R_ARM_THM_JUMP24:
|
|
return readAddend_THM_CALL(location);
|
|
case R_ARM_THM_JUMP11:
|
|
return readAddend_THM_JUMP11(location);
|
|
case R_ARM_CALL:
|
|
case R_ARM_JUMP24:
|
|
return readAddend_ARM_CALL(location);
|
|
case R_ARM_MOVW_ABS_NC:
|
|
case R_ARM_MOVT_ABS:
|
|
return readAddend_ARM_MOV(location);
|
|
case R_ARM_THM_MOVW_ABS_NC:
|
|
case R_ARM_THM_MOVT_ABS:
|
|
return readAddend_THM_MOV(location);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline void applyArmReloc(uint8_t *location, uint32_t result,
|
|
uint32_t mask = 0xFFFFFFFF) {
|
|
assert(!(result & ~mask));
|
|
write32le(location, (read32le(location) & ~mask) | (result & mask));
|
|
}
|
|
|
|
static inline void applyThmReloc(uint8_t *location, uint16_t resHi,
|
|
uint16_t resLo, uint16_t maskHi,
|
|
uint16_t maskLo = 0xFFFF) {
|
|
assert(!(resHi & ~maskHi) && !(resLo & ~maskLo));
|
|
write16le(location, (read16le(location) & ~maskHi) | (resHi & maskHi));
|
|
location += 2;
|
|
write16le(location, (read16le(location) & ~maskLo) | (resLo & maskLo));
|
|
}
|
|
|
|
static inline void applyThumb16Reloc(uint8_t *location, uint16_t result,
|
|
uint16_t mask = 0xFFFF) {
|
|
assert(!(result & ~mask));
|
|
write16le(location, (read16le(location) & ~mask) | (result & mask));
|
|
}
|
|
|
|
/// \brief R_ARM_ABS32 - (S + A) | T
|
|
static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)((S + A) | T);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
/// \brief R_ARM_REL32 - ((S + A) | T) - P
|
|
static void relocR_ARM_REL32(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
/// \brief R_ARM_PREL31 - ((S + A) | T) - P
|
|
static void relocR_ARM_PREL31(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
const uint32_t mask = 0x7FFFFFFF;
|
|
uint32_t rel31 = result & mask;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result);
|
|
llvm::dbgs() << " rel31: 0x" << Twine::utohexstr(rel31) << "\n");
|
|
|
|
applyArmReloc(location, rel31, mask);
|
|
}
|
|
|
|
/// \brief Relocate B/BL instructions. useJs defines whether J1 & J2 are used
|
|
static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) {
|
|
result = (result & 0x01FFFFFE) >> 1;
|
|
|
|
const uint16_t imm10 = (result >> 11) & 0x3FF;
|
|
const uint16_t bitS = (result >> 23) & 0x1;
|
|
const uint16_t resHi = (bitS << 10) | imm10;
|
|
|
|
const uint16_t imm11 = result & 0x7FF;
|
|
const uint16_t bitJ2 = useJs ? ((result >> 21) & 0x1) : bitS;
|
|
const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1;
|
|
const uint16_t bitJ1 = useJs ? ((result >> 22) & 0x1) : bitS;
|
|
const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1;
|
|
const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11;
|
|
|
|
applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF);
|
|
}
|
|
|
|
/// \brief R_ARM_THM_CALL - ((S + A) | T) - P
|
|
static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool useJs, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
const bool switchMode = !addressesThumb;
|
|
|
|
if (switchMode) {
|
|
P &= ~0x3; // Align(P, 4) by rounding down
|
|
}
|
|
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
relocR_ARM_THM_B_L(location, result, useJs);
|
|
|
|
if (switchMode) {
|
|
applyThmReloc(location, 0, 0, 0, 0x1001);
|
|
}
|
|
}
|
|
|
|
/// \brief R_ARM_THM_JUMP24 - ((S + A) | T) - P
|
|
static void relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
relocR_ARM_THM_B_L(location, result, true);
|
|
}
|
|
|
|
/// \brief R_ARM_THM_JUMP11 - S + A - P
|
|
static void relocR_ARM_THM_JUMP11(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
uint32_t result = (uint32_t)(S + A - P);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
|
|
//we cut off first bit because it is always 1 according to p. 4.5.3
|
|
result = (result & 0x0FFE) >> 1;
|
|
|
|
applyThumb16Reloc(location, result, 0x7FF);
|
|
}
|
|
|
|
/// \brief R_ARM_BASE_PREL - B(S) + A - P => S + A - P
|
|
static void relocR_ARM_BASE_PREL(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
uint32_t result = (uint32_t)(S + A - P);
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
/// \brief R_ARM_GOT_BREL - GOT(S) + A - GOT_ORG => S + A - GOT_ORG
|
|
static void relocR_ARM_GOT_BREL(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, uint64_t GOT_ORG) {
|
|
uint32_t result = (uint32_t)(S + A - GOT_ORG);
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
/// \brief R_ARM_CALL - ((S + A) | T) - P
|
|
static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
const bool switchMode = addressesThumb;
|
|
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
const uint32_t imm24 = (result & 0x03FFFFFC) >> 2;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, imm24, 0xFFFFFF);
|
|
|
|
if (switchMode) {
|
|
const uint32_t bitH = (result & 0x2) >> 1;
|
|
applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000);
|
|
}
|
|
}
|
|
|
|
/// \brief R_ARM_JUMP24 - ((S + A) | T) - P
|
|
static void relocR_ARM_JUMP24(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)(((S + A) | T) - P);
|
|
const uint32_t imm24 = (result & 0x03FFFFFC) >> 2;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, imm24, 0xFFFFFF);
|
|
}
|
|
|
|
/// \brief Relocate ARM MOVW/MOVT instructions
|
|
static void relocR_ARM_MOV(uint8_t *location, uint32_t result) {
|
|
const uint32_t imm12 = result & 0xFFF;
|
|
const uint32_t imm4 = (result >> 12) & 0xF;
|
|
|
|
applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF);
|
|
}
|
|
|
|
/// \brief R_ARM_MOVW_ABS_NC - (S + A) | T
|
|
static void relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)((S + A) | T);
|
|
const uint32_t arg = result & 0x0000FFFF;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
return relocR_ARM_MOV(location, arg);
|
|
}
|
|
|
|
/// \brief R_ARM_MOVT_ABS - S + A
|
|
static void relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
uint32_t result = (uint32_t)(S + A);
|
|
const uint32_t arg = (result & 0xFFFF0000) >> 16;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
return relocR_ARM_MOV(location, arg);
|
|
}
|
|
|
|
/// \brief Relocate Thumb MOVW/MOVT instructions
|
|
static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) {
|
|
const uint16_t imm8 = result & 0xFF;
|
|
const uint16_t imm3 = (result >> 8) & 0x7;
|
|
const uint16_t resLo = (imm3 << 12) | imm8;
|
|
|
|
const uint16_t imm4 = (result >> 12) & 0xF;
|
|
const uint16_t bitI = (result >> 11) & 0x1;
|
|
const uint16_t resHi = (bitI << 10) | imm4;
|
|
|
|
applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF);
|
|
}
|
|
|
|
/// \brief R_ARM_THM_MOVW_ABS_NC - (S + A) | T
|
|
static void relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P,
|
|
uint64_t S, int64_t A,
|
|
bool addressesThumb) {
|
|
uint64_t T = addressesThumb;
|
|
uint32_t result = (uint32_t)((S + A) | T);
|
|
const uint32_t arg = result & 0x0000FFFF;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " T: 0x" << Twine::utohexstr(T);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
return relocR_ARM_THM_MOV(location, arg);
|
|
}
|
|
|
|
/// \brief R_ARM_THM_MOVT_ABS - S + A
|
|
static void relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
uint32_t result = (uint32_t)(S + A);
|
|
const uint32_t arg = (result & 0xFFFF0000) >> 16;
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
return relocR_ARM_THM_MOV(location, arg);
|
|
}
|
|
|
|
/// \brief R_ARM_TLS_IE32 - GOT(S) + A - P => S + A - P
|
|
static void relocR_ARM_TLS_IE32(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
uint32_t result = (uint32_t)(S + A - P);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
/// \brief R_ARM_TLS_LE32 - S + A - tp => S + A + tpoff
|
|
static void relocR_ARM_TLS_LE32(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A, uint64_t tpoff) {
|
|
uint32_t result = (uint32_t)(S + A + tpoff);
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n");
|
|
applyArmReloc(location, result);
|
|
}
|
|
|
|
template <uint32_t lshift>
|
|
static void relocR_ARM_ALU_PC_GN_NC(uint8_t *location, uint32_t result) {
|
|
static_assert(lshift < 32 && lshift % 2 == 0,
|
|
"lshift must be even and less than word size");
|
|
|
|
const uint32_t rshift = 32 - lshift;
|
|
result = ((result >> lshift) & 0xFF) | ((rshift / 2) << 8);
|
|
|
|
applyArmReloc(location, result, 0xFFF);
|
|
}
|
|
|
|
/// \brief R_ARM_ALU_PC_G0_NC - ((S + A) | T) - P => S + A - P
|
|
static void relocR_ARM_ALU_PC_G0_NC(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
int32_t result = (int32_t)((S + A) - P);
|
|
|
|
if (result < 0)
|
|
llvm_unreachable(
|
|
"Negative offsets for group relocations has not been implemented");
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) << "\n");
|
|
|
|
relocR_ARM_ALU_PC_GN_NC<20>(location, (uint32_t)result);
|
|
}
|
|
|
|
/// \brief R_ARM_ALU_PC_G1_NC - ((S + A) | T) - P => S + A - P
|
|
static void relocR_ARM_ALU_PC_G1_NC(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
int32_t result = (int32_t)((S + A) - P);
|
|
|
|
if (result < 0)
|
|
llvm_unreachable(
|
|
"Negative offsets for group relocations has not been implemented");
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) << "\n");
|
|
|
|
relocR_ARM_ALU_PC_GN_NC<12>(location, (uint32_t)result);
|
|
}
|
|
|
|
/// \brief R_ARM_LDR_PC_G2 - S + A - P
|
|
static void relocR_ARM_LDR_PC_G2(uint8_t *location, uint64_t P, uint64_t S,
|
|
int64_t A) {
|
|
int32_t result = (int32_t)((S + A) - P);
|
|
|
|
if (result < 0)
|
|
llvm_unreachable(
|
|
"Negative offsets for group relocations has not been implemented");
|
|
|
|
DEBUG_WITH_TYPE(
|
|
"ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
|
|
llvm::dbgs() << " S: 0x" << Twine::utohexstr(S);
|
|
llvm::dbgs() << " A: 0x" << Twine::utohexstr(A);
|
|
llvm::dbgs() << " P: 0x" << Twine::utohexstr(P);
|
|
llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) << "\n");
|
|
|
|
const uint32_t mask = 0xFFF;
|
|
applyArmReloc(location, (uint32_t)result & mask, mask);
|
|
}
|
|
|
|
std::error_code ARMTargetRelocationHandler::applyRelocation(
|
|
ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom,
|
|
const Reference &ref) const {
|
|
uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset;
|
|
uint8_t *location = atomContent + ref.offsetInAtom();
|
|
uint64_t targetVAddress = writer.addressOfAtom(ref.target());
|
|
uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom();
|
|
|
|
if (ref.kindNamespace() != Reference::KindNamespace::ELF)
|
|
return std::error_code();
|
|
assert(ref.kindArch() == Reference::KindArch::ARM);
|
|
|
|
// Calculate proper initial addend for the relocation
|
|
const Reference::Addend addend =
|
|
readAddend(location, ref.kindValue()) + ref.addend();
|
|
|
|
// Flags that the relocation addresses Thumb instruction
|
|
bool addressesThumb = false;
|
|
|
|
if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) {
|
|
addressesThumb = isThumbCode(definedAtom);
|
|
}
|
|
|
|
switch (ref.kindValue()) {
|
|
case R_ARM_NONE:
|
|
break;
|
|
case R_ARM_ABS32:
|
|
relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_REL32:
|
|
relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_THM_CALL:
|
|
// TODO: consider adding bool variable to disable J1 & J2 for archs
|
|
// before ARMv6
|
|
relocR_ARM_THM_CALL(location, relocVAddress, targetVAddress, addend, true,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_CALL:
|
|
relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_JUMP24:
|
|
relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_THM_JUMP24:
|
|
relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_THM_JUMP11:
|
|
relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_MOVW_ABS_NC:
|
|
relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_MOVT_ABS:
|
|
relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_THM_MOVW_ABS_NC:
|
|
relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_THM_MOVT_ABS:
|
|
relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_PREL31:
|
|
relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend,
|
|
addressesThumb);
|
|
break;
|
|
case R_ARM_TLS_IE32:
|
|
relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_TLS_LE32:
|
|
relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend,
|
|
_armLayout.getTPOffset());
|
|
break;
|
|
case R_ARM_GOT_BREL:
|
|
relocR_ARM_GOT_BREL(location, relocVAddress, targetVAddress, addend,
|
|
_armLayout.getGOTSymAddr());
|
|
break;
|
|
case R_ARM_BASE_PREL:
|
|
// GOT origin is used for NULL symbol and when explicitly specified
|
|
if (!targetVAddress ||
|
|
ref.target()->name().equals("_GLOBAL_OFFSET_TABLE_")) {
|
|
targetVAddress = _armLayout.getGOTSymAddr();
|
|
} else {
|
|
llvm_unreachable("Segment-base relative addressing is not supported");
|
|
}
|
|
relocR_ARM_BASE_PREL(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_ALU_PC_G0_NC:
|
|
relocR_ARM_ALU_PC_G0_NC(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_ALU_PC_G1_NC:
|
|
relocR_ARM_ALU_PC_G1_NC(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_LDR_PC_G2:
|
|
relocR_ARM_LDR_PC_G2(location, relocVAddress, targetVAddress, addend);
|
|
break;
|
|
case R_ARM_IRELATIVE:
|
|
// Runtime only relocations. Ignore here.
|
|
break;
|
|
default:
|
|
return make_unhandled_reloc_error();
|
|
}
|
|
|
|
return std::error_code();
|
|
}
|