summaryrefslogtreecommitdiffstats
path: root/lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
blob: 16be34e783789cf324804bd1a9ad2ba6b67d0647 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//===--------- 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/MathExtras.h"

using namespace lld;
using namespace elf;

static Reference::Addend readAddend_ARM_CALL(const uint8_t *location) {
  const auto value = int32_t(
      *reinterpret_cast<const llvm::support::little32_t *>(location));

  const bool isBLX = (value & 0xF0000000) == 0xF0000000;
  const int32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0;

  const int32_t result = ((value & 0xFFFFFF) << 2) | (bitH << 1);
  return llvm::SignExtend64<26>(result);
}

static Reference::Addend readAddend(const uint8_t *location,
                                    Reference::KindValue kindValue) {
  switch (kindValue) {
  case R_ARM_ABS32:
    return int32_t(
        *reinterpret_cast<const llvm::support::little32_t *>(location));
  case R_ARM_CALL:
    return readAddend_ARM_CALL(location);
  default:
    return 0;
  }
}

static inline void applyArmReloc(uint8_t *location, uint32_t result,
                                 uint32_t mask = 0xFFFFFFFF) {
  assert(!(result & ~mask));
  *reinterpret_cast<llvm::support::ulittle32_t *>(location) =
      (uint32_t(*reinterpret_cast<llvm::support::ulittle32_t *>(location)) &
       ~mask) | (result & mask);
}

/// \brief R_ARM_ABS32 - (S + A) | T => S + A
static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S,
                             int64_t A) {
  uint32_t result = (uint32_t)(S + A);
  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 => S + A - P
static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S,
                            int64_t A) {
  uint32_t result = (uint32_t)((S + A) - 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() << " result: 0x" << Twine::utohexstr(result) << "\n");
  applyArmReloc(location, imm24, 0xFFFFFF);
}

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());

  switch (ref.kindValue()) {
  case R_ARM_NONE:
    break;
  case R_ARM_ABS32:
    relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend);
    break;
  case R_ARM_CALL:
    relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend);
    break;
  default:
    return make_unhandled_reloc_error();
  }

  return std::error_code();
}
OpenPOWER on IntegriCloud