summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp')
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp578
1 files changed, 379 insertions, 199 deletions
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
index d24fdf0fa410..97b149133ff2 100644
--- a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
+++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
@@ -14,6 +14,8 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/MathExtras.h"
+#define DEBUG_TYPE "ARM"
+
using namespace lld;
using namespace lld::elf;
using namespace llvm::support::endian;
@@ -74,7 +76,7 @@ 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);
+ return llvm::SignExtend64<12>(imm11 << 1);
}
static Reference::Addend readAddend(const uint8_t *location,
@@ -82,11 +84,15 @@ static Reference::Addend readAddend(const uint8_t *location,
switch (kindValue) {
case R_ARM_ABS32:
case R_ARM_REL32:
+ case R_ARM_TARGET1:
+ case R_ARM_GOT_BREL:
+ case R_ARM_BASE_PREL:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LE32:
+ case R_ARM_TLS_TPOFF32:
return (int32_t)read32le(location);
case R_ARM_PREL31:
- return (int32_t)(read32le(location) & 0x7FFFFFFF);
+ return llvm::SignExtend64<31>(read32le(location) & 0x7FFFFFFF);
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
return readAddend_THM_CALL(location);
@@ -106,81 +112,98 @@ static Reference::Addend readAddend(const uint8_t *location,
}
}
-static inline void applyArmReloc(uint8_t *location, uint32_t result,
- uint32_t mask = 0xFFFFFFFF) {
+static inline void report_unsupported_range_group_reloc_error() {
+ llvm::report_fatal_error(
+ "Negative offsets for group relocations are not implemented");
+}
+
+static inline std::error_code applyArmReloc(uint8_t *location, uint32_t result,
+ uint32_t mask = 0xFFFFFFFF) {
assert(!(result & ~mask));
write32le(location, (read32le(location) & ~mask) | (result & mask));
+ return std::error_code();
}
-static inline void applyThmReloc(uint8_t *location, uint16_t resHi,
- uint16_t resLo, uint16_t maskHi,
- uint16_t maskLo = 0xFFFF) {
+static inline std::error_code applyThumb32Reloc(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));
+ return std::error_code();
}
-static inline void applyThumb16Reloc(uint8_t *location, uint16_t result,
- uint16_t mask = 0xFFFF) {
+static inline std::error_code
+applyThumb16Reloc(uint8_t *location, uint16_t result, uint16_t mask = 0xFFFF) {
assert(!(result & ~mask));
write16le(location, (read16le(location) & ~mask) | (result & mask));
+ return std::error_code();
}
/// \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) {
+static std::error_code 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);
+ DEBUG(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 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) {
+static std::error_code 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);
+ DEBUG(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 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) {
+static std::error_code 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);
+ if (!llvm::isInt<31>((int32_t)result))
+ return make_out_of_range_reloc_error();
+
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");
+ DEBUG(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);
+ return 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) {
+static std::error_code relocR_ARM_THM_B_L(uint8_t *location, uint32_t result,
+ bool useJs) {
+ if ((useJs && !llvm::isInt<25>((int32_t)result)) ||
+ (!useJs && !llvm::isInt<23>((int32_t)result)))
+ return make_out_of_range_reloc_error();
+
result = (result & 0x01FFFFFE) >> 1;
const uint16_t imm10 = (result >> 11) & 0x3FF;
@@ -194,12 +217,13 @@ static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) {
const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1;
const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11;
- applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF);
+ return applyThumb32Reloc(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) {
+static std::error_code 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;
@@ -209,137 +233,171 @@ static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S,
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);
+ DEBUG(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");
+ if (auto ec = relocR_ARM_THM_B_L(location, result, useJs))
+ return ec;
if (switchMode) {
- applyThmReloc(location, 0, 0, 0, 0x1001);
+ return applyThumb32Reloc(location, 0, 0, 0, 0x1001);
}
+ return std::error_code();
}
/// \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) {
+static std::error_code 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);
+ DEBUG(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_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) {
+static std::error_code 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");
+ DEBUG(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
+ if (!llvm::isInt<12>((int32_t)result))
+ return make_out_of_range_reloc_error();
+
+ // we cut off first bit because it is always 1 according to p. 4.5.3
result = (result & 0x0FFE) >> 1;
+ return applyThumb16Reloc(location, result, 0x7FF);
+}
+
+/// \brief R_ARM_BASE_PREL - B(S) + A - P => S + A - P
+static std::error_code 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(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 applyArmReloc(location, result);
+}
- applyThumb16Reloc(location, result, 0x7FF);
+/// \brief R_ARM_GOT_BREL - GOT(S) + A - GOT_ORG => S + A - GOT_ORG
+static std::error_code 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(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 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) {
+static std::error_code 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);
+ if (!llvm::isInt<26>((int32_t)result))
+ return make_out_of_range_reloc_error();
+
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);
+ DEBUG(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");
+ if (auto ec = applyArmReloc(location, imm24, 0xFFFFFF))
+ return ec;
if (switchMode) {
const uint32_t bitH = (result & 0x2) >> 1;
- applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000);
+ return applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000);
}
+ return std::error_code();
}
/// \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) {
+static std::error_code 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);
+ if (!llvm::isInt<26>((int32_t)result))
+ return make_out_of_range_reloc_error();
+
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);
+ DEBUG(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 applyArmReloc(location, imm24, 0xFFFFFF);
}
/// \brief Relocate ARM MOVW/MOVT instructions
-static void relocR_ARM_MOV(uint8_t *location, uint32_t result) {
+static std::error_code 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);
+ return 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) {
+static std::error_code 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");
+ DEBUG(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) {
+static std::error_code 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");
+ DEBUG(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) {
+static std::error_code 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;
@@ -348,153 +406,275 @@ static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) {
const uint16_t bitI = (result >> 11) & 0x1;
const uint16_t resHi = (bitI << 10) | imm4;
- applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF);
+ return applyThumb32Reloc(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) {
+static std::error_code 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");
+ DEBUG(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) {
+static std::error_code 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");
+ DEBUG(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) {
+static std::error_code 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);
+ DEBUG(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 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) {
+static std::error_code 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);
+ DEBUG(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 applyArmReloc(location, result);
+}
+
+/// \brief R_ARM_TLS_TPOFF32 - S + A - tp => S + A (offset within TLS block)
+static std::error_code relocR_ARM_TLS_TPOFF32(uint8_t *location, uint64_t P,
+ uint64_t S, int64_t A) {
+ uint32_t result = (uint32_t)(S + A);
+
+ DEBUG(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 applyArmReloc(location, result);
+}
+
+template <uint32_t lshift>
+static std::error_code 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);
+
+ return applyArmReloc(location, result, 0xFFF);
+}
+
+/// \brief R_ARM_ALU_PC_G0_NC - ((S + A) | T) - P => S + A - P
+static std::error_code 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)
+ report_unsupported_range_group_reloc_error();
+
+ DEBUG(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");
+
+ return 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 std::error_code 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)
+ report_unsupported_range_group_reloc_error();
+
+ DEBUG(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");
+
+ return relocR_ARM_ALU_PC_GN_NC<12>(location, (uint32_t)result);
+}
+
+/// \brief R_ARM_LDR_PC_G2 - S + A - P
+static std::error_code 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)
+ report_unsupported_range_group_reloc_error();
+
+ DEBUG(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;
+ return applyArmReloc(location, (uint32_t)result & mask, mask);
+}
+
+/// \brief Fixup unresolved weak reference with NOP instruction
+static bool fixupUnresolvedWeakCall(uint8_t *location,
+ Reference::KindValue kindValue) {
+ // TODO: workaround for archs without NOP instruction
+ switch (kindValue) {
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ // Thumb32 NOP.W
+ write32le(location, 0x8000F3AF);
+ break;
+ case R_ARM_THM_JUMP11:
+ // Thumb16 NOP
+ write16le(location, 0xBF00);
+ break;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ // A1 NOP<c>, save condition bits
+ applyArmReloc(location, 0x320F000, 0xFFFFFFF);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
}
std::error_code ARMTargetRelocationHandler::applyRelocation(
- ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom,
+ ELFWriter &writer, llvm::FileOutputBuffer &buf, const 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();
+ uint8_t *loc = atomContent + ref.offsetInAtom();
+ uint64_t target = writer.addressOfAtom(ref.target());
+ uint64_t reloc = atom._virtualAddr + ref.offsetInAtom();
if (ref.kindNamespace() != Reference::KindNamespace::ELF)
return std::error_code();
assert(ref.kindArch() == Reference::KindArch::ARM);
+ // Fixup unresolved weak references
+ if (!target) {
+ bool isCallFixed = fixupUnresolvedWeakCall(loc, ref.kindValue());
+
+ if (isCallFixed) {
+ DEBUG(llvm::dbgs() << "\t\tFixup unresolved weak reference '";
+ llvm::dbgs() << ref.target()->name() << "'";
+ llvm::dbgs() << " at address: 0x" << Twine::utohexstr(reloc);
+ llvm::dbgs() << (isCallFixed ? "\n" : " isn't possible\n"));
+ return std::error_code();
+ }
+ }
+
// Calculate proper initial addend for the relocation
const Reference::Addend addend =
- readAddend(location, ref.kindValue());
+ readAddend(loc, ref.kindValue()) + ref.addend();
// Flags that the relocation addresses Thumb instruction
- bool addressesThumb = false;
-
+ bool thumb = false;
if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) {
- addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel());
+ thumb = isThumbCode(definedAtom);
}
switch (ref.kindValue()) {
case R_ARM_NONE:
- break;
+ return std::error_code();
case R_ARM_ABS32:
- relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_ABS32(loc, reloc, target, addend, thumb);
case R_ARM_REL32:
- relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_REL32(loc, reloc, target, addend, thumb);
+ case R_ARM_TARGET1:
+ if (_armLayout.target1Rel())
+ return relocR_ARM_REL32(loc, reloc, target, addend, thumb);
+ else
+ return relocR_ARM_ABS32(loc, reloc, target, addend, thumb);
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;
+ return relocR_ARM_THM_CALL(loc, reloc, target, addend, true, thumb);
case R_ARM_CALL:
- relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_CALL(loc, reloc, target, addend, thumb);
case R_ARM_JUMP24:
- relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_JUMP24(loc, reloc, target, addend, thumb);
case R_ARM_THM_JUMP24:
- relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_THM_JUMP24(loc, reloc, target, addend, thumb);
case R_ARM_THM_JUMP11:
- relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend);
- break;
+ return relocR_ARM_THM_JUMP11(loc, reloc, target, addend);
case R_ARM_MOVW_ABS_NC:
- relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_MOVW_ABS_NC(loc, reloc, target, addend, thumb);
case R_ARM_MOVT_ABS:
- relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
- break;
+ return relocR_ARM_MOVT_ABS(loc, reloc, target, addend);
case R_ARM_THM_MOVW_ABS_NC:
- relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_THM_MOVW_ABS_NC(loc, reloc, target, addend, thumb);
case R_ARM_THM_MOVT_ABS:
- relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend);
- break;
+ return relocR_ARM_THM_MOVT_ABS(loc, reloc, target, addend);
case R_ARM_PREL31:
- relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend,
- addressesThumb);
- break;
+ return relocR_ARM_PREL31(loc, reloc, target, addend, thumb);
case R_ARM_TLS_IE32:
- relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend);
- break;
+ return relocR_ARM_TLS_IE32(loc, reloc, target, addend);
case R_ARM_TLS_LE32:
- relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend,
- _armLayout.getTPOffset());
- break;
+ return relocR_ARM_TLS_LE32(loc, reloc, target, addend,
+ _armLayout.getTPOffset());
+ case R_ARM_TLS_TPOFF32:
+ return relocR_ARM_TLS_TPOFF32(loc, reloc, target, addend);
+ case R_ARM_GOT_BREL:
+ return relocR_ARM_GOT_BREL(loc, reloc, target, addend,
+ _armLayout.getGOTSymAddr());
+ case R_ARM_BASE_PREL:
+ // GOT origin is used for NULL symbol and when explicitly specified
+ if (!target || ref.target()->name().equals("_GLOBAL_OFFSET_TABLE_")) {
+ target = _armLayout.getGOTSymAddr();
+ } else {
+ return make_dynamic_error_code(
+ "Segment-base relative addressing is not supported");
+ }
+ return relocR_ARM_BASE_PREL(loc, reloc, target, addend);
+ case R_ARM_ALU_PC_G0_NC:
+ return relocR_ARM_ALU_PC_G0_NC(loc, reloc, target, addend);
+ case R_ARM_ALU_PC_G1_NC:
+ return relocR_ARM_ALU_PC_G1_NC(loc, reloc, target, addend);
+ case R_ARM_LDR_PC_G2:
+ return relocR_ARM_LDR_PC_G2(loc, reloc, target, addend);
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_GLOB_DAT:
+ case R_ARM_IRELATIVE:
+ // Runtime only relocations. Ignore here.
+ return std::error_code();
+ case R_ARM_V4BX:
+ // TODO implement
+ return std::error_code();
default:
return make_unhandled_reloc_error();
}
- return std::error_code();
+ llvm_unreachable("All switch cases must return directly");
}