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