Skip to content

Commit dc8bee9

Browse files
committed
[lld-macho] Check address ranges when applying relocations
This diff required fixing `getEmbeddedAddend` to apply sign extension to 32-bit values. We were previously passing around wrong 64-bit addend values that became "right" after being truncated back to 32-bit. I've also made `getEmbeddedAddend` return a signed int, which is similar to what LLD-ELF does for its `getImplicitAddend`. `reportRangeError`, `checkUInt`, and `checkInt` are counterparts of similar functions in LLD-ELF. Reviewed By: #lld-macho, thakis Differential Revision: https://reviews.llvm.org/D98387
1 parent 7b5ab95 commit dc8bee9

File tree

7 files changed

+139
-34
lines changed

7 files changed

+139
-34
lines changed

lld/MachO/Arch/ARM64.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ namespace {
2828
struct ARM64 : TargetInfo {
2929
ARM64();
3030

31-
uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
32-
const relocation_info) const override;
31+
int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
32+
const relocation_info) const override;
3333
void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
3434
uint64_t pc) const override;
3535

@@ -77,8 +77,8 @@ const RelocAttrs &ARM64::getRelocAttrs(uint8_t type) const {
7777
return relocAttrsArray[type];
7878
}
7979

80-
uint64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
81-
const relocation_info rel) const {
80+
int64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
81+
const relocation_info rel) const {
8282
if (rel.r_type != ARM64_RELOC_UNSIGNED &&
8383
rel.r_type != ARM64_RELOC_SUBTRACTOR) {
8484
// All other reloc types should use the ADDEND relocation to store their
@@ -91,7 +91,7 @@ uint64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
9191
const uint8_t *loc = buf + sec.offset + rel.r_address;
9292
switch (rel.r_length) {
9393
case 2:
94-
return read32le(loc);
94+
return static_cast<int32_t>(read32le(loc));
9595
case 3:
9696
return read64le(loc);
9797
default:
@@ -108,18 +108,30 @@ inline uint64_t bitField(uint64_t value, int right, int width, int left) {
108108
// | | imm26 |
109109
// +-----------+---------------------------------------------------+
110110

111-
inline uint64_t encodeBranch26(uint64_t base, uint64_t va) {
111+
inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) {
112+
checkInt(r, va, 28);
112113
// Since branch destinations are 4-byte aligned, the 2 least-
113114
// significant bits are 0. They are right shifted off the end.
114115
return (base | bitField(va, 2, 26, 0));
115116
}
116117

118+
inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) {
119+
checkInt(d, va, 28);
120+
return (base | bitField(va, 2, 26, 0));
121+
}
122+
117123
// 30 29 23 5
118124
// +-+---+---------+-------------------------------------+---------+
119125
// | |ilo| | immhi | |
120126
// +-+---+---------+-------------------------------------+---------+
121127

122-
inline uint64_t encodePage21(uint64_t base, uint64_t va) {
128+
inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) {
129+
checkInt(r, va, 35);
130+
return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
131+
}
132+
133+
inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) {
134+
checkInt(d, va, 35);
123135
return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
124136
}
125137

@@ -158,21 +170,25 @@ void ARM64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
158170
value += r.addend;
159171
switch (r.type) {
160172
case ARM64_RELOC_BRANCH26:
161-
value = encodeBranch26(base, value - pc);
173+
value = encodeBranch26(r, base, value - pc);
162174
break;
163175
case ARM64_RELOC_SUBTRACTOR:
164176
case ARM64_RELOC_UNSIGNED:
177+
if (r.length == 2)
178+
checkInt(r, value, 32);
165179
break;
166180
case ARM64_RELOC_POINTER_TO_GOT:
167181
if (r.pcrel)
168182
value -= pc;
183+
checkInt(r, value, 32);
169184
break;
170185
case ARM64_RELOC_PAGE21:
171186
case ARM64_RELOC_GOT_LOAD_PAGE21:
172-
case ARM64_RELOC_TLVP_LOAD_PAGE21:
187+
case ARM64_RELOC_TLVP_LOAD_PAGE21: {
173188
assert(r.pcrel);
174-
value = encodePage21(base, pageBits(value) - pageBits(pc));
189+
value = encodePage21(r, base, pageBits(value) - pageBits(pc));
175190
break;
191+
}
176192
case ARM64_RELOC_PAGEOFF12:
177193
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
178194
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
@@ -206,7 +222,8 @@ void ARM64::writeStub(uint8_t *buf8, const macho::Symbol &sym) const {
206222
uint64_t pcPageBits =
207223
pageBits(in.stubs->addr + sym.stubsIndex * sizeof(stubCode));
208224
uint64_t lazyPointerVA = in.lazyPointers->addr + sym.stubsIndex * WordSize;
209-
buf32[0] = encodePage21(stubCode[0], pageBits(lazyPointerVA) - pcPageBits);
225+
buf32[0] = encodePage21({&sym, "stub"}, stubCode[0],
226+
pageBits(lazyPointerVA) - pcPageBits);
210227
buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA);
211228
buf32[2] = stubCode[2];
212229
}
@@ -226,14 +243,15 @@ void ARM64::writeStubHelperHeader(uint8_t *buf8) const {
226243
return pageBits(in.stubHelper->addr + i * sizeof(uint32_t));
227244
};
228245
uint64_t loaderVA = in.imageLoaderCache->getVA();
229-
buf32[0] =
230-
encodePage21(stubHelperHeaderCode[0], pageBits(loaderVA) - pcPageBits(0));
246+
SymbolDiagnostic d = {nullptr, "stub header helper"};
247+
buf32[0] = encodePage21(d, stubHelperHeaderCode[0],
248+
pageBits(loaderVA) - pcPageBits(0));
231249
buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA);
232250
buf32[2] = stubHelperHeaderCode[2];
233251
uint64_t binderVA =
234252
in.got->addr + in.stubHelper->stubBinder->gotIndex * WordSize;
235-
buf32[3] =
236-
encodePage21(stubHelperHeaderCode[3], pageBits(binderVA) - pcPageBits(3));
253+
buf32[3] = encodePage21(d, stubHelperHeaderCode[3],
254+
pageBits(binderVA) - pcPageBits(3));
237255
buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA);
238256
buf32[5] = stubHelperHeaderCode[5];
239257
}
@@ -250,8 +268,8 @@ void ARM64::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym,
250268
auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); };
251269
uint64_t stubHelperHeaderVA = in.stubHelper->addr;
252270
buf32[0] = stubHelperEntryCode[0];
253-
buf32[1] =
254-
encodeBranch26(stubHelperEntryCode[1], stubHelperHeaderVA - pcVA(1));
271+
buf32[1] = encodeBranch26({&sym, "stub helper"}, stubHelperEntryCode[1],
272+
stubHelperHeaderVA - pcVA(1));
255273
buf32[2] = sym.lazyBindOffset;
256274
}
257275

