xref: /llvm-project-15.0.7/lld/ELF/Thunks.cpp (revision 9e5283f2)
1 //===- Thunks.cpp --------------------------------------------------------===//
2 //
3 //                             The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===---------------------------------------------------------------------===//
9 //
10 // This file contains Thunk subclasses.
11 //
12 // A thunk is a small piece of code written after an input section
13 // which is used to jump between "incompatible" functions
14 // such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions.
15 //
16 // If a jump target is too far and its address doesn't fit to a
17 // short jump instruction, we need to create a thunk too, but we
18 // haven't supported it yet.
19 //
20 // i386 and x86-64 don't need thunks.
21 //
22 //===---------------------------------------------------------------------===//
23 
24 #include "Thunks.h"
25 #include "Config.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/ErrorHandler.h"
32 #include "lld/Common/Memory.h"
33 #include "llvm/BinaryFormat/ELF.h"
34 #include "llvm/Support/Casting.h"
35 #include "llvm/Support/Endian.h"
36 #include "llvm/Support/ErrorHandling.h"
37 #include "llvm/Support/MathExtras.h"
38 #include <cstdint>
39 #include <cstring>
40 
41 using namespace llvm;
42 using namespace llvm::object;
43 using namespace llvm::ELF;
44 
45 namespace lld {
46 namespace elf {
47 
48 namespace {
49 
50 // AArch64 long range Thunks
51 class AArch64ABSLongThunk final : public Thunk {
52 public:
53   AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
54   uint32_t size() override { return 16; }
55   void writeTo(uint8_t *Buf) override;
56   void addSymbols(ThunkSection &IS) override;
57 };
58 
59 class AArch64ADRPThunk final : public Thunk {
60 public:
61   AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
62   uint32_t size() override { return 12; }
63   void writeTo(uint8_t *Buf) override;
64   void addSymbols(ThunkSection &IS) override;
65 };
66 
67 // Base class for ARM thunks.
68 //
69 // An ARM thunk may be either short or long. A short thunk is simply a branch
70 // (B) instruction, and it may be used to call ARM functions when the distance
71 // from the thunk to the target is less than 32MB. Long thunks can branch to any
72 // virtual address and can switch between ARM and Thumb, and they are
73 // implemented in the derived classes. This class tries to create a short thunk
74 // if the target is in range, otherwise it creates a long thunk.
75 class ARMThunk : public Thunk {
76 public:
77   ARMThunk(Symbol &Dest) : Thunk(Dest) {}
78 
79   bool mayUseShortThunk();
80   uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
81   void writeTo(uint8_t *Buf) override;
82   bool isCompatibleWith(RelType Type) const override;
83 
84   // Returns the size of a long thunk.
85   virtual uint32_t sizeLong() = 0;
86 
87   // Writes a long thunk to Buf.
88   virtual void writeLong(uint8_t *Buf) = 0;
89 
90 private:
91   // This field tracks whether all previously considered layouts would allow
92   // this thunk to be short. If we have ever needed a long thunk, we always
93   // create a long thunk, even if the thunk may be short given the current
94   // distance to the target. We do this because transitioning from long to short
95   // can create layout oscillations in certain corner cases which would prevent
96   // the layout from converging.
97   bool MayUseShortThunk = true;
98 };
99 
100 // Base class for Thumb-2 thunks.
101 //
102 // This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
103 // which has a range of 16MB.
104 class ThumbThunk : public Thunk {
105 public:
106   ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
107 
108   bool mayUseShortThunk();
109   uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
110   void writeTo(uint8_t *Buf) override;
111   bool isCompatibleWith(RelType Type) const override;
112 
113   // Returns the size of a long thunk.
114   virtual uint32_t sizeLong() = 0;
115 
116   // Writes a long thunk to Buf.
117   virtual void writeLong(uint8_t *Buf) = 0;
118 
119 private:
120   // See comment in ARMThunk above.
121   bool MayUseShortThunk = true;
122 };
123 
124 // Specific ARM Thunk implementations. The naming convention is:
125 // Source State, TargetState, Target Requirement, ABS or PI, Range
126 class ARMV7ABSLongThunk final : public ARMThunk {
127 public:
128   ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
129 
130   uint32_t sizeLong() override { return 12; }
131   void writeLong(uint8_t *Buf) override;
132   void addSymbols(ThunkSection &IS) override;
133 };
134 
135 class ARMV7PILongThunk final : public ARMThunk {
136 public:
137   ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
138 
139   uint32_t sizeLong() override { return 16; }
140   void writeLong(uint8_t *Buf) override;
141   void addSymbols(ThunkSection &IS) override;
142 };
143 
144 class ThumbV7ABSLongThunk final : public ThumbThunk {
145 public:
146   ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
147 
148   uint32_t sizeLong() override { return 10; }
149   void writeLong(uint8_t *Buf) override;
150   void addSymbols(ThunkSection &IS) override;
151 };
152 
153 class ThumbV7PILongThunk final : public ThumbThunk {
154 public:
155   ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
156 
157   uint32_t sizeLong() override { return 12; }
158   void writeLong(uint8_t *Buf) override;
159   void addSymbols(ThunkSection &IS) override;
160 };
161 
162 // MIPS LA25 thunk
163 class MipsThunk final : public Thunk {
164 public:
165   MipsThunk(Symbol &Dest) : Thunk(Dest) {}
166 
167   uint32_t size() override { return 16; }
168   void writeTo(uint8_t *Buf) override;
169   void addSymbols(ThunkSection &IS) override;
170   InputSection *getTargetInputSection() const override;
171 };
172 
173 // microMIPS R2-R5 LA25 thunk
174 class MicroMipsThunk final : public Thunk {
175 public:
176   MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
177 
178   uint32_t size() override { return 14; }
179   void writeTo(uint8_t *Buf) override;
180   void addSymbols(ThunkSection &IS) override;
181   InputSection *getTargetInputSection() const override;
182 };
183 
184 // microMIPS R6 LA25 thunk
185 class MicroMipsR6Thunk final : public Thunk {
186 public:
187   MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
188 
189   uint32_t size() override { return 12; }
190   void writeTo(uint8_t *Buf) override;
191   void addSymbols(ThunkSection &IS) override;
192   InputSection *getTargetInputSection() const override;
193 };
194 
195 } // end anonymous namespace
196 
197 Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
198                           InputSectionBase &Section) {
199   Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
200   Syms.push_back(D);
201   return D;
202 }
203 
204 void Thunk::setOffset(uint64_t NewOffset) {
205   for (Defined *D : Syms)
206     D->Value = D->Value - Offset + NewOffset;
207   Offset = NewOffset;
208 }
209 
210 // AArch64 long range Thunks
211 
212 static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
213   uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
214   return V;
215 }
216 
217 void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
218   const uint8_t Data[] = {
219     0x50, 0x00, 0x00, 0x58, //     ldr x16, L0
220     0x00, 0x02, 0x1f, 0xd6, //     br  x16
221     0x00, 0x00, 0x00, 0x00, // L0: .xword S
222     0x00, 0x00, 0x00, 0x00,
223   };
224   uint64_t S = getAArch64ThunkDestVA(Destination);
225   memcpy(Buf, Data, sizeof(Data));
226   Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
227 }
228 
229 void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
230   addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
231             STT_FUNC, 0, IS);
232   addSymbol("$x", STT_NOTYPE, 0, IS);
233   addSymbol("$d", STT_NOTYPE, 8, IS);
234 }
235 
236 // This Thunk has a maximum range of 4Gb, this is sufficient for all programs
237 // using the small code model, including pc-relative ones. At time of writing
238 // clang and gcc do not support the large code model for position independent
239 // code so it is safe to use this for position independent thunks without
240 // worrying about the destination being more than 4Gb away.
241 void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
242   const uint8_t Data[] = {
243       0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
244       0x10, 0x02, 0x00, 0x91, // add  x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
245       0x00, 0x02, 0x1f, 0xd6, // br   x16
246   };
247   uint64_t S = getAArch64ThunkDestVA(Destination);
248   uint64_t P = getThunkTargetSym()->getVA();
249   memcpy(Buf, Data, sizeof(Data));
250   Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
251                       getAArch64Page(S) - getAArch64Page(P));
252   Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
253 }
254 
255 void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
256   addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
257             0, IS);
258   addSymbol("$x", STT_NOTYPE, 0, IS);
259 }
260 
261 // ARM Target Thunks
262 static uint64_t getARMThunkDestVA(const Symbol &S) {
263   uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
264   return SignExtend64<32>(V);
265 }
266 
267 // This function returns true if the target is not Thumb and is within 2^26, and
268 // it has not previously returned false (see comment for MayUseShortThunk).
269 bool ARMThunk::mayUseShortThunk() {
270   if (!MayUseShortThunk)
271     return false;
272   uint64_t S = getARMThunkDestVA(Destination);
273   if (S & 1) {
274     MayUseShortThunk = false;
275     return false;
276   }
277   uint64_t P = getThunkTargetSym()->getVA();
278   int64_t Offset = S - P - 8;
279   MayUseShortThunk = llvm::isInt<26>(Offset);
280   return MayUseShortThunk;
281 }
282 
283 void ARMThunk::writeTo(uint8_t *Buf) {
284   if (!mayUseShortThunk()) {
285     writeLong(Buf);
286     return;
287   }
288 
289   uint64_t S = getARMThunkDestVA(Destination);
290   uint64_t P = getThunkTargetSym()->getVA();
291   int64_t Offset = S - P - 8;
292   const uint8_t Data[] = {
293     0x00, 0x00, 0x00, 0xea, // b S
294   };
295   memcpy(Buf, Data, sizeof(Data));
296   Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
297 }
298 
299 bool ARMThunk::isCompatibleWith(RelType Type) const {
300   // Thumb branch relocations can't use BLX
301   return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
302 }
303 
304 // This function returns true if the target is Thumb and is within 2^25, and
305 // it has not previously returned false (see comment for MayUseShortThunk).
306 bool ThumbThunk::mayUseShortThunk() {
307   if (!MayUseShortThunk)
308     return false;
309   uint64_t S = getARMThunkDestVA(Destination);
310   if ((S & 1) == 0) {
311     MayUseShortThunk = false;
312     return false;
313   }
314   uint64_t P = getThunkTargetSym()->getVA() & ~1;
315   int64_t Offset = S - P - 4;
316   MayUseShortThunk = llvm::isInt<25>(Offset);
317   return MayUseShortThunk;
318 }
319 
320 void ThumbThunk::writeTo(uint8_t *Buf) {
321   if (!mayUseShortThunk()) {
322     writeLong(Buf);
323     return;
324   }
325 
326   uint64_t S = getARMThunkDestVA(Destination);
327   uint64_t P = getThunkTargetSym()->getVA();
328   int64_t Offset = S - P - 4;
329   const uint8_t Data[] = {
330       0x00, 0xf0, 0x00, 0xb0, // b.w S
331   };
332   memcpy(Buf, Data, sizeof(Data));
333   Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
334 }
335 
336 bool ThumbThunk::isCompatibleWith(RelType Type) const {
337   // ARM branch relocations can't use BLX
338   return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
339 }
340 
341 void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
342   const uint8_t Data[] = {
343       0x00, 0xc0, 0x00, 0xe3, // movw         ip,:lower16:S
344       0x00, 0xc0, 0x40, 0xe3, // movt         ip,:upper16:S
345       0x1c, 0xff, 0x2f, 0xe1, // bx   ip
346   };
347   uint64_t S = getARMThunkDestVA(Destination);
348   memcpy(Buf, Data, sizeof(Data));
349   Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
350   Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
351 }
352 
353 void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
354   addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
355             STT_FUNC, 0, IS);
356   addSymbol("$a", STT_NOTYPE, 0, IS);
357 }
358 
359 void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
360   const uint8_t Data[] = {
361       0x40, 0xf2, 0x00, 0x0c, // movw         ip, :lower16:S
362       0xc0, 0xf2, 0x00, 0x0c, // movt         ip, :upper16:S
363       0x60, 0x47,             // bx   ip
364   };
365   uint64_t S = getARMThunkDestVA(Destination);
366   memcpy(Buf, Data, sizeof(Data));
367   Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
368   Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
369 }
370 
371 void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
372   addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
373             STT_FUNC, 1, IS);
374   addSymbol("$t", STT_NOTYPE, 0, IS);
375 }
376 
377 void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
378   const uint8_t Data[] = {
379       0xf0, 0xcf, 0x0f, 0xe3, // P:  movw ip,:lower16:S - (P + (L1-P) + 8)
380       0x00, 0xc0, 0x40, 0xe3, //     movt ip,:upper16:S - (P + (L1-P) + 8)
381       0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
382       0x1c, 0xff, 0x2f, 0xe1, //     bx r12
383   };
384   uint64_t S = getARMThunkDestVA(Destination);
385   uint64_t P = getThunkTargetSym()->getVA();
386   uint64_t Offset = S - P - 16;
387   memcpy(Buf, Data, sizeof(Data));
388   Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
389   Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
390 }
391 
392 void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
393   addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
394             0, IS);
395   addSymbol("$a", STT_NOTYPE, 0, IS);
396 }
397 
398 void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
399   const uint8_t Data[] = {
400       0x4f, 0xf6, 0xf4, 0x7c, // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
401       0xc0, 0xf2, 0x00, 0x0c, //     movt ip,:upper16:S - (P + (L1-P) + 4)
402       0xfc, 0x44,             // L1: add  r12, pc
403       0x60, 0x47,             //     bx   r12
404   };
405   uint64_t S = getARMThunkDestVA(Destination);
406   uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
407   uint64_t Offset = S - P - 12;
408   memcpy(Buf, Data, sizeof(Data));
409   Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
410   Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
411 }
412 
413 void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
414   addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
415             STT_FUNC, 1, IS);
416   addSymbol("$t", STT_NOTYPE, 0, IS);
417 }
418 
419 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
420 void MipsThunk::writeTo(uint8_t *Buf) {
421   uint64_t S = Destination.getVA();
422   write32(Buf, 0x3c190000); // lui   $25, %hi(func)
423   write32(Buf + 4, 0x08000000 | (S >> 2)); // j     func
424   write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
425   write32(Buf + 12, 0x00000000); // nop
426   Target->relocateOne(Buf, R_MIPS_HI16, S);
427   Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
428 }
429 
430 void MipsThunk::addSymbols(ThunkSection &IS) {
431   addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
432             IS);
433 }
434 
435 InputSection *MipsThunk::getTargetInputSection() const {
436   auto &DR = cast<Defined>(Destination);
437   return dyn_cast<InputSection>(DR.Section);
438 }
439 
440 // Write microMIPS R2-R5 LA25 thunk code
441 // to call PIC function from the non-PIC one.
442 void MicroMipsThunk::writeTo(uint8_t *Buf) {
443   uint64_t S = Destination.getVA() | 1;
444   write16(Buf, 0x41b9);       // lui   $25, %hi(func)
445   write16(Buf + 4, 0xd400);   // j     func
446   write16(Buf + 8, 0x3339);   // addiu $25, $25, %lo(func)
447   write16(Buf + 12, 0x0c00);  // nop
448   Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
449   Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
450   Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
451 }
452 
453 void MicroMipsThunk::addSymbols(ThunkSection &IS) {
454   Defined *D = addSymbol(
455       Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
456   D->StOther |= STO_MIPS_MICROMIPS;
457 }
458 
459 InputSection *MicroMipsThunk::getTargetInputSection() const {
460   auto &DR = cast<Defined>(Destination);
461   return dyn_cast<InputSection>(DR.Section);
462 }
463 
464 // Write microMIPS R6 LA25 thunk code
465 // to call PIC function from the non-PIC one.
466 void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
467   uint64_t S = Destination.getVA() | 1;
468   uint64_t P = getThunkTargetSym()->getVA();
469   write16(Buf, 0x1320);       // lui   $25, %hi(func)
470   write16(Buf + 4, 0x3339);   // addiu $25, $25, %lo(func)
471   write16(Buf + 8, 0x9400);   // bc    func
472   Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
473   Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
474   Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
475 }
476 
477 void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
478   Defined *D = addSymbol(
479       Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
480   D->StOther |= STO_MIPS_MICROMIPS;
481 }
482 
483 InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
484   auto &DR = cast<Defined>(Destination);
485   return dyn_cast<InputSection>(DR.Section);
486 }
487 
488 Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
489 
490 Thunk::~Thunk() = default;
491 
492 static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
493   if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
494     fatal("unrecognized relocation type");
495   if (Config->Pic)
496     return make<AArch64ADRPThunk>(S);
497   return make<AArch64ABSLongThunk>(S);
498 }
499 
500 // Creates a thunk for Thumb-ARM interworking.
501 static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
502   // ARM relocations need ARM to Thumb interworking Thunks.
503   // Thumb relocations need Thumb to ARM relocations.
504   // Use position independent Thunks if we require position independent code.
505   switch (Reloc) {
506   case R_ARM_PC24:
507   case R_ARM_PLT32:
508   case R_ARM_JUMP24:
509   case R_ARM_CALL:
510     if (Config->Pic)
511       return make<ARMV7PILongThunk>(S);
512     return make<ARMV7ABSLongThunk>(S);
513   case R_ARM_THM_JUMP19:
514   case R_ARM_THM_JUMP24:
515   case R_ARM_THM_CALL:
516     if (Config->Pic)
517       return make<ThumbV7PILongThunk>(S);
518     return make<ThumbV7ABSLongThunk>(S);
519   }
520   fatal("unrecognized relocation type");
521 }
522 
523 static Thunk *addThunkMips(RelType Type, Symbol &S) {
524   if ((S.StOther & STO_MIPS_MICROMIPS) && isMipsR6())
525     return make<MicroMipsR6Thunk>(S);
526   if (S.StOther & STO_MIPS_MICROMIPS)
527     return make<MicroMipsThunk>(S);
528   return make<MipsThunk>(S);
529 }
530 
531 Thunk *addThunk(RelType Type, Symbol &S) {
532   if (Config->EMachine == EM_AARCH64)
533     return addThunkAArch64(Type, S);
534   else if (Config->EMachine == EM_ARM)
535     return addThunkArm(Type, S);
536   else if (Config->EMachine == EM_MIPS)
537     return addThunkMips(Type, S);
538   llvm_unreachable("add Thunk only supported for ARM and Mips");
539   return nullptr;
540 }
541 
542 } // end namespace elf
543 } // end namespace lld
544