1 //===- Thunks.cpp --------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This file contains Thunk subclasses.
10 //
11 // A thunk is a small piece of code written after an input section
12 // which is used to jump between "incompatible" functions
13 // such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions.
14 //
15 // If a jump target is too far and its address doesn't fit to a
16 // short jump instruction, we need to create a thunk too, but we
17 // haven't supported it yet.
18 //
19 // i386 and x86-64 don't need thunks.
20 //
21 //===---------------------------------------------------------------------===//
22
23 #include "Thunks.h"
24 #include "Config.h"
25 #include "InputFiles.h"
26 #include "InputSection.h"
27 #include "OutputSections.h"
28 #include "Symbols.h"
29 #include "SyntheticSections.h"
30 #include "Target.h"
31 #include "lld/Common/CommonLinkerContext.h"
32 #include "llvm/BinaryFormat/ELF.h"
33 #include "llvm/Support/Casting.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include "llvm/Support/MathExtras.h"
36 #include <cstdint>
37 #include <cstring>
38
39 using namespace llvm;
40 using namespace llvm::object;
41 using namespace llvm::ELF;
42 using namespace lld;
43 using namespace lld::elf;
44
45 namespace {
46
47 // AArch64 long range Thunks
48 class AArch64ABSLongThunk final : public Thunk {
49 public:
AArch64ABSLongThunk(Symbol & dest,int64_t addend)50 AArch64ABSLongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
size()51 uint32_t size() override { return 16; }
52 void writeTo(uint8_t *buf) override;
53 void addSymbols(ThunkSection &isec) override;
54 };
55
56 class AArch64ADRPThunk final : public Thunk {
57 public:
AArch64ADRPThunk(Symbol & dest,int64_t addend)58 AArch64ADRPThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
size()59 uint32_t size() override { return 12; }
60 void writeTo(uint8_t *buf) override;
61 void addSymbols(ThunkSection &isec) override;
62 };
63
64 // Base class for ARM thunks.
65 //
66 // An ARM thunk may be either short or long. A short thunk is simply a branch
67 // (B) instruction, and it may be used to call ARM functions when the distance
68 // from the thunk to the target is less than 32MB. Long thunks can branch to any
69 // virtual address and can switch between ARM and Thumb, and they are
70 // implemented in the derived classes. This class tries to create a short thunk
71 // if the target is in range, otherwise it creates a long thunk.
72 class ARMThunk : public Thunk {
73 public:
ARMThunk(Symbol & dest,int64_t addend)74 ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
75
76 bool getMayUseShortThunk();
size()77 uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
78 void writeTo(uint8_t *buf) override;
79 bool isCompatibleWith(const InputSection &isec,
80 const Relocation &rel) const override;
81
82 // Returns the size of a long thunk.
83 virtual uint32_t sizeLong() = 0;
84
85 // Writes a long thunk to Buf.
86 virtual void writeLong(uint8_t *buf) = 0;
87
88 private:
89 // This field tracks whether all previously considered layouts would allow
90 // this thunk to be short. If we have ever needed a long thunk, we always
91 // create a long thunk, even if the thunk may be short given the current
92 // distance to the target. We do this because transitioning from long to short
93 // can create layout oscillations in certain corner cases which would prevent
94 // the layout from converging.
95 bool mayUseShortThunk = true;
96 };
97
98 // Base class for Thumb-2 thunks.
99 //
100 // This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
101 // which has a range of 16MB.
102 class ThumbThunk : public Thunk {
103 public:
ThumbThunk(Symbol & dest,int64_t addend)104 ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
105 alignment = 2;
106 }
107
108 bool getMayUseShortThunk();
size()109 uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
110 void writeTo(uint8_t *buf) override;
111 bool isCompatibleWith(const InputSection &isec,
112 const Relocation &rel) const override;
113
114 // Returns the size of a long thunk.
115 virtual uint32_t sizeLong() = 0;
116
117 // Writes a long thunk to Buf.
118 virtual void writeLong(uint8_t *buf) = 0;
119
120 private:
121 // See comment in ARMThunk above.
122 bool mayUseShortThunk = true;
123 };
124
125 // Specific ARM Thunk implementations. The naming convention is:
126 // Source State, TargetState, Target Requirement, ABS or PI, Range
127 class ARMV7ABSLongThunk final : public ARMThunk {
128 public:
ARMV7ABSLongThunk(Symbol & dest,int64_t addend)129 ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
130
sizeLong()131 uint32_t sizeLong() override { return 12; }
132 void writeLong(uint8_t *buf) override;
133 void addSymbols(ThunkSection &isec) override;
134 };
135
136 class ARMV7PILongThunk final : public ARMThunk {
137 public:
ARMV7PILongThunk(Symbol & dest,int64_t addend)138 ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
139
sizeLong()140 uint32_t sizeLong() override { return 16; }
141 void writeLong(uint8_t *buf) override;
142 void addSymbols(ThunkSection &isec) override;
143 };
144
145 class ThumbV7ABSLongThunk final : public ThumbThunk {
146 public:
ThumbV7ABSLongThunk(Symbol & dest,int64_t addend)147 ThumbV7ABSLongThunk(Symbol &dest, int64_t addend)
148 : ThumbThunk(dest, addend) {}
149
sizeLong()150 uint32_t sizeLong() override { return 10; }
151 void writeLong(uint8_t *buf) override;
152 void addSymbols(ThunkSection &isec) override;
153 };
154
155 class ThumbV7PILongThunk final : public ThumbThunk {
156 public:
ThumbV7PILongThunk(Symbol & dest,int64_t addend)157 ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {}
158
sizeLong()159 uint32_t sizeLong() override { return 12; }
160 void writeLong(uint8_t *buf) override;
161 void addSymbols(ThunkSection &isec) override;
162 };
163
164 // Implementations of Thunks for older Arm architectures that do not support
165 // the movt/movw instructions. These thunks require at least Architecture v5
166 // as used on processors such as the Arm926ej-s. There are no Thumb entry
167 // points as there is no Thumb branch instruction on these architecture that
168 // can result in a thunk
169 class ARMV5ABSLongThunk final : public ARMThunk {
170 public:
ARMV5ABSLongThunk(Symbol & dest,int64_t addend)171 ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
172
sizeLong()173 uint32_t sizeLong() override { return 8; }
174 void writeLong(uint8_t *buf) override;
175 void addSymbols(ThunkSection &isec) override;
176 bool isCompatibleWith(const InputSection &isec,
177 const Relocation &rel) const override;
178 };
179
180 class ARMV5PILongThunk final : public ARMThunk {
181 public:
ARMV5PILongThunk(Symbol & dest,int64_t addend)182 ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
183
sizeLong()184 uint32_t sizeLong() override { return 16; }
185 void writeLong(uint8_t *buf) override;
186 void addSymbols(ThunkSection &isec) override;
187 bool isCompatibleWith(const InputSection &isec,
188 const Relocation &rel) const override;
189 };
190
191 // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
192 class ThumbV6MABSLongThunk final : public ThumbThunk {
193 public:
ThumbV6MABSLongThunk(Symbol & dest,int64_t addend)194 ThumbV6MABSLongThunk(Symbol &dest, int64_t addend)
195 : ThumbThunk(dest, addend) {}
196
sizeLong()197 uint32_t sizeLong() override { return 12; }
198 void writeLong(uint8_t *buf) override;
199 void addSymbols(ThunkSection &isec) override;
200 };
201
202 class ThumbV6MPILongThunk final : public ThumbThunk {
203 public:
ThumbV6MPILongThunk(Symbol & dest,int64_t addend)204 ThumbV6MPILongThunk(Symbol &dest, int64_t addend)
205 : ThumbThunk(dest, addend) {}
206
sizeLong()207 uint32_t sizeLong() override { return 16; }
208 void writeLong(uint8_t *buf) override;
209 void addSymbols(ThunkSection &isec) override;
210 };
211
212 // MIPS LA25 thunk
213 class MipsThunk final : public Thunk {
214 public:
MipsThunk(Symbol & dest)215 MipsThunk(Symbol &dest) : Thunk(dest, 0) {}
216
size()217 uint32_t size() override { return 16; }
218 void writeTo(uint8_t *buf) override;
219 void addSymbols(ThunkSection &isec) override;
220 InputSection *getTargetInputSection() const override;
221 };
222
223 // microMIPS R2-R5 LA25 thunk
224 class MicroMipsThunk final : public Thunk {
225 public:
MicroMipsThunk(Symbol & dest)226 MicroMipsThunk(Symbol &dest) : Thunk(dest, 0) {}
227
size()228 uint32_t size() override { return 14; }
229 void writeTo(uint8_t *buf) override;
230 void addSymbols(ThunkSection &isec) override;
231 InputSection *getTargetInputSection() const override;
232 };
233
234 // microMIPS R6 LA25 thunk
235 class MicroMipsR6Thunk final : public Thunk {
236 public:
MicroMipsR6Thunk(Symbol & dest)237 MicroMipsR6Thunk(Symbol &dest) : Thunk(dest, 0) {}
238
size()239 uint32_t size() override { return 12; }
240 void writeTo(uint8_t *buf) override;
241 void addSymbols(ThunkSection &isec) override;
242 InputSection *getTargetInputSection() const override;
243 };
244
245 class PPC32PltCallStub final : public Thunk {
246 public:
247 // For R_PPC_PLTREL24, Thunk::addend records the addend which will be used to
248 // decide the offsets in the call stub.
PPC32PltCallStub(const InputSection & isec,const Relocation & rel,Symbol & dest)249 PPC32PltCallStub(const InputSection &isec, const Relocation &rel,
250 Symbol &dest)
251 : Thunk(dest, rel.addend), file(isec.file) {}
size()252 uint32_t size() override { return 16; }
253 void writeTo(uint8_t *buf) override;
254 void addSymbols(ThunkSection &isec) override;
255 bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override;
256
257 private:
258 // Records the call site of the call stub.
259 const InputFile *file;
260 };
261
262 class PPC32LongThunk final : public Thunk {
263 public:
PPC32LongThunk(Symbol & dest,int64_t addend)264 PPC32LongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
size()265 uint32_t size() override { return config->isPic ? 32 : 16; }
266 void writeTo(uint8_t *buf) override;
267 void addSymbols(ThunkSection &isec) override;
268 };
269
270 // PPC64 Plt call stubs.
271 // Any call site that needs to call through a plt entry needs a call stub in
272 // the .text section. The call stub is responsible for:
273 // 1) Saving the toc-pointer to the stack.
274 // 2) Loading the target functions address from the procedure linkage table into
275 // r12 for use by the target functions global entry point, and into the count
276 // register.
277 // 3) Transferring control to the target function through an indirect branch.
278 class PPC64PltCallStub final : public Thunk {
279 public:
PPC64PltCallStub(Symbol & dest)280 PPC64PltCallStub(Symbol &dest) : Thunk(dest, 0) {}
size()281 uint32_t size() override { return 20; }
282 void writeTo(uint8_t *buf) override;
283 void addSymbols(ThunkSection &isec) override;
284 bool isCompatibleWith(const InputSection &isec,
285 const Relocation &rel) const override;
286 };
287
288 // PPC64 R2 Save Stub
289 // When the caller requires a valid R2 TOC pointer but the callee does not
290 // require a TOC pointer and the callee cannot guarantee that it doesn't
291 // clobber R2 then we need to save R2. This stub:
292 // 1) Saves the TOC pointer to the stack.
293 // 2) Tail calls the callee.
294 class PPC64R2SaveStub final : public Thunk {
295 public:
PPC64R2SaveStub(Symbol & dest,int64_t addend)296 PPC64R2SaveStub(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
297 alignment = 16;
298 }
299
300 // To prevent oscillations in layout when moving from short to long thunks
301 // we make sure that once a thunk has been set to long it cannot go back.
getMayUseShortThunk()302 bool getMayUseShortThunk() {
303 if (!mayUseShortThunk)
304 return false;
305 if (!isInt<26>(computeOffset())) {
306 mayUseShortThunk = false;
307 return false;
308 }
309 return true;
310 }
size()311 uint32_t size() override { return getMayUseShortThunk() ? 8 : 32; }
312 void writeTo(uint8_t *buf) override;
313 void addSymbols(ThunkSection &isec) override;
314 bool isCompatibleWith(const InputSection &isec,
315 const Relocation &rel) const override;
316
317 private:
318 // Transitioning from long to short can create layout oscillations in
319 // certain corner cases which would prevent the layout from converging.
320 // This is similar to the handling for ARMThunk.
321 bool mayUseShortThunk = true;
computeOffset() const322 int64_t computeOffset() const {
323 return destination.getVA() - (getThunkTargetSym()->getVA() + 4);
324 }
325 };
326
327 // PPC64 R12 Setup Stub
328 // When a caller that does not maintain a toc-pointer performs a local call to
329 // a callee which requires a toc-pointer then we need this stub to place the
330 // callee's global entry point into r12 without a save of R2.
331 class PPC64R12SetupStub final : public Thunk {
332 public:
PPC64R12SetupStub(Symbol & dest)333 PPC64R12SetupStub(Symbol &dest) : Thunk(dest, 0) { alignment = 16; }
size()334 uint32_t size() override { return 32; }
335 void writeTo(uint8_t *buf) override;
336 void addSymbols(ThunkSection &isec) override;
337 bool isCompatibleWith(const InputSection &isec,
338 const Relocation &rel) const override;
339 };
340
341 // PPC64 PC-relative PLT Stub
342 // When a caller that does not maintain a toc-pointer performs an extern call
343 // then this stub is needed for:
344 // 1) Loading the target functions address from the procedure linkage table into
345 // r12 for use by the target functions global entry point, and into the count
346 // register with pc-relative instructions.
347 // 2) Transferring control to the target function through an indirect branch.
348 class PPC64PCRelPLTStub final : public Thunk {
349 public:
PPC64PCRelPLTStub(Symbol & dest)350 PPC64PCRelPLTStub(Symbol &dest) : Thunk(dest, 0) { alignment = 16; }
size()351 uint32_t size() override { return 32; }
352 void writeTo(uint8_t *buf) override;
353 void addSymbols(ThunkSection &isec) override;
354 bool isCompatibleWith(const InputSection &isec,
355 const Relocation &rel) const override;
356 };
357
358 // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
359 // alignment. This gives a possible 26 bits of 'reach'. If the call offset is
360 // larger than that we need to emit a long-branch thunk. The target address
361 // of the callee is stored in a table to be accessed TOC-relative. Since the
362 // call must be local (a non-local call will have a PltCallStub instead) the
363 // table stores the address of the callee's local entry point. For
364 // position-independent code a corresponding relative dynamic relocation is
365 // used.
366 class PPC64LongBranchThunk : public Thunk {
367 public:
size()368 uint32_t size() override { return 32; }
369 void writeTo(uint8_t *buf) override;
370 void addSymbols(ThunkSection &isec) override;
371 bool isCompatibleWith(const InputSection &isec,
372 const Relocation &rel) const override;
373
374 protected:
PPC64LongBranchThunk(Symbol & dest,int64_t addend)375 PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
376 };
377
378 class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
379 public:
PPC64PILongBranchThunk(Symbol & dest,int64_t addend)380 PPC64PILongBranchThunk(Symbol &dest, int64_t addend)
381 : PPC64LongBranchThunk(dest, addend) {
382 assert(!dest.isPreemptible);
383 if (Optional<uint32_t> index =
384 in.ppc64LongBranchTarget->addEntry(&dest, addend)) {
385 mainPart->relaDyn->addRelativeReloc(
386 target->relativeRel, *in.ppc64LongBranchTarget, *index * UINT64_C(8),
387 dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther),
388 target->symbolicRel, R_ABS);
389 }
390 }
391 };
392
393 class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
394 public:
PPC64PDLongBranchThunk(Symbol & dest,int64_t addend)395 PPC64PDLongBranchThunk(Symbol &dest, int64_t addend)
396 : PPC64LongBranchThunk(dest, addend) {
397 in.ppc64LongBranchTarget->addEntry(&dest, addend);
398 }
399 };
400
401 } // end anonymous namespace
402
addSymbol(StringRef name,uint8_t type,uint64_t value,InputSectionBase & section)403 Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
404 InputSectionBase §ion) {
405 Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section);
406 syms.push_back(d);
407 return d;
408 }
409
setOffset(uint64_t newOffset)410 void Thunk::setOffset(uint64_t newOffset) {
411 for (Defined *d : syms)
412 d->value = d->value - offset + newOffset;
413 offset = newOffset;
414 }
415
416 // AArch64 long range Thunks
417
getAArch64ThunkDestVA(const Symbol & s,int64_t a)418 static uint64_t getAArch64ThunkDestVA(const Symbol &s, int64_t a) {
419 uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(a);
420 return v;
421 }
422
writeTo(uint8_t * buf)423 void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
424 const uint8_t data[] = {
425 0x50, 0x00, 0x00, 0x58, // ldr x16, L0
426 0x00, 0x02, 0x1f, 0xd6, // br x16
427 0x00, 0x00, 0x00, 0x00, // L0: .xword S
428 0x00, 0x00, 0x00, 0x00,
429 };
430 uint64_t s = getAArch64ThunkDestVA(destination, addend);
431 memcpy(buf, data, sizeof(data));
432 target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s);
433 }
434
addSymbols(ThunkSection & isec)435 void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
436 addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()),
437 STT_FUNC, 0, isec);
438 addSymbol("$x", STT_NOTYPE, 0, isec);
439 addSymbol("$d", STT_NOTYPE, 8, isec);
440 }
441
442 // This Thunk has a maximum range of 4Gb, this is sufficient for all programs
443 // using the small code model, including pc-relative ones. At time of writing
444 // clang and gcc do not support the large code model for position independent
445 // code so it is safe to use this for position independent thunks without
446 // worrying about the destination being more than 4Gb away.
writeTo(uint8_t * buf)447 void AArch64ADRPThunk::writeTo(uint8_t *buf) {
448 const uint8_t data[] = {
449 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
450 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
451 0x00, 0x02, 0x1f, 0xd6, // br x16
452 };
453 uint64_t s = getAArch64ThunkDestVA(destination, addend);
454 uint64_t p = getThunkTargetSym()->getVA();
455 memcpy(buf, data, sizeof(data));
456 target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
457 getAArch64Page(s) - getAArch64Page(p));
458 target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
459 }
460
addSymbols(ThunkSection & isec)461 void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
462 addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()),
463 STT_FUNC, 0, isec);
464 addSymbol("$x", STT_NOTYPE, 0, isec);
465 }
466
467 // ARM Target Thunks
getARMThunkDestVA(const Symbol & s)468 static uint64_t getARMThunkDestVA(const Symbol &s) {
469 uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
470 return SignExtend64<32>(v);
471 }
472
473 // This function returns true if the target is not Thumb and is within 2^26, and
474 // it has not previously returned false (see comment for mayUseShortThunk).
getMayUseShortThunk()475 bool ARMThunk::getMayUseShortThunk() {
476 if (!mayUseShortThunk)
477 return false;
478 uint64_t s = getARMThunkDestVA(destination);
479 if (s & 1) {
480 mayUseShortThunk = false;
481 return false;
482 }
483 uint64_t p = getThunkTargetSym()->getVA();
484 int64_t offset = s - p - 8;
485 mayUseShortThunk = llvm::isInt<26>(offset);
486 return mayUseShortThunk;
487 }
488
writeTo(uint8_t * buf)489 void ARMThunk::writeTo(uint8_t *buf) {
490 if (!getMayUseShortThunk()) {
491 writeLong(buf);
492 return;
493 }
494
495 uint64_t s = getARMThunkDestVA(destination);
496 uint64_t p = getThunkTargetSym()->getVA();
497 int64_t offset = s - p - 8;
498 const uint8_t data[] = {
499 0x00, 0x00, 0x00, 0xea, // b S
500 };
501 memcpy(buf, data, sizeof(data));
502 target->relocateNoSym(buf, R_ARM_JUMP24, offset);
503 }
504
isCompatibleWith(const InputSection & isec,const Relocation & rel) const505 bool ARMThunk::isCompatibleWith(const InputSection &isec,
506 const Relocation &rel) const {
507 // Thumb branch relocations can't use BLX
508 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
509 }
510
511 // This function returns true if the target is Thumb and is within 2^25, and
512 // it has not previously returned false (see comment for mayUseShortThunk).
getMayUseShortThunk()513 bool ThumbThunk::getMayUseShortThunk() {
514 if (!mayUseShortThunk)
515 return false;
516 uint64_t s = getARMThunkDestVA(destination);
517 if ((s & 1) == 0) {
518 mayUseShortThunk = false;
519 return false;
520 }
521 uint64_t p = getThunkTargetSym()->getVA() & ~1;
522 int64_t offset = s - p - 4;
523 mayUseShortThunk = llvm::isInt<25>(offset);
524 return mayUseShortThunk;
525 }
526
writeTo(uint8_t * buf)527 void ThumbThunk::writeTo(uint8_t *buf) {
528 if (!getMayUseShortThunk()) {
529 writeLong(buf);
530 return;
531 }
532
533 uint64_t s = getARMThunkDestVA(destination);
534 uint64_t p = getThunkTargetSym()->getVA();
535 int64_t offset = s - p - 4;
536 const uint8_t data[] = {
537 0x00, 0xf0, 0x00, 0xb0, // b.w S
538 };
539 memcpy(buf, data, sizeof(data));
540 target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset);
541 }
542
isCompatibleWith(const InputSection & isec,const Relocation & rel) const543 bool ThumbThunk::isCompatibleWith(const InputSection &isec,
544 const Relocation &rel) const {
545 // ARM branch relocations can't use BLX
546 return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
547 }
548
writeLong(uint8_t * buf)549 void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
550 const uint8_t data[] = {
551 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
552 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
553 0x1c, 0xff, 0x2f, 0xe1, // bx ip
554 };
555 uint64_t s = getARMThunkDestVA(destination);
556 memcpy(buf, data, sizeof(data));
557 target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s);
558 target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s);
559 }
560
addSymbols(ThunkSection & isec)561 void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
562 addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()),
563 STT_FUNC, 0, isec);
564 addSymbol("$a", STT_NOTYPE, 0, isec);
565 }
566
writeLong(uint8_t * buf)567 void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
568 const uint8_t data[] = {
569 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
570 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
571 0x60, 0x47, // bx ip
572 };
573 uint64_t s = getARMThunkDestVA(destination);
574 memcpy(buf, data, sizeof(data));
575 target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s);
576 target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s);
577 }
578
addSymbols(ThunkSection & isec)579 void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
580 addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()),
581 STT_FUNC, 1, isec);
582 addSymbol("$t", STT_NOTYPE, 0, isec);
583 }
584
writeLong(uint8_t * buf)585 void ARMV7PILongThunk::writeLong(uint8_t *buf) {
586 const uint8_t data[] = {
587 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
588 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
589 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
590 0x1c, 0xff, 0x2f, 0xe1, // bx ip
591 };
592 uint64_t s = getARMThunkDestVA(destination);
593 uint64_t p = getThunkTargetSym()->getVA();
594 int64_t offset = s - p - 16;
595 memcpy(buf, data, sizeof(data));
596 target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset);
597 target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset);
598 }
599
addSymbols(ThunkSection & isec)600 void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
601 addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()),
602 STT_FUNC, 0, isec);
603 addSymbol("$a", STT_NOTYPE, 0, isec);
604 }
605
writeLong(uint8_t * buf)606 void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
607 const uint8_t data[] = {
608 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
609 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
610 0xfc, 0x44, // L1: add ip, pc
611 0x60, 0x47, // bx ip
612 };
613 uint64_t s = getARMThunkDestVA(destination);
614 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
615 int64_t offset = s - p - 12;
616 memcpy(buf, data, sizeof(data));
617 target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset);
618 target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset);
619 }
620
addSymbols(ThunkSection & isec)621 void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
622 addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()),
623 STT_FUNC, 1, isec);
624 addSymbol("$t", STT_NOTYPE, 0, isec);
625 }
626
writeLong(uint8_t * buf)627 void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
628 const uint8_t data[] = {
629 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
630 0x00, 0x00, 0x00, 0x00, // L1: .word S
631 };
632 memcpy(buf, data, sizeof(data));
633 target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
634 }
635
addSymbols(ThunkSection & isec)636 void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
637 addSymbol(saver().save("__ARMv5ABSLongThunk_" + destination.getName()),
638 STT_FUNC, 0, isec);
639 addSymbol("$a", STT_NOTYPE, 0, isec);
640 addSymbol("$d", STT_NOTYPE, 4, isec);
641 }
642
isCompatibleWith(const InputSection & isec,const Relocation & rel) const643 bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec,
644 const Relocation &rel) const {
645 // Thumb branch relocations can't use BLX
646 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
647 }
648
writeLong(uint8_t * buf)649 void ARMV5PILongThunk::writeLong(uint8_t *buf) {
650 const uint8_t data[] = {
651 0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
652 0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
653 0x1c, 0xff, 0x2f, 0xe1, // bx ip
654 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
655 };
656 uint64_t s = getARMThunkDestVA(destination);
657 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
658 memcpy(buf, data, sizeof(data));
659 target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
660 }
661
addSymbols(ThunkSection & isec)662 void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
663 addSymbol(saver().save("__ARMV5PILongThunk_" + destination.getName()),
664 STT_FUNC, 0, isec);
665 addSymbol("$a", STT_NOTYPE, 0, isec);
666 addSymbol("$d", STT_NOTYPE, 12, isec);
667 }
668
isCompatibleWith(const InputSection & isec,const Relocation & rel) const669 bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec,
670 const Relocation &rel) const {
671 // Thumb branch relocations can't use BLX
672 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
673 }
674
writeLong(uint8_t * buf)675 void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
676 // Most Thumb instructions cannot access the high registers r8 - r15. As the
677 // only register we can corrupt is r12 we must instead spill a low register
678 // to the stack to use as a scratch register. We push r1 even though we
679 // don't need to get some space to use for the return address.
680 const uint8_t data[] = {
681 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers
682 0x01, 0x48, // ldr r0, [pc, #4] ; L1
683 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S
684 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest
685 0x00, 0x00, 0x00, 0x00 // L1: .word S
686 };
687 uint64_t s = getARMThunkDestVA(destination);
688 memcpy(buf, data, sizeof(data));
689 target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
690 }
691
addSymbols(ThunkSection & isec)692 void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
693 addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()),
694 STT_FUNC, 1, isec);
695 addSymbol("$t", STT_NOTYPE, 0, isec);
696 addSymbol("$d", STT_NOTYPE, 8, isec);
697 }
698
writeLong(uint8_t * buf)699 void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
700 // Most Thumb instructions cannot access the high registers r8 - r15. As the
701 // only register we can corrupt is ip (r12) we must instead spill a low
702 // register to the stack to use as a scratch register.
703 const uint8_t data[] = {
704 0x01, 0xb4, // P: push {r0} ; Obtain scratch register
705 0x02, 0x48, // ldr r0, [pc, #8] ; L2
706 0x84, 0x46, // mov ip, r0 ; high to low register
707 0x01, 0xbc, // pop {r0} ; restore scratch register
708 0xe7, 0x44, // L1: add pc, ip ; transfer control
709 0xc0, 0x46, // nop ; pad to 4-byte boundary
710 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
711 };
712 uint64_t s = getARMThunkDestVA(destination);
713 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
714 memcpy(buf, data, sizeof(data));
715 target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
716 }
717
addSymbols(ThunkSection & isec)718 void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
719 addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()),
720 STT_FUNC, 1, isec);
721 addSymbol("$t", STT_NOTYPE, 0, isec);
722 addSymbol("$d", STT_NOTYPE, 12, isec);
723 }
724
725 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)726 void MipsThunk::writeTo(uint8_t *buf) {
727 uint64_t s = destination.getVA();
728 write32(buf, 0x3c190000); // lui $25, %hi(func)
729 write32(buf + 4, 0x08000000 | (s >> 2)); // j func
730 write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
731 write32(buf + 12, 0x00000000); // nop
732 target->relocateNoSym(buf, R_MIPS_HI16, s);
733 target->relocateNoSym(buf + 8, R_MIPS_LO16, s);
734 }
735
addSymbols(ThunkSection & isec)736 void MipsThunk::addSymbols(ThunkSection &isec) {
737 addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
738 isec);
739 }
740
getTargetInputSection() const741 InputSection *MipsThunk::getTargetInputSection() const {
742 auto &dr = cast<Defined>(destination);
743 return dyn_cast<InputSection>(dr.section);
744 }
745
746 // Write microMIPS R2-R5 LA25 thunk code
747 // to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)748 void MicroMipsThunk::writeTo(uint8_t *buf) {
749 uint64_t s = destination.getVA();
750 write16(buf, 0x41b9); // lui $25, %hi(func)
751 write16(buf + 4, 0xd400); // j func
752 write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func)
753 write16(buf + 12, 0x0c00); // nop
754 target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
755 target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s);
756 target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s);
757 }
758
addSymbols(ThunkSection & isec)759 void MicroMipsThunk::addSymbols(ThunkSection &isec) {
760 Defined *d =
761 addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
762 STT_FUNC, 0, isec);
763 d->stOther |= STO_MIPS_MICROMIPS;
764 }
765
getTargetInputSection() const766 InputSection *MicroMipsThunk::getTargetInputSection() const {
767 auto &dr = cast<Defined>(destination);
768 return dyn_cast<InputSection>(dr.section);
769 }
770
771 // Write microMIPS R6 LA25 thunk code
772 // to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)773 void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
774 uint64_t s = destination.getVA();
775 uint64_t p = getThunkTargetSym()->getVA();
776 write16(buf, 0x1320); // lui $25, %hi(func)
777 write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func)
778 write16(buf + 8, 0x9400); // bc func
779 target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
780 target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s);
781 target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
782 }
783
addSymbols(ThunkSection & isec)784 void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
785 Defined *d =
786 addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
787 STT_FUNC, 0, isec);
788 d->stOther |= STO_MIPS_MICROMIPS;
789 }
790
getTargetInputSection() const791 InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
792 auto &dr = cast<Defined>(destination);
793 return dyn_cast<InputSection>(dr.section);
794 }
795
writePPC32PltCallStub(uint8_t * buf,uint64_t gotPltVA,const InputFile * file,int64_t addend)796 void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
797 const InputFile *file, int64_t addend) {
798 if (!config->isPic) {
799 write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
800 write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
801 write32(buf + 8, 0x7d6903a6); // mtctr r11
802 write32(buf + 12, 0x4e800420); // bctr
803 return;
804 }
805 uint32_t offset;
806 if (addend >= 0x8000) {
807 // The stub loads an address relative to r30 (.got2+Addend). Addend is
808 // almost always 0x8000. The address of .got2 is different in another object
809 // file, so a stub cannot be shared.
810 offset = gotPltVA -
811 (in.ppc32Got2->getParent()->getVA() +
812 (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend);
813 } else {
814 // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
815 // currently the address of .got).
816 offset = gotPltVA - in.got->getVA();
817 }
818 uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
819 if (ha == 0) {
820 write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30)
821 write32(buf + 4, 0x7d6903a6); // mtctr r11
822 write32(buf + 8, 0x4e800420); // bctr
823 write32(buf + 12, 0x60000000); // nop
824 } else {
825 write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha
826 write32(buf + 4, 0x816b0000 | l); // lwz r11,l(r11)
827 write32(buf + 8, 0x7d6903a6); // mtctr r11
828 write32(buf + 12, 0x4e800420); // bctr
829 }
830 }
831
writeTo(uint8_t * buf)832 void PPC32PltCallStub::writeTo(uint8_t *buf) {
833 writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend);
834 }
835
addSymbols(ThunkSection & isec)836 void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
837 std::string buf;
838 raw_string_ostream os(buf);
839 os << format_hex_no_prefix(addend, 8);
840 if (!config->isPic)
841 os << ".plt_call32.";
842 else if (addend >= 0x8000)
843 os << ".got2.plt_pic32.";
844 else
845 os << ".plt_pic32.";
846 os << destination.getName();
847 addSymbol(saver().save(os.str()), STT_FUNC, 0, isec);
848 }
849
isCompatibleWith(const InputSection & isec,const Relocation & rel) const850 bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
851 const Relocation &rel) const {
852 return !config->isPic || (isec.file == file && rel.addend == addend);
853 }
854
addSymbols(ThunkSection & isec)855 void PPC32LongThunk::addSymbols(ThunkSection &isec) {
856 addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0,
857 isec);
858 }
859
writeTo(uint8_t * buf)860 void PPC32LongThunk::writeTo(uint8_t *buf) {
861 auto ha = [](uint32_t v) -> uint16_t { return (v + 0x8000) >> 16; };
862 auto lo = [](uint32_t v) -> uint16_t { return v; };
863 uint32_t d = destination.getVA(addend);
864 if (config->isPic) {
865 uint32_t off = d - (getThunkTargetSym()->getVA() + 8);
866 write32(buf + 0, 0x7c0802a6); // mflr r12,0
867 write32(buf + 4, 0x429f0005); // bcl r20,r31,.+4
868 write32(buf + 8, 0x7d8802a6); // mtctr r12
869 write32(buf + 12, 0x3d8c0000 | ha(off)); // addis r12,r12,off@ha
870 write32(buf + 16, 0x398c0000 | lo(off)); // addi r12,r12,off@l
871 write32(buf + 20, 0x7c0803a6); // mtlr r0
872 buf += 24;
873 } else {
874 write32(buf + 0, 0x3d800000 | ha(d)); // lis r12,d@ha
875 write32(buf + 4, 0x398c0000 | lo(d)); // addi r12,r12,d@l
876 buf += 8;
877 }
878 write32(buf + 0, 0x7d8903a6); // mtctr r12
879 write32(buf + 4, 0x4e800420); // bctr
880 }
881
writePPC64LoadAndBranch(uint8_t * buf,int64_t offset)882 void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
883 uint16_t offHa = (offset + 0x8000) >> 16;
884 uint16_t offLo = offset & 0xffff;
885
886 write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
887 write32(buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12)
888 write32(buf + 8, 0x7d8903a6); // mtctr r12
889 write32(buf + 12, 0x4e800420); // bctr
890 }
891
writeTo(uint8_t * buf)892 void PPC64PltCallStub::writeTo(uint8_t *buf) {
893 int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
894 // Save the TOC pointer to the save-slot reserved in the call frame.
895 write32(buf + 0, 0xf8410018); // std r2,24(r1)
896 writePPC64LoadAndBranch(buf + 4, offset);
897 }
898
addSymbols(ThunkSection & isec)899 void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
900 Defined *s = addSymbol(saver().save("__plt_" + destination.getName()),
901 STT_FUNC, 0, isec);
902 s->needsTocRestore = true;
903 s->file = destination.file;
904 }
905
isCompatibleWith(const InputSection & isec,const Relocation & rel) const906 bool PPC64PltCallStub::isCompatibleWith(const InputSection &isec,
907 const Relocation &rel) const {
908 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
909 }
910
writeTo(uint8_t * buf)911 void PPC64R2SaveStub::writeTo(uint8_t *buf) {
912 const int64_t offset = computeOffset();
913 write32(buf + 0, 0xf8410018); // std r2,24(r1)
914 // The branch offset needs to fit in 26 bits.
915 if (getMayUseShortThunk()) {
916 write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset>
917 } else if (isInt<34>(offset)) {
918 int nextInstOffset;
919 uint64_t tocOffset = destination.getVA() - getPPC64TocBase();
920 if (tocOffset >> 16 > 0) {
921 const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff);
922 const uint64_t addis =
923 ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff);
924 write32(buf + 4, addis); // addis r12, r2 , top of offset
925 write32(buf + 8, addi); // addi r12, r12, bottom of offset
926 nextInstOffset = 12;
927 } else {
928 const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff);
929 write32(buf + 4, addi); // addi r12, r2, offset
930 nextInstOffset = 8;
931 }
932 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
933 write32(buf + nextInstOffset + 4, BCTR); // bctr
934 } else {
935 in.ppc64LongBranchTarget->addEntry(&destination, addend);
936 const int64_t offsetFromTOC =
937 in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
938 getPPC64TocBase();
939 writePPC64LoadAndBranch(buf + 4, offsetFromTOC);
940 }
941 }
942
addSymbols(ThunkSection & isec)943 void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
944 Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()),
945 STT_FUNC, 0, isec);
946 s->needsTocRestore = true;
947 }
948
isCompatibleWith(const InputSection & isec,const Relocation & rel) const949 bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec,
950 const Relocation &rel) const {
951 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
952 }
953
writeTo(uint8_t * buf)954 void PPC64R12SetupStub::writeTo(uint8_t *buf) {
955 int64_t offset = destination.getVA() - getThunkTargetSym()->getVA();
956 if (!isInt<34>(offset))
957 reportRangeError(buf, offset, 34, destination, "R12 setup stub offset");
958
959 int nextInstOffset;
960 if (!config->power10Stubs) {
961 uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
962 write32(buf + 0, 0x7c0802a6); // mflr r12
963 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4
964 write32(buf + 8, 0x7d6802a6); // mflr r11
965 write32(buf + 12, 0x7d8803a6); // mtlr r12
966 write32(buf + 16, 0x3d8b0000 | computeHiBits(off));// addis r12,r11,off@ha
967 write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l
968 nextInstOffset = 24;
969 } else {
970 uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
971 (offset & 0xffff);
972 writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1
973 nextInstOffset = 8;
974 }
975 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
976 write32(buf + nextInstOffset + 4, BCTR); // bctr
977 }
978
addSymbols(ThunkSection & isec)979 void PPC64R12SetupStub::addSymbols(ThunkSection &isec) {
980 addSymbol(saver().save("__gep_setup_" + destination.getName()), STT_FUNC, 0,
981 isec);
982 }
983
isCompatibleWith(const InputSection & isec,const Relocation & rel) const984 bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec,
985 const Relocation &rel) const {
986 return rel.type == R_PPC64_REL24_NOTOC;
987 }
988
writeTo(uint8_t * buf)989 void PPC64PCRelPLTStub::writeTo(uint8_t *buf) {
990 int nextInstOffset = 0;
991 int64_t offset = destination.getGotPltVA() - getThunkTargetSym()->getVA();
992
993 if (config->power10Stubs) {
994 if (!isInt<34>(offset))
995 reportRangeError(buf, offset, 34, destination,
996 "PC-relative PLT stub offset");
997 const uint64_t pld = PLD_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
998 (offset & 0xffff);
999 writePrefixedInstruction(buf + 0, pld); // pld r12, func@plt@pcrel
1000 nextInstOffset = 8;
1001 } else {
1002 uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
1003 write32(buf + 0, 0x7c0802a6); // mflr r12
1004 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4
1005 write32(buf + 8, 0x7d6802a6); // mflr r11
1006 write32(buf + 12, 0x7d8803a6); // mtlr r12
1007 write32(buf + 16, 0x3d8b0000 | computeHiBits(off)); // addis r12,r11,off@ha
1008 write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l
1009 nextInstOffset = 24;
1010 }
1011 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
1012 write32(buf + nextInstOffset + 4, BCTR); // bctr
1013 }
1014
addSymbols(ThunkSection & isec)1015 void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) {
1016 addSymbol(saver().save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0,
1017 isec);
1018 }
1019
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1020 bool PPC64PCRelPLTStub::isCompatibleWith(const InputSection &isec,
1021 const Relocation &rel) const {
1022 return rel.type == R_PPC64_REL24_NOTOC;
1023 }
1024
writeTo(uint8_t * buf)1025 void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
1026 int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
1027 getPPC64TocBase();
1028 writePPC64LoadAndBranch(buf, offset);
1029 }
1030
addSymbols(ThunkSection & isec)1031 void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
1032 addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0,
1033 isec);
1034 }
1035
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1036 bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
1037 const Relocation &rel) const {
1038 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
1039 }
1040
Thunk(Symbol & d,int64_t a)1041 Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {}
1042
1043 Thunk::~Thunk() = default;
1044
addThunkAArch64(RelType type,Symbol & s,int64_t a)1045 static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
1046 if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
1047 type != R_AARCH64_PLT32)
1048 fatal("unrecognized relocation type");
1049 if (config->picThunk)
1050 return make<AArch64ADRPThunk>(s, a);
1051 return make<AArch64ABSLongThunk>(s, a);
1052 }
1053
1054 // Creates a thunk for Thumb-ARM interworking.
1055 // Arm Architectures v5 and v6 do not support Thumb2 technology. This means
1056 // - MOVT and MOVW instructions cannot be used
1057 // - Only Thumb relocation that can generate a Thunk is a BL, this can always
1058 // be transformed into a BLX
addThunkPreArmv7(RelType reloc,Symbol & s,int64_t a)1059 static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) {
1060 switch (reloc) {
1061 case R_ARM_PC24:
1062 case R_ARM_PLT32:
1063 case R_ARM_JUMP24:
1064 case R_ARM_CALL:
1065 case R_ARM_THM_CALL:
1066 if (config->picThunk)
1067 return make<ARMV5PILongThunk>(s, a);
1068 return make<ARMV5ABSLongThunk>(s, a);
1069 }
1070 fatal("relocation " + toString(reloc) + " to " + toString(s) +
1071 " not supported for Armv5 or Armv6 targets");
1072 }
1073
1074 // Create a thunk for Thumb long branch on V6-M.
1075 // Arm Architecture v6-M only supports Thumb instructions. This means
1076 // - MOVT and MOVW instructions cannot be used.
1077 // - Only a limited number of instructions can access registers r8 and above
1078 // - No interworking support is needed (all Thumb).
addThunkV6M(RelType reloc,Symbol & s,int64_t a)1079 static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) {
1080 switch (reloc) {
1081 case R_ARM_THM_JUMP19:
1082 case R_ARM_THM_JUMP24:
1083 case R_ARM_THM_CALL:
1084 if (config->isPic)
1085 return make<ThumbV6MPILongThunk>(s, a);
1086 return make<ThumbV6MABSLongThunk>(s, a);
1087 }
1088 fatal("relocation " + toString(reloc) + " to " + toString(s) +
1089 " not supported for Armv6-M targets");
1090 }
1091
1092 // Creates a thunk for Thumb-ARM interworking or branch range extension.
addThunkArm(RelType reloc,Symbol & s,int64_t a)1093 static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) {
1094 // Decide which Thunk is needed based on:
1095 // Available instruction set
1096 // - An Arm Thunk can only be used if Arm state is available.
1097 // - A Thumb Thunk can only be used if Thumb state is available.
1098 // - Can only use a Thunk if it uses instructions that the Target supports.
1099 // Relocation is branch or branch and link
1100 // - Branch instructions cannot change state, can only select Thunk that
1101 // starts in the same state as the caller.
1102 // - Branch and link relocations can change state, can select Thunks from
1103 // either Arm or Thumb.
1104 // Position independent Thunks if we require position independent code.
1105
1106 // Handle architectures that have restrictions on the instructions that they
1107 // can use in Thunks. The flags below are set by reading the BuildAttributes
1108 // of the input objects. InputFiles.cpp contains the mapping from ARM
1109 // architecture to flag.
1110 if (!config->armHasMovtMovw) {
1111 if (!config->armJ1J2BranchEncoding)
1112 return addThunkPreArmv7(reloc, s, a);
1113 return addThunkV6M(reloc, s, a);
1114 }
1115
1116 switch (reloc) {
1117 case R_ARM_PC24:
1118 case R_ARM_PLT32:
1119 case R_ARM_JUMP24:
1120 case R_ARM_CALL:
1121 if (config->picThunk)
1122 return make<ARMV7PILongThunk>(s, a);
1123 return make<ARMV7ABSLongThunk>(s, a);
1124 case R_ARM_THM_JUMP19:
1125 case R_ARM_THM_JUMP24:
1126 case R_ARM_THM_CALL:
1127 if (config->picThunk)
1128 return make<ThumbV7PILongThunk>(s, a);
1129 return make<ThumbV7ABSLongThunk>(s, a);
1130 }
1131 fatal("unrecognized relocation type");
1132 }
1133
addThunkMips(RelType type,Symbol & s)1134 static Thunk *addThunkMips(RelType type, Symbol &s) {
1135 if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
1136 return make<MicroMipsR6Thunk>(s);
1137 if (s.stOther & STO_MIPS_MICROMIPS)
1138 return make<MicroMipsThunk>(s);
1139 return make<MipsThunk>(s);
1140 }
1141
addThunkPPC32(const InputSection & isec,const Relocation & rel,Symbol & s)1142 static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel,
1143 Symbol &s) {
1144 assert((rel.type == R_PPC_LOCAL24PC || rel.type == R_PPC_REL24 ||
1145 rel.type == R_PPC_PLTREL24) &&
1146 "unexpected relocation type for thunk");
1147 if (s.isInPlt())
1148 return make<PPC32PltCallStub>(isec, rel, s);
1149 return make<PPC32LongThunk>(s, rel.addend);
1150 }
1151
addThunkPPC64(RelType type,Symbol & s,int64_t a)1152 static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
1153 assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 ||
1154 type == R_PPC64_REL24_NOTOC) &&
1155 "unexpected relocation type for thunk");
1156 if (s.isInPlt())
1157 return type == R_PPC64_REL24_NOTOC ? (Thunk *)make<PPC64PCRelPLTStub>(s)
1158 : (Thunk *)make<PPC64PltCallStub>(s);
1159
1160 // This check looks at the st_other bits of the callee. If the value is 1
1161 // then the callee clobbers the TOC and we need an R2 save stub when RelType
1162 // is R_PPC64_REL14 or R_PPC64_REL24.
1163 if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1)
1164 return make<PPC64R2SaveStub>(s, a);
1165
1166 if (type == R_PPC64_REL24_NOTOC)
1167 return make<PPC64R12SetupStub>(s);
1168
1169 if (config->picThunk)
1170 return make<PPC64PILongBranchThunk>(s, a);
1171
1172 return make<PPC64PDLongBranchThunk>(s, a);
1173 }
1174
addThunk(const InputSection & isec,Relocation & rel)1175 Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
1176 Symbol &s = *rel.sym;
1177 int64_t a = rel.addend;
1178
1179 if (config->emachine == EM_AARCH64)
1180 return addThunkAArch64(rel.type, s, a);
1181
1182 if (config->emachine == EM_ARM)
1183 return addThunkArm(rel.type, s, a);
1184
1185 if (config->emachine == EM_MIPS)
1186 return addThunkMips(rel.type, s);
1187
1188 if (config->emachine == EM_PPC)
1189 return addThunkPPC32(isec, rel, s);
1190
1191 if (config->emachine == EM_PPC64)
1192 return addThunkPPC64(rel.type, s, a);
1193
1194 llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
1195 }
1196