Skip to content

Commit a552fb2

Browse files
committed
[lld-macho] Have relocation address included in range-check error message
This makes it easier to debug those errors. See e.g. #52767 (comment) We take the approach of 'reverse-engineering' the InputSection from the output buffer offset. This provides for a cleaner Target API, and is similar to LLD-ELF's implementation of getErrorPlace(). Reviewed By: #lld-macho, Roger Differential Revision: https://reviews.llvm.org/D118903
1 parent e03d216 commit a552fb2

File tree

8 files changed

+123
-67
lines changed

8 files changed

+123
-67
lines changed

lld/MachO/Arch/ARM64Common.cpp

+21-20
Original file line numberDiff line numberDiff line change
@@ -38,56 +38,57 @@ int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
3838
}
3939
}
4040

41+
static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
42+
switch (r.length) {
43+
case 2:
44+
checkInt(loc, r, value, 32);
45+
write32le(loc, value);
46+
break;
47+
case 3:
48+
write64le(loc, value);
49+
break;
50+
default:
51+
llvm_unreachable("invalid r_length");
52+
}
53+
}
54+
4155
// For instruction relocations (load, store, add), the base
4256
// instruction is pre-populated in the text section. A pre-populated
4357
// instruction has opcode & register-operand bits set, with immediate
4458
// operands zeroed. We read it from text, OR-in the immediate
4559
// operands, then write-back the completed instruction.
46-
4760
void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
4861
uint64_t pc) const {
62+
auto loc32 = reinterpret_cast<uint32_t *>(loc);
4963
uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
5064
switch (r.type) {
5165
case ARM64_RELOC_BRANCH26:
52-
value = encodeBranch26(r, base, value - pc);
66+
encodeBranch26(loc32, r, base, value - pc);
5367
break;
5468
case ARM64_RELOC_SUBTRACTOR:
5569
case ARM64_RELOC_UNSIGNED:
56-
if (r.length == 2)
57-
checkInt(r, value, 32);
70+
writeValue(loc, r, value);
5871
break;
5972
case ARM64_RELOC_POINTER_TO_GOT:
6073
if (r.pcrel)
6174
value -= pc;
62-
checkInt(r, value, 32);
75+
writeValue(loc, r, value);
6376
break;
6477
case ARM64_RELOC_PAGE21:
6578
case ARM64_RELOC_GOT_LOAD_PAGE21:
66-
case ARM64_RELOC_TLVP_LOAD_PAGE21: {
79+
case ARM64_RELOC_TLVP_LOAD_PAGE21:
6780
assert(r.pcrel);
68-
value = encodePage21(r, base, pageBits(value) - pageBits(pc));
81+
encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
6982
break;
70-
}
7183
case ARM64_RELOC_PAGEOFF12:
7284
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
7385
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
7486
assert(!r.pcrel);
75-
value = encodePageOff12(base, value);
87+
encodePageOff12(loc32, base, value);
7688
break;
7789
default:
7890
llvm_unreachable("unexpected relocation type");
7991
}
80-
81-
switch (r.length) {
82-
case 2:
83-
write32le(loc, value);
84-
break;
85-
case 3:
86-
write64le(loc, value);
87-
break;
88-
default:
89-
llvm_unreachable("invalid r_length");
90-
}
9192
}
9293

9394
void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {

lld/MachO/Arch/ARM64Common.h

+32-25
Original file line numberDiff line numberDiff line change
@@ -40,39 +40,45 @@ inline uint64_t bitField(uint64_t value, int right, int width, int left) {
4040
// | | imm26 |
4141
// +-----------+---------------------------------------------------+
4242

43-
inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) {
44-
checkInt(r, va, 28);
43+
inline void encodeBranch26(uint32_t *loc, const Reloc &r, uint32_t base,
44+
uint64_t va) {
45+
checkInt(loc, r, va, 28);
4546
// Since branch destinations are 4-byte aligned, the 2 least-
4647
// significant bits are 0. They are right shifted off the end.
47-
return (base | bitField(va, 2, 26, 0));
48+
llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0));
4849
}
4950

50-
inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) {
51-
checkInt(d, va, 28);
52-
return (base | bitField(va, 2, 26, 0));
51+
inline void encodeBranch26(uint32_t *loc, SymbolDiagnostic d, uint32_t base,
52+
uint64_t va) {
53+
checkInt(loc, d, va, 28);
54+
llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0));
5355
}
5456

