Skip to content

Commit 7cd729b

Browse files
authored
Merge pull request #384 from frasercrmck/refactor-relocs
[loader] Refactor common relocation code into helper
2 parents f0588da + a698445 commit 7cd729b

File tree

1 file changed

+62
-92
lines changed

1 file changed

+62
-92
lines changed

modules/loader/source/relocations.cpp

Lines changed: 62 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <loader/relocations.h>
2020

2121
#include <algorithm>
22+
#include <cstdint>
23+
#include <optional>
2224

2325
// There are many relocation types, but LLVM only emits a few, only those
2426
// present in lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp are implemented
@@ -76,33 +78,58 @@ inline Integer setBitRange(Integer value, Integer subvalue, int first,
7678
return (value & ~mask) | ((subvalue << first) & mask);
7779
}
7880

79-
bool resolveX86_32(const loader::Relocation &r, loader::ElfMap &map) {
80-
const uint32_t relocation_offset = r.offset & 0xFFFFFFFF;
81-
const int32_t addend = static_cast<int32_t>(r.addend);
81+
// Returns a triple of common variables used in relocation calculation:
82+
// * Writable position in for relocations (host memory)
83+
// * P - position of the relocation (target memory)
84+
// * S - value of the symbol in the symbol table (target memory)
85+
template <typename T>
86+
std::optional<std::tuple<uint8_t *, T, T>> decomposeRelocation(
87+
const loader::Relocation &r, loader::ElfMap &map) {
88+
static_assert(std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>,
89+
"Invalid relocation size");
90+
using SignedT = std::make_signed_t<T>;
91+
const T relocation_offset = r.offset & 0xFFFFFFFF;
92+
const SignedT addend = static_cast<SignedT>(r.addend);
8293

83-
uint32_t symbol_target_address = static_cast<uint32_t>(
84-
map.getSymbolTargetAddress(r.symbol_index).value_or(0));
94+
T symbol_target_address =
95+
static_cast<T>(map.getSymbolTargetAddress(r.symbol_index).value_or(0));
8596
if (symbol_target_address == 0) {
8697
#ifndef NDEBUG
8798
auto name = map.getSymbolName(r.symbol_index).value_or("<unknown symbol>");
8899
(void)fprintf(stderr, "Missing symbol: %.*s\n", (int)name.size(),
89100
name.data());
90101
#endif
91-
return false;
102+
return std::nullopt;
92103
}
93104
symbol_target_address += addend;
94-
const uint32_t relocated_section_base = static_cast<uint32_t>(
95-
map.getSectionTargetAddress(r.section_index).value_or(0));
105+
const T relocated_section_base =
106+
static_cast<T>(map.getSectionTargetAddress(r.section_index).value_or(0));
96107
if (relocated_section_base == 0) {
97-
return false;
108+
return std::nullopt;
98109
}
99110
uint8_t *relocated_section_begin =
100111
map.getSectionWritableAddress(r.section_index).value_or(nullptr);
101112
if (relocated_section_begin == nullptr) {
102-
return false;
113+
return std::nullopt;
103114
}
115+
104116
uint8_t *relocation_address = relocated_section_begin + relocation_offset;
105117

118+
const T relocation_target_address =
119+
relocated_section_base + relocation_offset;
120+
121+
return std::make_tuple(relocation_address, relocation_target_address,
122+
symbol_target_address);
123+
}
124+
125+
bool resolveX86_32(const loader::Relocation &r, loader::ElfMap &map) {
126+
auto relocation_data = decomposeRelocation<uint32_t>(r, map);
127+
if (!relocation_data) {
128+
return false;
129+
}
130+
auto [relocation_address, relocated_target_address, symbol_target_address] =
131+
*relocation_data;
132+
106133
using namespace loader::RelocationTypes::X86_32;
107134
switch (r.type) {
108135
default:
@@ -135,15 +162,14 @@ bool resolveX86_32(const loader::Relocation &r, loader::ElfMap &map) {
135162
// R_386_PC32 stores an addend at the relocation target as an int8_t
136163
uint8_t implicit_addend;
137164
cargo::read_little_endian(&implicit_addend, relocation_address);
138-
const uint32_t value = symbol_target_address - relocated_section_base -
139-
relocation_offset +
165+
const uint32_t value = symbol_target_address - relocated_target_address +
140166
static_cast<int8_t>(implicit_addend);
141167
cargo::write_little_endian(value, relocation_address);
142168
break;
143169
}
144170
case R_386_PC16: {
145171
const uint32_t real_value =
146-
symbol_target_address - relocated_section_base - relocation_offset;
172+
symbol_target_address - relocated_target_address;
147173
const uint16_t trunc_value = real_value & 0xFFFF;
148174
if (static_cast<int32_t>(real_value) !=
149175
static_cast<int16_t>(trunc_value)) {
@@ -154,7 +180,7 @@ bool resolveX86_32(const loader::Relocation &r, loader::ElfMap &map) {
154180
}
155181
case R_386_PC8: {
156182
const uint32_t real_value =
157-
symbol_target_address - relocated_section_base - relocation_offset;
183+
symbol_target_address - relocated_target_address;
158184
const uint8_t trunc_value = real_value & 0xFF;
159185
if (static_cast<int32_t>(real_value) !=
160186
static_cast<int8_t>(trunc_value)) {
@@ -168,31 +194,12 @@ bool resolveX86_32(const loader::Relocation &r, loader::ElfMap &map) {
168194
}
169195

170196
bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
171-
const uint64_t relocation_offset = r.offset & 0xFFFFFFFF;
172-
const int64_t addend = r.addend;
173-
174-
uint64_t symbol_target_address =
175-
map.getSymbolTargetAddress(r.symbol_index).value_or(0);
176-
if (symbol_target_address == 0) {
177-
#ifndef NDEBUG
178-
auto name = map.getSymbolName(r.symbol_index).value_or("<unknown symbol>");
179-
(void)fprintf(stderr, "Missing symbol: %.*s\n", (int)name.size(),
180-
name.data());
181-
#endif
197+
auto relocation_data = decomposeRelocation<uint64_t>(r, map);
198+
if (!relocation_data) {
182199
return false;
183200
}
184-
symbol_target_address += addend;
185-
const uint64_t relocated_section_base =
186-
map.getSectionTargetAddress(r.section_index).value_or(0);
187-
if (relocated_section_base == 0) {
188-
return false;
189-
}
190-
uint8_t *relocated_section_begin =
191-
map.getSectionWritableAddress(r.section_index).value_or(nullptr);
192-
if (relocated_section_begin == nullptr) {
193-
return false;
194-
}
195-
uint8_t *relocation_address = relocated_section_begin + relocation_offset;
201+
auto [relocation_address, relocated_target_address, symbol_target_address] =
202+
*relocation_data;
196203

197204
using namespace loader::RelocationTypes::X86_64;
198205
switch (r.type) {
@@ -210,8 +217,7 @@ bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
210217
}
211218
// PC-relative 64-bit relocation
212219
case R_X86_64_PC64: {
213-
const uint64_t value =
214-
symbol_target_address - relocated_section_base - relocation_offset;
220+
const uint64_t value = symbol_target_address - relocated_target_address;
215221
cargo::write_little_endian(value, relocation_address);
216222
break;
217223
}
@@ -240,7 +246,7 @@ bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
240246
// sign-extensions are valid
241247
case R_X86_64_PC32: {
242248
const uint64_t real_value =
243-
symbol_target_address - relocated_section_base - relocation_offset;
249+
symbol_target_address - relocated_target_address;
244250
const uint32_t trunc_value = real_value & 0xFFFFFFFF;
245251
if (static_cast<int64_t>(real_value) !=
246252
static_cast<int32_t>(trunc_value)) {
@@ -251,7 +257,7 @@ bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
251257
}
252258
case R_X86_64_PC16: {
253259
const uint64_t real_value =
254-
symbol_target_address - relocated_section_base - relocation_offset;
260+
symbol_target_address - relocated_target_address;
255261
const uint16_t trunc_value = real_value & 0xFFFF;
256262
if (static_cast<int64_t>(real_value) !=
257263
static_cast<int16_t>(trunc_value)) {
@@ -262,7 +268,7 @@ bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
262268
}
263269
case R_X86_64_PC8: {
264270
const uint64_t real_value =
265-
symbol_target_address - relocated_section_base - relocation_offset;
271+
symbol_target_address - relocated_target_address;
266272
const uint8_t trunc_value = real_value & 0xFF;
267273
if (static_cast<int64_t>(real_value) !=
268274
static_cast<int8_t>(trunc_value)) {
@@ -278,33 +284,16 @@ bool resolveX86_64(const loader::Relocation &r, loader::ElfMap &map) {
278284
// assumes little-endian Arm, because big-endian is extremely rare
279285
bool resolveArm(const loader::Relocation &r, loader::ElfMap &map,
280286
loader::Relocation::StubMap &stubs) {
281-
const uint32_t relocation_offset = r.offset & 0xFFFFFFFF;
282-
const int32_t addend = static_cast<int32_t>(r.addend);
283-
284-
uint32_t symbol_target_address = static_cast<uint32_t>(
285-
map.getSymbolTargetAddress(r.symbol_index).value_or(0));
286-
if (symbol_target_address == 0) {
287-
#ifndef NDEBUG
288-
auto name = map.getSymbolName(r.symbol_index).value_or("<unknown symbol>");
289-
(void)fprintf(stderr, "Missing symbol: %.*s\n", (int)name.size(),
290-
name.data());
291-
#endif
287+
auto relocation_data = decomposeRelocation<uint32_t>(r, map);
288+
if (!relocation_data) {
292289
return false;
293290
}
294-
symbol_target_address += addend;
295-
const uint32_t relocated_section_base = static_cast<uint32_t>(
296-
map.getSectionTargetAddress(r.section_index).value_or(0));
297-
if (relocated_section_base == 0) {
298-
return false;
299-
}
300-
uint8_t *relocated_section_begin =
301-
map.getSectionWritableAddress(r.section_index).value_or(nullptr);
302-
if (relocated_section_begin == nullptr) {
303-
return false;
304-
}
305-
uint8_t *relocation_address = relocated_section_begin + relocation_offset;
306-
const uint32_t relocation_target_address =
307-
relocated_section_base + relocation_offset;
291+
auto [relocation_address, relocation_target_address, symbol_tgt_address] =
292+
*relocation_data;
293+
// We can't auto-capture structured bindings (lambda, below) so make an
294+
// explicit copy of it here.
295+
auto symbol_target_address = symbol_tgt_address;
296+
308297
uint32_t value;
309298
cargo::read_little_endian(&value, relocation_address);
310299

@@ -394,38 +383,18 @@ bool resolveAArch64(const loader::Relocation &r, loader::ElfMap &map,
394383
map.getSymbolName(r.symbol_index).value_or("<unknown symbol>");
395384
#endif
396385

397-
const uint64_t relocation_offset = r.offset;
398-
const int64_t addend = r.addend;
399-
400-
uint64_t symbol_target_address =
401-
map.getSymbolTargetAddress(r.symbol_index).value_or(0);
402-
if (symbol_target_address == 0) {
403-
#ifndef NDEBUG
404-
(void)fprintf(stderr, "Missing symbol: %.*s\n", (int)symbol_name.size(),
405-
symbol_name.data());
406-
#endif
407-
return false;
408-
}
409-
symbol_target_address += addend;
410-
const uint64_t relocated_section_base =
411-
map.getSectionTargetAddress(r.section_index).value_or(0);
412-
if (relocated_section_base == 0) {
413-
return false;
414-
}
415-
uint8_t *relocated_section_begin =
416-
map.getSectionWritableAddress(r.section_index).value_or(nullptr);
417-
if (relocated_section_begin == nullptr) {
386+
auto relocation_data = decomposeRelocation<uint64_t>(r, map);
387+
if (!relocation_data) {
418388
return false;
419389
}
420-
uint8_t *relocation_address = relocated_section_begin + relocation_offset;
421-
const uint64_t relocation_target_address =
422-
relocated_section_base + relocation_offset;
390+
auto [relocation_address, relocation_target_address, symbol_target_address] =
391+
*relocation_data;
392+
423393
uint64_t value;
424394
cargo::read_little_endian(&value, relocation_address);
425395
uint32_t value32;
426396
cargo::read_little_endian(&value32, relocation_address);
427397

428-
using namespace loader::RelocationTypes::AArch64;
429398
// returns the address of the stub to jump to
430399
auto getOrCreateStub = [&](uint64_t symbol_target_address) -> uint64_t {
431400
auto found_stub_target = stubs.getTarget(symbol_target_address);
@@ -472,6 +441,7 @@ bool resolveAArch64(const loader::Relocation &r, loader::ElfMap &map,
472441
return target + 4;
473442
};
474443

444+
using namespace loader::RelocationTypes::AArch64;
475445
switch (r.type) {
476446
default:
477447
CARGO_ASSERT(0, "Unsupported relocation type.");

0 commit comments

Comments
 (0)