lld/MachO/Arch/X86_64.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace {
2525
struct X86_64 : TargetInfo {
2626
X86_64();
2727

28-
uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
29-
const relocation_info) const override;
28+
int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
29+
const relocation_info) const override;
3030
void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
3131
uint64_t relocVA) const override;
3232

@@ -77,14 +77,14 @@ static int pcrelOffset(uint8_t type) {
7777
}
7878
}
7979

80-
uint64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
81-
relocation_info rel) const {
80+
int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
81+
relocation_info rel) const {
8282
auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
8383
const uint8_t *loc = buf + sec.offset + rel.r_address;
8484

8585
switch (rel.r_length) {
8686
case 2:
87-
return read32le(loc) + pcrelOffset(rel.r_type);
87+
return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);
8888
case 3:
8989
return read64le(loc) + pcrelOffset(rel.r_type);
9090
default:
@@ -102,6 +102,10 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
102102

103103
switch (r.length) {
104104
case 2:
105+
if (r.type == X86_64_RELOC_UNSIGNED)
106+
checkUInt(r, value, 32);
107+
else
108+
checkInt(r, value, 32);
105109
write32le(loc, value);
106110
break;
107111
case 3:
@@ -121,9 +125,10 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
121125
// bufAddr: The virtual address corresponding to buf[0].
122126
// bufOff: The offset within buf of the next instruction.
123127
// destAddr: The destination address that the current instruction references.
124-
static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff,
125-
uint64_t destAddr) {
128+
static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
129+
uint64_t bufOff, uint64_t destAddr) {
126130
uint64_t rip = bufAddr + bufOff;
131+
checkInt(d, destAddr - rip, 32);
127132
// For the instructions we care about, the RIP-relative address is always
128133
// stored in the last 4 bytes of the instruction.
129134
write32le(buf + bufOff - 4, destAddr - rip);
@@ -136,7 +141,7 @@ static constexpr uint8_t stub[] = {
136141
void X86_64::writeStub(uint8_t *buf, const macho::Symbol &sym) const {
137142
memcpy(buf, stub, 2); // just copy the two nonzero bytes
138143
uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
139-
writeRipRelative(buf, stubAddr, sizeof(stub),
144+
writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub),
140145
in.lazyPointers->addr + sym.stubsIndex * WordSize);
141146
}
142147

@@ -149,8 +154,10 @@ static constexpr uint8_t stubHelperHeader[] = {
149154

150155
void X86_64::writeStubHelperHeader(uint8_t *buf) const {
151156
memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
152-
writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA());
153-
writeRipRelative(buf, in.stubHelper->addr, 0xf,
157+
SymbolDiagnostic d = {nullptr, "stub helper header"};
158+
writeRipRelative(d, buf, in.stubHelper->addr, 7,
159+
in.imageLoaderCache->getVA());
160+
writeRipRelative(d, buf, in.stubHelper->addr, 0xf,
154161
in.got->addr +
155162
in.stubHelper->stubBinder->gotIndex * WordSize);
156163
}
@@ -164,8 +171,8 @@ void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
164171
uint64_t entryAddr) const {
165172
memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
166173
write32le(buf + 1, sym.lazyBindOffset);
167-
writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry),
168-
in.stubHelper->addr);
174+
writeRipRelative({&sym, "stub helper"}, buf, entryAddr,
175+
sizeof(stubHelperEntry), in.stubHelper->addr);
169176
}
170177