5557
// 30 29 23 5
5658
// +-+---+---------+-------------------------------------+---------+
5759
// | |ilo| | immhi | |
5860
// +-+---+---------+-------------------------------------+---------+
5961

60-
inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) {
61-
checkInt(r, va, 35);
62-
return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
62+
inline void encodePage21(uint32_t *loc, const Reloc &r, uint32_t base,
63+
uint64_t va) {
64+
checkInt(loc, r, va, 35);
65+
llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) |
66+
bitField(va, 14, 19, 5));
6367
}
6468

65-
inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) {
66-
checkInt(d, va, 35);
67-
return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
69+
inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base,
70+
uint64_t va) {
71+
checkInt(loc, d, va, 35);
72+
llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) |
73+
bitField(va, 14, 19, 5));
6874
}
6975

7076
// 21 10
7177
// +-------------------+-----------------------+-------------------+
7278
// | | imm12 | |
7379
// +-------------------+-----------------------+-------------------+
7480

75-
inline uint64_t encodePageOff12(uint32_t base, uint64_t va) {
81+
inline void encodePageOff12(uint32_t *loc, uint32_t base, uint64_t va) {
7682
int scale = 0;
7783
if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store
7884
scale = base >> 30;
@@ -82,7 +88,8 @@ inline uint64_t encodePageOff12(uint32_t base, uint64_t va) {
8288

8389
// TODO(gkm): extract embedded addend and warn if != 0
8490
// uint64_t addend = ((base & 0x003FFC00) >> 10);
85-
return (base | bitField(va, scale, 12 - scale, 10));
91+
llvm::support::endian::write32le(loc,
92+
base | bitField(va, scale, 12 - scale, 10));
8693
}
8794

8895
inline uint64_t pageBits(uint64_t address) {
@@ -99,9 +106,9 @@ inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3],
99106
pageBits(in.stubs->addr + sym.stubsIndex * stubCodeSize);
100107
uint64_t lazyPointerVA =
101108
in.lazyPointers->addr + sym.stubsIndex * LP::wordSize;
102-
buf32[0] = encodePage21({&sym, "stub"}, stubCode[0],
103-
pageBits(lazyPointerVA) - pcPageBits);
104-
buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA);
109+
encodePage21(&buf32[0], {&sym, "stub"}, stubCode[0],
110+
pageBits(lazyPointerVA) - pcPageBits);
111+
encodePageOff12(&buf32[1], stubCode[1], lazyPointerVA);
105112
buf32[2] = stubCode[2];
106113
}
107114

@@ -114,15 +121,15 @@ inline void writeStubHelperHeader(uint8_t *buf8,
114121
};
115122
uint64_t loaderVA = in.imageLoaderCache->getVA();
116123
SymbolDiagnostic d = {nullptr, "stub header helper"};
117-
buf32[0] = encodePage21(d, stubHelperHeaderCode[0],
118-
pageBits(loaderVA) - pcPageBits(0));
119-
buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA);
124+
encodePage21(&buf32[0], d, stubHelperHeaderCode[0],
125+
pageBits(loaderVA) - pcPageBits(0));
126+
encodePageOff12(&buf32[1], stubHelperHeaderCode[1], loaderVA);
120127
buf32[2] = stubHelperHeaderCode[2];
121128
uint64_t binderVA =
122129
in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize;
123-
buf32[3] = encodePage21(d, stubHelperHeaderCode[3],
124-
pageBits(binderVA) - pcPageBits(3));
125-
buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA);
130+
encodePage21(&buf32[3], d, stubHelperHeaderCode[3],
131+
pageBits(binderVA) - pcPageBits(3));
132+
encodePageOff12(&buf32[4], stubHelperHeaderCode[4], binderVA);
126133
buf32[5] = stubHelperHeaderCode[5];
127134
}
128135

@@ -133,8 +140,8 @@ inline void writeStubHelperEntry(uint8_t *buf8,
133140
auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); };
134141
uint64_t stubHelperHeaderVA = in.stubHelper->addr;
135142
buf32[0] = stubHelperEntryCode[0];
136-
buf32[1] = encodeBranch26({&sym, "stub helper"}, stubHelperEntryCode[1],
137-
stubHelperHeaderVA - pcVA(1));
143+
encodeBranch26(&buf32[1], {&sym, "stub helper"}, stubHelperEntryCode[1],
144+
stubHelperHeaderVA - pcVA(1));
138145
buf32[2] = sym.lazyBindOffset;
139146
}
140147