171178
void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {

lld/MachO/InputFiles.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ void ObjFile::parseRelocations(const section_64 &sec,
264264
// and insert them. Storing addends in the instruction stream is
265265
// possible, but inconvenient and more costly at link time.
266266

267-
uint64_t pairedAddend = 0;
267+
int64_t pairedAddend = 0;
268268
relocation_info relInfo = relInfos[i];
269269
if (target->hasAttr(relInfo.r_type, RelocAttrBits::ADDEND)) {
270270
pairedAddend = SignExtend64<24>(relInfo.r_symbolnum);
@@ -276,9 +276,9 @@ void ObjFile::parseRelocations(const section_64 &sec,
276276
if (relInfo.r_address & R_SCATTERED)
277277
fatal("TODO: Scattered relocations not supported");
278278

279-
uint64_t embeddedAddend = target->getEmbeddedAddend(mb, sec, relInfo);
279+
int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec, relInfo);
280280
assert(!(embeddedAddend && pairedAddend));
281-
uint64_t totalAddend = pairedAddend + embeddedAddend;
281+
int64_t totalAddend = pairedAddend + embeddedAddend;
282282
Reloc r;
283283
r.type = relInfo.r_type;
284284
r.pcrel = relInfo.r_pcrel;

lld/MachO/Relocations.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,24 @@ bool macho::validateSymbolRelocation(const Symbol *sym,
3939
return valid;
4040
}
4141

42+
void macho::reportRangeError(const Reloc &r, const Twine &v, uint8_t bits,
43+
int64_t min, uint64_t max) {
44+
std::string hint;
45+
if (auto *sym = r.referent.dyn_cast<Symbol *>())
46+
hint = "; references " + toString(*sym);
47+
// TODO: get location of reloc using something like LLD-ELF's getErrorPlace()
48+
error("relocation " + target->getRelocAttrs(r.type).name +
49+
" is out of range: " + v + " is not in [" + Twine(min) + ", " +
50+
Twine(max) + "]" + hint);
51+
}
52+
53+
void macho::reportRangeError(SymbolDiagnostic d, const Twine &v, uint8_t bits,
54+
int64_t min, uint64_t max) {
55+
std::string hint;
56+
if (d.symbol)
57+
hint = "; references " + toString(*d.symbol);
58+
error(d.reason + " is out of range: " + v + " is not in [" + Twine(min) +
59+
", " + Twine(max) + "]" + hint);
60+
}
61+
4262
const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0};

lld/MachO/Relocations.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,41 @@ struct Reloc {
5959
uint32_t offset = 0;
6060
// Adding this offset to the address of the referent symbol or subsection
6161
// gives the destination that this relocation refers to.
62-
uint64_t addend = 0;
62+
int64_t addend = 0;
6363
llvm::PointerUnion<Symbol *, InputSection *> referent = nullptr;
6464
};
6565

6666
bool validateSymbolRelocation(const Symbol *, const InputSection *,
6767
const Reloc &);
6868

69+
/*
70+
* v: The value the relocation is attempting to encode
71+
* bits: The number of bits actually available to encode this relocation
72+
*/
73+
void reportRangeError(const Reloc &, const llvm::Twine &v, uint8_t bits,
74+
int64_t min, uint64_t max);
75+
76+
struct SymbolDiagnostic {
77+
const Symbol *symbol;
78+
llvm::StringRef reason;
79+
};
80+
81+
void reportRangeError(SymbolDiagnostic, const llvm::Twine &v, uint8_t bits,
82+
int64_t min, uint64_t max);
83+
84+
template <typename Diagnostic>
85+
inline void checkInt(Diagnostic d, int64_t v, int bits) {
86+
if (v != llvm::SignExtend64(v, bits))
87+
reportRangeError(d, llvm::Twine(v), bits, llvm::minIntN(bits),
88+
llvm::maxIntN(bits));
89+
}
90+
91+
template <typename Diagnostic>
92+
inline void checkUInt(Diagnostic d, uint64_t v, int bits) {
93+
if ((v >> bits) != 0)
94+
reportRangeError(d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits));
95+
}
96+
6997
extern const RelocAttrs invalidRelocAttrs;
7098

7199
} // namespace macho

lld/MachO/Target.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class TargetInfo {
3939
virtual ~TargetInfo() = default;
4040

4141
// Validate the relocation structure and get its addend.
42-
virtual uint64_t
42+
virtual int64_t
4343
getEmbeddedAddend(llvm::MemoryBufferRef, const llvm::MachO::section_64 &,
4444
const llvm::MachO::relocation_info) const = 0;
4545
virtual void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# REQUIRES: x86
2+
3+
# RUN: split-file %s %t
4+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/bar.s -o %t/bar.o
6+
# RUN: %lld -dylib %t/bar.o -o %t/libbar.dylib
7+
# RUN: not %lld -lSystem -o /dev/null %t/libbar.dylib %t/test.o 2>&1 | FileCheck %s
8+
9+
# CHECK: error: relocation UNSIGNED is out of range: 8589942792 is not in [0, 4294967295]; references _foo
10+
# CHECK: error: relocation GOT_LOAD is out of range: 4294974033 is not in [-2147483648, 2147483647]; references _foo
11+
# CHECK: error: stub is out of range: 4294974006 is not in [-2147483648, 2147483647]; references _bar
12+
# CHECK: error: stub helper header is out of range: 4294974005 is not in [-2147483648, 2147483647]
13+
# CHECK: error: stub helper header is out of range: 4294969893 is not in [-2147483648, 2147483647]
14+
15+
#--- bar.s
16+
.globl _bar
17+
_bar:
18+
19+
#--- test.s
20+
.globl _main, _foo
21+
22+
_main:
23+
movq _foo@GOTPCREL(%rip), %rax
24+
callq _bar
25+
ret
26+
27+
.int _foo
28+
.zerofill __TEXT,bss,_zero,0xffffffff
29+
30+
.data
31+
_foo:
32+
.space 0

0 commit comments

Comments
 (0)