lld/MachO/Arch/X86_64.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
102102
switch (r.length) {
103103
case 2:
104104
if (r.type == X86_64_RELOC_UNSIGNED)
105-
checkUInt(r, value, 32);
105+
checkUInt(loc, r, value, 32);
106106
else
107-
checkInt(r, value, 32);
107+
checkInt(loc, r, value, 32);
108108
write32le(loc, value);
109109
break;
110110
case 3:
@@ -127,7 +127,7 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
127127
static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
128128
uint64_t bufOff, uint64_t destAddr) {
129129
uint64_t rip = bufAddr + bufOff;
130-
checkInt(d, destAddr - rip, 32);
130+
checkInt(buf, d, destAddr - rip, 32);
131131
// For the instructions we care about, the RIP-relative address is always
132132
// stored in the last 4 bytes of the instruction.
133133
write32le(buf + bufOff - 4, destAddr - rip);

lld/MachO/Relocations.cpp

+53-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Relocations.h"
10+
#include "ConcatOutputSection.h"
1011
#include "Symbols.h"
1112
#include "SyntheticSections.h"
1213
#include "Target.h"
@@ -38,19 +39,65 @@ bool macho::validateSymbolRelocation(const Symbol *sym,
3839
return valid;
3940
}
4041
41-
void macho::reportRangeError(const Reloc &r, const Twine &v, uint8_t bits,
42-
int64_t min, uint64_t max) {
42+
// Given an offset in the output buffer, figure out which ConcatInputSection (if
43+
// any) maps to it. At the same time, update the offset such that it is relative
44+
// to the InputSection rather than to the output buffer.
45+
//
46+
// Obtaining the InputSection allows us to have better error diagnostics.
47+
// However, many of our relocation-handling methods do not take the InputSection
48+
// as a parameter. Since we are already passing the buffer offsets to our Target
49+
// methods, this function allows us to emit better errors without threading an
50+
// additional InputSection argument through the call stack.
51+
//
52+
// This is implemented as a slow linear search through OutputSegments,
53+
// OutputSections, and finally the InputSections themselves. However, this
54+
// function should be called only on error paths, so some overhead is fine.
55+
static InputSection *offsetToInputSection(uint64_t *off) {
56+
for (OutputSegment *seg : outputSegments) {
57+
if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize)
58+
continue;
59+
60+
const std::vector<OutputSection *> &sections = seg->getSections();
61+
size_t osecIdx = 0;
62+
for (; osecIdx < sections.size(); ++osecIdx)
63+
if (*off < sections[osecIdx]->fileOff)
64+
break;
65+
assert(osecIdx > 0);
66+
// We should be only calling this function on offsets that belong to
67+
// ConcatOutputSections.
68+
auto *osec = cast<ConcatOutputSection>(sections[osecIdx - 1]);
69+
*off -= osec->fileOff;
70+
71+
size_t isecIdx = 0;
72+
for (; isecIdx < osec->inputs.size(); ++isecIdx) {
73+
const ConcatInputSection *isec = osec->inputs[isecIdx];
74+
if (*off < isec->outSecOff)
75+
break;
76+
}
77+
assert(isecIdx > 0);
78+
ConcatInputSection *isec = osec->inputs[isecIdx - 1];
79+
*off -= isec->outSecOff;
80+
return isec;
81+
}
82+
return nullptr;
83+
}
84+
85+
void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v,
86+
uint8_t bits, int64_t min, uint64_t max) {
4387
std::string hint;
88+
uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
89+
const InputSection *isec = offsetToInputSection(&off);
90+
std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
4491
if (auto *sym = r.referent.dyn_cast<Symbol *>())
4592
hint = "; references " + toString(*sym);
46-
// TODO: get location of reloc using something like LLD-ELF's getErrorPlace()
47-
error("relocation " + target->getRelocAttrs(r.type).name +
93+
error(locStr + ": relocation " + target->getRelocAttrs(r.type).name +
4894
" is out of range: " + v + " is not in [" + Twine(min) + ", " +
4995
Twine(max) + "]" + hint);
5096
}
5197
52-
void macho::reportRangeError(SymbolDiagnostic d, const Twine &v, uint8_t bits,
53-
int64_t min, uint64_t max) {
98+
void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v,
99+
uint8_t bits, int64_t min, uint64_t max) {
100+
// FIXME: should we use `loc` somehow to provide a better error message?
54101
std::string hint;
55102
if (d.symbol)
56103
hint = "; references " + toString(*d.symbol);

lld/MachO/Relocations.h

+8-8
Original file line numberDiff line numberDiff line change
@@ -70,28 +70,28 @@ bool validateSymbolRelocation(const Symbol *, const InputSection *,
7070
* v: The value the relocation is attempting to encode
7171
* bits: The number of bits actually available to encode this relocation
7272
*/
73-
void reportRangeError(const Reloc &, const llvm::Twine &v, uint8_t bits,
74-
int64_t min, uint64_t max);
73+
void reportRangeError(void *loc, const Reloc &, const llvm::Twine &v,
74+
uint8_t bits, int64_t min, uint64_t max);
7575

7676
struct SymbolDiagnostic {
7777
const Symbol *symbol;
7878
llvm::StringRef reason;
7979
};
8080

81-
void reportRangeError(SymbolDiagnostic, const llvm::Twine &v, uint8_t bits,
82-
int64_t min, uint64_t max);
81+
void reportRangeError(void *loc, SymbolDiagnostic, const llvm::Twine &v,
82+
uint8_t bits, int64_t min, uint64_t max);
8383

8484
template <typename Diagnostic>
85-
inline void checkInt(Diagnostic d, int64_t v, int bits) {
85+
inline void checkInt(void *loc, Diagnostic d, int64_t v, int bits) {
8686
if (v != llvm::SignExtend64(v, bits))
87-
reportRangeError(d, llvm::Twine(v), bits, llvm::minIntN(bits),
87+
reportRangeError(loc, d, llvm::Twine(v), bits, llvm::minIntN(bits),
8888
llvm::maxIntN(bits));
8989
}
9090

9191
template <typename Diagnostic>
92-
inline void checkUInt(Diagnostic d, uint64_t v, int bits) {
92+
inline void checkUInt(void *loc, Diagnostic d, uint64_t v, int bits) {
9393
if ((v >> bits) != 0)
94-
reportRangeError(d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits));
94+
reportRangeError(loc, d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits));
9595
}
9696

9797
inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) {

lld/MachO/SyntheticSections.h

+1
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ class WordLiteralSection final : public SyntheticSection {
595595
};
596596

597597
struct InStruct {
598+
const uint8_t *bufferStart = nullptr;
598599
MachHeaderSection *header = nullptr;
599600
CStringSection *cStringSection = nullptr;
600601
WordLiteralSection *wordLiteralSection = nullptr;

lld/MachO/Writer.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -1048,10 +1048,10 @@ void Writer::openFile() {
10481048
FileOutputBuffer::F_executable);
10491049

10501050
if (!bufferOrErr)
1051-
error("failed to open " + config->outputFile + ": " +
1051+
fatal("failed to open " + config->outputFile + ": " +
10521052
llvm::toString(bufferOrErr.takeError()));
1053-
else
1054-
buffer = std::move(*bufferOrErr);
1053+
buffer = std::move(*bufferOrErr);
1054+
in.bufferStart = buffer->getBufferStart();
10551055
}
10561056

10571057
void Writer::writeSections() {

lld/test/MachO/invalid/range-check.s

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
# RUN: %lld -dylib %t/bar.o -o %t/libbar.dylib
77
# RUN: not %lld -lSystem -o /dev/null %t/libbar.dylib %t/test.o 2>&1 | FileCheck %s
88

9-
# CHECK: error: relocation UNSIGNED is out of range: [[#]] is not in [0, 4294967295]; references _foo
10-
# CHECK: error: relocation GOT_LOAD is out of range: [[#]] is not in [-2147483648, 2147483647]; references _foo
9+
# CHECK: error: {{.*}}test.o:(symbol _main+0xd): relocation UNSIGNED is out of range: [[#]] is not in [0, 4294967295]; references _foo
10+
# CHECK: error: {{.*}}test.o:(symbol _main+0x3): relocation GOT_LOAD is out of range: [[#]] is not in [-2147483648, 2147483647]; references _foo
1111
# CHECK: error: stub is out of range: [[#]] is not in [-2147483648, 2147483647]; references _bar
1212
# CHECK: error: stub helper header is out of range: [[#]] is not in [-2147483648, 2147483647]
1313
# CHECK: error: stub helper header is out of range: [[#]] is not in [-2147483648, 2147483647]

0 commit comments

Comments
 (0)