10b57cec5SDimitry Andric //===- Thunks.cpp --------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===---------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file contains Thunk subclasses.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric // A thunk is a small piece of code written after an input section
120b57cec5SDimitry Andric // which is used to jump between "incompatible" functions
130b57cec5SDimitry Andric // such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions.
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric // If a jump target is too far and its address doesn't fit to a
160b57cec5SDimitry Andric // short jump instruction, we need to create a thunk too, but we
170b57cec5SDimitry Andric // haven't supported it yet.
180b57cec5SDimitry Andric //
190b57cec5SDimitry Andric // i386 and x86-64 don't need thunks.
200b57cec5SDimitry Andric //
210b57cec5SDimitry Andric //===---------------------------------------------------------------------===//
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric #include "Thunks.h"
240b57cec5SDimitry Andric #include "Config.h"
2581ad6265SDimitry Andric #include "InputFiles.h"
260b57cec5SDimitry Andric #include "InputSection.h"
270b57cec5SDimitry Andric #include "OutputSections.h"
280b57cec5SDimitry Andric #include "Symbols.h"
290b57cec5SDimitry Andric #include "SyntheticSections.h"
300b57cec5SDimitry Andric #include "Target.h"
3104eeddc0SDimitry Andric #include "lld/Common/CommonLinkerContext.h"
320b57cec5SDimitry Andric #include "llvm/BinaryFormat/ELF.h"
330b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
340b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
350b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
360b57cec5SDimitry Andric #include <cstdint>
370b57cec5SDimitry Andric #include <cstring>
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric using namespace llvm;
400b57cec5SDimitry Andric using namespace llvm::object;
410b57cec5SDimitry Andric using namespace llvm::ELF;
425ffd83dbSDimitry Andric using namespace lld;
435ffd83dbSDimitry Andric using namespace lld::elf;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric namespace {
460b57cec5SDimitry Andric 
4706c3fb27SDimitry Andric // Base class for AArch64 thunks.
4806c3fb27SDimitry Andric //
4906c3fb27SDimitry Andric // An AArch64 thunk may be either short or long. A short thunk is simply a
5006c3fb27SDimitry Andric // branch (B) instruction, and it may be used to call AArch64 functions when the
5106c3fb27SDimitry Andric // distance from the thunk to the target is less than 128MB. Long thunks can
5206c3fb27SDimitry Andric // branch to any virtual address and they are implemented in the derived
5306c3fb27SDimitry Andric // classes. This class tries to create a short thunk if the target is in range,
5406c3fb27SDimitry Andric // otherwise it creates a long thunk.
5506c3fb27SDimitry Andric class AArch64Thunk : public Thunk {
560b57cec5SDimitry Andric public:
AArch64Thunk(Symbol & dest,int64_t addend)5706c3fb27SDimitry Andric   AArch64Thunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
5806c3fb27SDimitry Andric   bool getMayUseShortThunk();
590b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
6006c3fb27SDimitry Andric 
6106c3fb27SDimitry Andric private:
6206c3fb27SDimitry Andric   bool mayUseShortThunk = true;
6306c3fb27SDimitry Andric   virtual void writeLong(uint8_t *buf) = 0;
640b57cec5SDimitry Andric };
650b57cec5SDimitry Andric 
6606c3fb27SDimitry Andric // AArch64 long range Thunks.
6706c3fb27SDimitry Andric class AArch64ABSLongThunk final : public AArch64Thunk {
680b57cec5SDimitry Andric public:
AArch64ABSLongThunk(Symbol & dest,int64_t addend)6906c3fb27SDimitry Andric   AArch64ABSLongThunk(Symbol &dest, int64_t addend)
7006c3fb27SDimitry Andric       : AArch64Thunk(dest, addend) {}
size()7106c3fb27SDimitry Andric   uint32_t size() override { return getMayUseShortThunk() ? 4 : 16; }
720b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
7306c3fb27SDimitry Andric 
7406c3fb27SDimitry Andric private:
7506c3fb27SDimitry Andric   void writeLong(uint8_t *buf) override;
7606c3fb27SDimitry Andric };
7706c3fb27SDimitry Andric 
7806c3fb27SDimitry Andric class AArch64ADRPThunk final : public AArch64Thunk {
7906c3fb27SDimitry Andric public:
AArch64ADRPThunk(Symbol & dest,int64_t addend)8006c3fb27SDimitry Andric   AArch64ADRPThunk(Symbol &dest, int64_t addend) : AArch64Thunk(dest, addend) {}
size()8106c3fb27SDimitry Andric   uint32_t size() override { return getMayUseShortThunk() ? 4 : 12; }
8206c3fb27SDimitry Andric   void addSymbols(ThunkSection &isec) override;
8306c3fb27SDimitry Andric 
8406c3fb27SDimitry Andric private:
8506c3fb27SDimitry Andric   void writeLong(uint8_t *buf) override;
860b57cec5SDimitry Andric };
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric // Base class for ARM thunks.
890b57cec5SDimitry Andric //
900b57cec5SDimitry Andric // An ARM thunk may be either short or long. A short thunk is simply a branch
910b57cec5SDimitry Andric // (B) instruction, and it may be used to call ARM functions when the distance
920b57cec5SDimitry Andric // from the thunk to the target is less than 32MB. Long thunks can branch to any
930b57cec5SDimitry Andric // virtual address and can switch between ARM and Thumb, and they are
940b57cec5SDimitry Andric // implemented in the derived classes. This class tries to create a short thunk
950b57cec5SDimitry Andric // if the target is in range, otherwise it creates a long thunk.
960b57cec5SDimitry Andric class ARMThunk : public Thunk {
970b57cec5SDimitry Andric public:
ARMThunk(Symbol & dest,int64_t addend)98fe6060f1SDimitry Andric   ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   bool getMayUseShortThunk();
size()1010b57cec5SDimitry Andric   uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
1020b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
1030b57cec5SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
1040b57cec5SDimitry Andric                         const Relocation &rel) const override;
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric   // Returns the size of a long thunk.
1070b57cec5SDimitry Andric   virtual uint32_t sizeLong() = 0;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   // Writes a long thunk to Buf.
1100b57cec5SDimitry Andric   virtual void writeLong(uint8_t *buf) = 0;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric private:
1130b57cec5SDimitry Andric   // This field tracks whether all previously considered layouts would allow
1140b57cec5SDimitry Andric   // this thunk to be short. If we have ever needed a long thunk, we always
1150b57cec5SDimitry Andric   // create a long thunk, even if the thunk may be short given the current
1160b57cec5SDimitry Andric   // distance to the target. We do this because transitioning from long to short
1170b57cec5SDimitry Andric   // can create layout oscillations in certain corner cases which would prevent
1180b57cec5SDimitry Andric   // the layout from converging.
1190b57cec5SDimitry Andric   bool mayUseShortThunk = true;
1200b57cec5SDimitry Andric };
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric // Base class for Thumb-2 thunks.
1230b57cec5SDimitry Andric //
1240b57cec5SDimitry Andric // This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
1250b57cec5SDimitry Andric // which has a range of 16MB.
1260b57cec5SDimitry Andric class ThumbThunk : public Thunk {
1270b57cec5SDimitry Andric public:
ThumbThunk(Symbol & dest,int64_t addend)128fe6060f1SDimitry Andric   ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
129fe6060f1SDimitry Andric     alignment = 2;
130fe6060f1SDimitry Andric   }
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   bool getMayUseShortThunk();
size()1330b57cec5SDimitry Andric   uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
1340b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
1350b57cec5SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
1360b57cec5SDimitry Andric                         const Relocation &rel) const override;
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   // Returns the size of a long thunk.
1390b57cec5SDimitry Andric   virtual uint32_t sizeLong() = 0;
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   // Writes a long thunk to Buf.
1420b57cec5SDimitry Andric   virtual void writeLong(uint8_t *buf) = 0;
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric private:
1450b57cec5SDimitry Andric   // See comment in ARMThunk above.
1460b57cec5SDimitry Andric   bool mayUseShortThunk = true;
1470b57cec5SDimitry Andric };
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric // Specific ARM Thunk implementations. The naming convention is:
1500b57cec5SDimitry Andric // Source State, TargetState, Target Requirement, ABS or PI, Range
1510b57cec5SDimitry Andric class ARMV7ABSLongThunk final : public ARMThunk {
1520b57cec5SDimitry Andric public:
ARMV7ABSLongThunk(Symbol & dest,int64_t addend)153fe6060f1SDimitry Andric   ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
1540b57cec5SDimitry Andric 
sizeLong()1550b57cec5SDimitry Andric   uint32_t sizeLong() override { return 12; }
1560b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
1570b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
1580b57cec5SDimitry Andric };
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric class ARMV7PILongThunk final : public ARMThunk {
1610b57cec5SDimitry Andric public:
ARMV7PILongThunk(Symbol & dest,int64_t addend)162fe6060f1SDimitry Andric   ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
1630b57cec5SDimitry Andric 
sizeLong()1640b57cec5SDimitry Andric   uint32_t sizeLong() override { return 16; }
1650b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
1660b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
1670b57cec5SDimitry Andric };
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric class ThumbV7ABSLongThunk final : public ThumbThunk {
1700b57cec5SDimitry Andric public:
ThumbV7ABSLongThunk(Symbol & dest,int64_t addend)171fe6060f1SDimitry Andric   ThumbV7ABSLongThunk(Symbol &dest, int64_t addend)
172fe6060f1SDimitry Andric       : ThumbThunk(dest, addend) {}
1730b57cec5SDimitry Andric 
sizeLong()1740b57cec5SDimitry Andric   uint32_t sizeLong() override { return 10; }
1750b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
1760b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
1770b57cec5SDimitry Andric };
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric class ThumbV7PILongThunk final : public ThumbThunk {
1800b57cec5SDimitry Andric public:
ThumbV7PILongThunk(Symbol & dest,int64_t addend)181fe6060f1SDimitry Andric   ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {}
1820b57cec5SDimitry Andric 
sizeLong()1830b57cec5SDimitry Andric   uint32_t sizeLong() override { return 12; }
1840b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
1850b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
1860b57cec5SDimitry Andric };
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
1890b57cec5SDimitry Andric class ThumbV6MABSLongThunk final : public ThumbThunk {
1900b57cec5SDimitry Andric public:
ThumbV6MABSLongThunk(Symbol & dest,int64_t addend)191fe6060f1SDimitry Andric   ThumbV6MABSLongThunk(Symbol &dest, int64_t addend)
192fe6060f1SDimitry Andric       : ThumbThunk(dest, addend) {}
1930b57cec5SDimitry Andric 
sizeLong()1940b57cec5SDimitry Andric   uint32_t sizeLong() override { return 12; }
1950b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
1960b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
1970b57cec5SDimitry Andric };
1980b57cec5SDimitry Andric 
19906c3fb27SDimitry Andric class ThumbV6MABSXOLongThunk final : public ThumbThunk {
20006c3fb27SDimitry Andric public:
ThumbV6MABSXOLongThunk(Symbol & dest,int64_t addend)20106c3fb27SDimitry Andric   ThumbV6MABSXOLongThunk(Symbol &dest, int64_t addend)
20206c3fb27SDimitry Andric       : ThumbThunk(dest, addend) {}
20306c3fb27SDimitry Andric 
sizeLong()20406c3fb27SDimitry Andric   uint32_t sizeLong() override { return 20; }
20506c3fb27SDimitry Andric   void writeLong(uint8_t *buf) override;
20606c3fb27SDimitry Andric   void addSymbols(ThunkSection &isec) override;
20706c3fb27SDimitry Andric };
20806c3fb27SDimitry Andric 
2090b57cec5SDimitry Andric class ThumbV6MPILongThunk final : public ThumbThunk {
2100b57cec5SDimitry Andric public:
ThumbV6MPILongThunk(Symbol & dest,int64_t addend)211fe6060f1SDimitry Andric   ThumbV6MPILongThunk(Symbol &dest, int64_t addend)
212fe6060f1SDimitry Andric       : ThumbThunk(dest, addend) {}
2130b57cec5SDimitry Andric 
sizeLong()2140b57cec5SDimitry Andric   uint32_t sizeLong() override { return 16; }
2150b57cec5SDimitry Andric   void writeLong(uint8_t *buf) override;
2160b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
2170b57cec5SDimitry Andric };
2180b57cec5SDimitry Andric 
219bdd1243dSDimitry Andric // Architectures v4, v5 and v6 do not support the movt/movw instructions. v5 and
220bdd1243dSDimitry Andric // v6 support BLX to which BL instructions can be rewritten inline. There are no
221bdd1243dSDimitry Andric // Thumb entrypoints for v5 and v6 as there is no Thumb branch instruction on
222bdd1243dSDimitry Andric // these architecture that can result in a thunk.
223bdd1243dSDimitry Andric 
224bdd1243dSDimitry Andric // LDR on v5 and v6 can switch processor state, so for v5 and v6,
225bdd1243dSDimitry Andric // ARMV5LongLdrPcThunk can be used for both Arm->Arm and Arm->Thumb calls. v4
226bdd1243dSDimitry Andric // can also use this thunk, but only for Arm->Arm calls.
227bdd1243dSDimitry Andric class ARMV5LongLdrPcThunk final : public ARMThunk {
228bdd1243dSDimitry Andric public:
ARMV5LongLdrPcThunk(Symbol & dest,int64_t addend)229bdd1243dSDimitry Andric   ARMV5LongLdrPcThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
230bdd1243dSDimitry Andric 
sizeLong()231bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 8; }
232bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
233bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
234bdd1243dSDimitry Andric };
235bdd1243dSDimitry Andric 
236bdd1243dSDimitry Andric // Implementations of Thunks for v4. BLX is not supported, and loads
237bdd1243dSDimitry Andric // will not invoke Arm/Thumb state changes.
238bdd1243dSDimitry Andric class ARMV4PILongBXThunk final : public ARMThunk {
239bdd1243dSDimitry Andric public:
ARMV4PILongBXThunk(Symbol & dest,int64_t addend)240bdd1243dSDimitry Andric   ARMV4PILongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
241bdd1243dSDimitry Andric 
sizeLong()242bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 16; }
243bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
244bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
245bdd1243dSDimitry Andric };
246bdd1243dSDimitry Andric 
247bdd1243dSDimitry Andric class ARMV4PILongThunk final : public ARMThunk {
248bdd1243dSDimitry Andric public:
ARMV4PILongThunk(Symbol & dest,int64_t addend)249bdd1243dSDimitry Andric   ARMV4PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
250bdd1243dSDimitry Andric 
sizeLong()251bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 12; }
252bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
253bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
254bdd1243dSDimitry Andric };
255bdd1243dSDimitry Andric 
256bdd1243dSDimitry Andric class ThumbV4PILongBXThunk final : public ThumbThunk {
257bdd1243dSDimitry Andric public:
ThumbV4PILongBXThunk(Symbol & dest,int64_t addend)258bdd1243dSDimitry Andric   ThumbV4PILongBXThunk(Symbol &dest, int64_t addend)
259bdd1243dSDimitry Andric       : ThumbThunk(dest, addend) {}
260bdd1243dSDimitry Andric 
sizeLong()261bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 16; }
262bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
263bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
264bdd1243dSDimitry Andric };
265bdd1243dSDimitry Andric 
266bdd1243dSDimitry Andric class ThumbV4PILongThunk final : public ThumbThunk {
267bdd1243dSDimitry Andric public:
ThumbV4PILongThunk(Symbol & dest,int64_t addend)268bdd1243dSDimitry Andric   ThumbV4PILongThunk(Symbol &dest, int64_t addend)
269bdd1243dSDimitry Andric       : ThumbThunk(dest, addend) {}
270bdd1243dSDimitry Andric 
sizeLong()271bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 20; }
272bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
273bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
274bdd1243dSDimitry Andric };
275bdd1243dSDimitry Andric 
276bdd1243dSDimitry Andric class ARMV4ABSLongBXThunk final : public ARMThunk {
277bdd1243dSDimitry Andric public:
ARMV4ABSLongBXThunk(Symbol & dest,int64_t addend)278bdd1243dSDimitry Andric   ARMV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
279bdd1243dSDimitry Andric 
sizeLong()280bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 12; }
281bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
282bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
283bdd1243dSDimitry Andric };
284bdd1243dSDimitry Andric 
285bdd1243dSDimitry Andric class ThumbV4ABSLongBXThunk final : public ThumbThunk {
286bdd1243dSDimitry Andric public:
ThumbV4ABSLongBXThunk(Symbol & dest,int64_t addend)287bdd1243dSDimitry Andric   ThumbV4ABSLongBXThunk(Symbol &dest, int64_t addend)
288bdd1243dSDimitry Andric       : ThumbThunk(dest, addend) {}
289bdd1243dSDimitry Andric 
sizeLong()290bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 12; }
291bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
292bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
293bdd1243dSDimitry Andric };
294bdd1243dSDimitry Andric 
295bdd1243dSDimitry Andric class ThumbV4ABSLongThunk final : public ThumbThunk {
296bdd1243dSDimitry Andric public:
ThumbV4ABSLongThunk(Symbol & dest,int64_t addend)297bdd1243dSDimitry Andric   ThumbV4ABSLongThunk(Symbol &dest, int64_t addend)
298bdd1243dSDimitry Andric       : ThumbThunk(dest, addend) {}
299bdd1243dSDimitry Andric 
sizeLong()300bdd1243dSDimitry Andric   uint32_t sizeLong() override { return 16; }
301bdd1243dSDimitry Andric   void writeLong(uint8_t *buf) override;
302bdd1243dSDimitry Andric   void addSymbols(ThunkSection &isec) override;
303bdd1243dSDimitry Andric };
304bdd1243dSDimitry Andric 
30506c3fb27SDimitry Andric // The AVR devices need thunks for R_AVR_LO8_LDI_GS/R_AVR_HI8_LDI_GS
30606c3fb27SDimitry Andric // when their destination is out of range [0, 0x1ffff].
30706c3fb27SDimitry Andric class AVRThunk : public Thunk {
30806c3fb27SDimitry Andric public:
AVRThunk(Symbol & dest,int64_t addend)30906c3fb27SDimitry Andric   AVRThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
size()31006c3fb27SDimitry Andric   uint32_t size() override { return 4; }
31106c3fb27SDimitry Andric   void writeTo(uint8_t *buf) override;
31206c3fb27SDimitry Andric   void addSymbols(ThunkSection &isec) override;
31306c3fb27SDimitry Andric };
31406c3fb27SDimitry Andric 
3150b57cec5SDimitry Andric // MIPS LA25 thunk
3160b57cec5SDimitry Andric class MipsThunk final : public Thunk {
3170b57cec5SDimitry Andric public:
MipsThunk(Symbol & dest)318480093f4SDimitry Andric   MipsThunk(Symbol &dest) : Thunk(dest, 0) {}
3190b57cec5SDimitry Andric 
size()3200b57cec5SDimitry Andric   uint32_t size() override { return 16; }
3210b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
3220b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
3230b57cec5SDimitry Andric   InputSection *getTargetInputSection() const override;
3240b57cec5SDimitry Andric };
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric // microMIPS R2-R5 LA25 thunk
3270b57cec5SDimitry Andric class MicroMipsThunk final : public Thunk {
3280b57cec5SDimitry Andric public:
MicroMipsThunk(Symbol & dest)329480093f4SDimitry Andric   MicroMipsThunk(Symbol &dest) : Thunk(dest, 0) {}
3300b57cec5SDimitry Andric 
size()3310b57cec5SDimitry Andric   uint32_t size() override { return 14; }
3320b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
3330b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
3340b57cec5SDimitry Andric   InputSection *getTargetInputSection() const override;
3350b57cec5SDimitry Andric };
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric // microMIPS R6 LA25 thunk
3380b57cec5SDimitry Andric class MicroMipsR6Thunk final : public Thunk {
3390b57cec5SDimitry Andric public:
MicroMipsR6Thunk(Symbol & dest)340480093f4SDimitry Andric   MicroMipsR6Thunk(Symbol &dest) : Thunk(dest, 0) {}
3410b57cec5SDimitry Andric 
size()3420b57cec5SDimitry Andric   uint32_t size() override { return 12; }
3430b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
3440b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
3450b57cec5SDimitry Andric   InputSection *getTargetInputSection() const override;
3460b57cec5SDimitry Andric };
3470b57cec5SDimitry Andric 
3480b57cec5SDimitry Andric class PPC32PltCallStub final : public Thunk {
3490b57cec5SDimitry Andric public:
350480093f4SDimitry Andric   // For R_PPC_PLTREL24, Thunk::addend records the addend which will be used to
351480093f4SDimitry Andric   // decide the offsets in the call stub.
PPC32PltCallStub(const InputSection & isec,const Relocation & rel,Symbol & dest)352480093f4SDimitry Andric   PPC32PltCallStub(const InputSection &isec, const Relocation &rel,
353480093f4SDimitry Andric                    Symbol &dest)
35413138422SDimitry Andric       : Thunk(dest, rel.addend), file(isec.file) {}
size()3550b57cec5SDimitry Andric   uint32_t size() override { return 16; }
3560b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
3570b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
3580b57cec5SDimitry Andric   bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override;
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric private:
3610b57cec5SDimitry Andric   // Records the call site of the call stub.
3620b57cec5SDimitry Andric   const InputFile *file;
3630b57cec5SDimitry Andric };
3640b57cec5SDimitry Andric 
36513138422SDimitry Andric class PPC32LongThunk final : public Thunk {
36613138422SDimitry Andric public:
PPC32LongThunk(Symbol & dest,int64_t addend)36713138422SDimitry Andric   PPC32LongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
size()36813138422SDimitry Andric   uint32_t size() override { return config->isPic ? 32 : 16; }
36913138422SDimitry Andric   void writeTo(uint8_t *buf) override;
37013138422SDimitry Andric   void addSymbols(ThunkSection &isec) override;
37113138422SDimitry Andric };
37213138422SDimitry Andric 
3730b57cec5SDimitry Andric // PPC64 Plt call stubs.
3740b57cec5SDimitry Andric // Any call site that needs to call through a plt entry needs a call stub in
3750b57cec5SDimitry Andric // the .text section. The call stub is responsible for:
3760b57cec5SDimitry Andric // 1) Saving the toc-pointer to the stack.
3770b57cec5SDimitry Andric // 2) Loading the target functions address from the procedure linkage table into
3780b57cec5SDimitry Andric //    r12 for use by the target functions global entry point, and into the count
3790b57cec5SDimitry Andric //    register.
380480093f4SDimitry Andric // 3) Transferring control to the target function through an indirect branch.
3810b57cec5SDimitry Andric class PPC64PltCallStub final : public Thunk {
3820b57cec5SDimitry Andric public:
PPC64PltCallStub(Symbol & dest)383480093f4SDimitry Andric   PPC64PltCallStub(Symbol &dest) : Thunk(dest, 0) {}
size()3840b57cec5SDimitry Andric   uint32_t size() override { return 20; }
3850b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
3860b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
387e8d8bef9SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
388e8d8bef9SDimitry Andric                         const Relocation &rel) const override;
3890b57cec5SDimitry Andric };
3900b57cec5SDimitry Andric 
3915ffd83dbSDimitry Andric // PPC64 R2 Save Stub
3925ffd83dbSDimitry Andric // When the caller requires a valid R2 TOC pointer but the callee does not
3935ffd83dbSDimitry Andric // require a TOC pointer and the callee cannot guarantee that it doesn't
3945ffd83dbSDimitry Andric // clobber R2 then we need to save R2. This stub:
3955ffd83dbSDimitry Andric // 1) Saves the TOC pointer to the stack.
3965ffd83dbSDimitry Andric // 2) Tail calls the callee.
3975ffd83dbSDimitry Andric class PPC64R2SaveStub final : public Thunk {
3985ffd83dbSDimitry Andric public:
PPC64R2SaveStub(Symbol & dest,int64_t addend)399e8d8bef9SDimitry Andric   PPC64R2SaveStub(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
400e8d8bef9SDimitry Andric     alignment = 16;
401e8d8bef9SDimitry Andric   }
402e8d8bef9SDimitry Andric 
403e8d8bef9SDimitry Andric   // To prevent oscillations in layout when moving from short to long thunks
404e8d8bef9SDimitry Andric   // we make sure that once a thunk has been set to long it cannot go back.
getMayUseShortThunk()405e8d8bef9SDimitry Andric   bool getMayUseShortThunk() {
406e8d8bef9SDimitry Andric     if (!mayUseShortThunk)
407e8d8bef9SDimitry Andric       return false;
408e8d8bef9SDimitry Andric     if (!isInt<26>(computeOffset())) {
409e8d8bef9SDimitry Andric       mayUseShortThunk = false;
410e8d8bef9SDimitry Andric       return false;
411e8d8bef9SDimitry Andric     }
412e8d8bef9SDimitry Andric     return true;
413e8d8bef9SDimitry Andric   }
size()414fe6060f1SDimitry Andric   uint32_t size() override { return getMayUseShortThunk() ? 8 : 32; }
415e8d8bef9SDimitry Andric   void writeTo(uint8_t *buf) override;
416e8d8bef9SDimitry Andric   void addSymbols(ThunkSection &isec) override;
417fe6060f1SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
418fe6060f1SDimitry Andric                         const Relocation &rel) const override;
419e8d8bef9SDimitry Andric 
420e8d8bef9SDimitry Andric private:
421e8d8bef9SDimitry Andric   // Transitioning from long to short can create layout oscillations in
422e8d8bef9SDimitry Andric   // certain corner cases which would prevent the layout from converging.
423e8d8bef9SDimitry Andric   // This is similar to the handling for ARMThunk.
424e8d8bef9SDimitry Andric   bool mayUseShortThunk = true;
computeOffset() const425e8d8bef9SDimitry Andric   int64_t computeOffset() const {
426e8d8bef9SDimitry Andric     return destination.getVA() - (getThunkTargetSym()->getVA() + 4);
427e8d8bef9SDimitry Andric   }
428e8d8bef9SDimitry Andric };
429e8d8bef9SDimitry Andric 
430e8d8bef9SDimitry Andric // PPC64 R12 Setup Stub
43106c3fb27SDimitry Andric // When a caller that does not maintain TOC calls a target which may possibly
43206c3fb27SDimitry Andric // use TOC (either non-preemptible with localentry>1 or preemptible), we need to
43306c3fb27SDimitry Andric // set r12 to satisfy the requirement of the global entry point.
434e8d8bef9SDimitry Andric class PPC64R12SetupStub final : public Thunk {
435e8d8bef9SDimitry Andric public:
PPC64R12SetupStub(Symbol & dest,bool gotPlt)43606c3fb27SDimitry Andric   PPC64R12SetupStub(Symbol &dest, bool gotPlt)
43706c3fb27SDimitry Andric       : Thunk(dest, 0), gotPlt(gotPlt) {
43806c3fb27SDimitry Andric     alignment = 16;
43906c3fb27SDimitry Andric   }
size()440fe6060f1SDimitry Andric   uint32_t size() override { return 32; }
4415ffd83dbSDimitry Andric   void writeTo(uint8_t *buf) override;
4425ffd83dbSDimitry Andric   void addSymbols(ThunkSection &isec) override;
443fe6060f1SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
444fe6060f1SDimitry Andric                         const Relocation &rel) const override;
4455ffd83dbSDimitry Andric 
44606c3fb27SDimitry Andric private:
44706c3fb27SDimitry Andric   bool gotPlt;
448e8d8bef9SDimitry Andric };
449e8d8bef9SDimitry Andric 
4500b57cec5SDimitry Andric // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
4510b57cec5SDimitry Andric // alignment. This gives a possible 26 bits of 'reach'. If the call offset is
452e8d8bef9SDimitry Andric // larger than that we need to emit a long-branch thunk. The target address
4530b57cec5SDimitry Andric // of the callee is stored in a table to be accessed TOC-relative. Since the
4540b57cec5SDimitry Andric // call must be local (a non-local call will have a PltCallStub instead) the
4550b57cec5SDimitry Andric // table stores the address of the callee's local entry point. For
4560b57cec5SDimitry Andric // position-independent code a corresponding relative dynamic relocation is
4570b57cec5SDimitry Andric // used.
4580b57cec5SDimitry Andric class PPC64LongBranchThunk : public Thunk {
4590b57cec5SDimitry Andric public:
size()460fe6060f1SDimitry Andric   uint32_t size() override { return 32; }
4610b57cec5SDimitry Andric   void writeTo(uint8_t *buf) override;
4620b57cec5SDimitry Andric   void addSymbols(ThunkSection &isec) override;
463e8d8bef9SDimitry Andric   bool isCompatibleWith(const InputSection &isec,
464e8d8bef9SDimitry Andric                         const Relocation &rel) const override;
4650b57cec5SDimitry Andric 
4660b57cec5SDimitry Andric protected:
PPC64LongBranchThunk(Symbol & dest,int64_t addend)467480093f4SDimitry Andric   PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
4680b57cec5SDimitry Andric };
4690b57cec5SDimitry Andric 
4700b57cec5SDimitry Andric class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
4710b57cec5SDimitry Andric public:
PPC64PILongBranchThunk(Symbol & dest,int64_t addend)472480093f4SDimitry Andric   PPC64PILongBranchThunk(Symbol &dest, int64_t addend)
473480093f4SDimitry Andric       : PPC64LongBranchThunk(dest, addend) {
4740b57cec5SDimitry Andric     assert(!dest.isPreemptible);
475bdd1243dSDimitry Andric     if (std::optional<uint32_t> index =
476480093f4SDimitry Andric             in.ppc64LongBranchTarget->addEntry(&dest, addend)) {
477fe6060f1SDimitry Andric       mainPart->relaDyn->addRelativeReloc(
4780eae32dcSDimitry Andric           target->relativeRel, *in.ppc64LongBranchTarget, *index * UINT64_C(8),
479fe6060f1SDimitry Andric           dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther),
480fe6060f1SDimitry Andric           target->symbolicRel, R_ABS);
481480093f4SDimitry Andric     }
4820b57cec5SDimitry Andric   }
4830b57cec5SDimitry Andric };
4840b57cec5SDimitry Andric 
4850b57cec5SDimitry Andric class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
4860b57cec5SDimitry Andric public:
PPC64PDLongBranchThunk(Symbol & dest,int64_t addend)487480093f4SDimitry Andric   PPC64PDLongBranchThunk(Symbol &dest, int64_t addend)
488480093f4SDimitry Andric       : PPC64LongBranchThunk(dest, addend) {
489480093f4SDimitry Andric     in.ppc64LongBranchTarget->addEntry(&dest, addend);
4900b57cec5SDimitry Andric   }
4910b57cec5SDimitry Andric };
4920b57cec5SDimitry Andric 
4930b57cec5SDimitry Andric } // end anonymous namespace
4940b57cec5SDimitry Andric 
addSymbol(StringRef name,uint8_t type,uint64_t value,InputSectionBase & section)4950b57cec5SDimitry Andric Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
4960b57cec5SDimitry Andric                           InputSectionBase &section) {
4970b57cec5SDimitry Andric   Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section);
4980b57cec5SDimitry Andric   syms.push_back(d);
4990b57cec5SDimitry Andric   return d;
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric 
setOffset(uint64_t newOffset)5020b57cec5SDimitry Andric void Thunk::setOffset(uint64_t newOffset) {
5030b57cec5SDimitry Andric   for (Defined *d : syms)
5040b57cec5SDimitry Andric     d->value = d->value - offset + newOffset;
5050b57cec5SDimitry Andric   offset = newOffset;
5060b57cec5SDimitry Andric }
5070b57cec5SDimitry Andric 
50806c3fb27SDimitry Andric // AArch64 Thunk base class.
getAArch64ThunkDestVA(const Symbol & s,int64_t a)509480093f4SDimitry Andric static uint64_t getAArch64ThunkDestVA(const Symbol &s, int64_t a) {
510480093f4SDimitry Andric   uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(a);
5110b57cec5SDimitry Andric   return v;
5120b57cec5SDimitry Andric }
5130b57cec5SDimitry Andric 
getMayUseShortThunk()51406c3fb27SDimitry Andric bool AArch64Thunk::getMayUseShortThunk() {
51506c3fb27SDimitry Andric   if (!mayUseShortThunk)
51606c3fb27SDimitry Andric     return false;
51706c3fb27SDimitry Andric   uint64_t s = getAArch64ThunkDestVA(destination, addend);
51806c3fb27SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
51906c3fb27SDimitry Andric   mayUseShortThunk = llvm::isInt<28>(s - p);
52006c3fb27SDimitry Andric   return mayUseShortThunk;
52106c3fb27SDimitry Andric }
52206c3fb27SDimitry Andric 
writeTo(uint8_t * buf)52306c3fb27SDimitry Andric void AArch64Thunk::writeTo(uint8_t *buf) {
52406c3fb27SDimitry Andric   if (!getMayUseShortThunk()) {
52506c3fb27SDimitry Andric     writeLong(buf);
52606c3fb27SDimitry Andric     return;
52706c3fb27SDimitry Andric   }
52806c3fb27SDimitry Andric   uint64_t s = getAArch64ThunkDestVA(destination, addend);
52906c3fb27SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
53006c3fb27SDimitry Andric   write32(buf, 0x14000000); // b S
53106c3fb27SDimitry Andric   target->relocateNoSym(buf, R_AARCH64_CALL26, s - p);
53206c3fb27SDimitry Andric }
53306c3fb27SDimitry Andric 
53406c3fb27SDimitry Andric // AArch64 long range Thunks.
writeLong(uint8_t * buf)53506c3fb27SDimitry Andric void AArch64ABSLongThunk::writeLong(uint8_t *buf) {
5360b57cec5SDimitry Andric   const uint8_t data[] = {
5370b57cec5SDimitry Andric     0x50, 0x00, 0x00, 0x58, //     ldr x16, L0
5380b57cec5SDimitry Andric     0x00, 0x02, 0x1f, 0xd6, //     br  x16
5390b57cec5SDimitry Andric     0x00, 0x00, 0x00, 0x00, // L0: .xword S
5400b57cec5SDimitry Andric     0x00, 0x00, 0x00, 0x00,
5410b57cec5SDimitry Andric   };
542480093f4SDimitry Andric   uint64_t s = getAArch64ThunkDestVA(destination, addend);
5430b57cec5SDimitry Andric   memcpy(buf, data, sizeof(data));
5445ffd83dbSDimitry Andric   target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s);
5450b57cec5SDimitry Andric }
5460b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)5470b57cec5SDimitry Andric void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
54804eeddc0SDimitry Andric   addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()),
5490b57cec5SDimitry Andric             STT_FUNC, 0, isec);
5500b57cec5SDimitry Andric   addSymbol("$x", STT_NOTYPE, 0, isec);
55106c3fb27SDimitry Andric   if (!getMayUseShortThunk())
5520b57cec5SDimitry Andric     addSymbol("$d", STT_NOTYPE, 8, isec);
5530b57cec5SDimitry Andric }
5540b57cec5SDimitry Andric 
5550b57cec5SDimitry Andric // This Thunk has a maximum range of 4Gb, this is sufficient for all programs
5560b57cec5SDimitry Andric // using the small code model, including pc-relative ones. At time of writing
5570b57cec5SDimitry Andric // clang and gcc do not support the large code model for position independent
5580b57cec5SDimitry Andric // code so it is safe to use this for position independent thunks without
5590b57cec5SDimitry Andric // worrying about the destination being more than 4Gb away.
writeLong(uint8_t * buf)56006c3fb27SDimitry Andric void AArch64ADRPThunk::writeLong(uint8_t *buf) {
5610b57cec5SDimitry Andric   const uint8_t data[] = {
5620b57cec5SDimitry Andric       0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
5630b57cec5SDimitry Andric       0x10, 0x02, 0x00, 0x91, // add  x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
5640b57cec5SDimitry Andric       0x00, 0x02, 0x1f, 0xd6, // br   x16
5650b57cec5SDimitry Andric   };
566480093f4SDimitry Andric   uint64_t s = getAArch64ThunkDestVA(destination, addend);
5670b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
5680b57cec5SDimitry Andric   memcpy(buf, data, sizeof(data));
5695ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
5700b57cec5SDimitry Andric                         getAArch64Page(s) - getAArch64Page(p));
5715ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
5720b57cec5SDimitry Andric }
5730b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)5740b57cec5SDimitry Andric void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
57504eeddc0SDimitry Andric   addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()),
57604eeddc0SDimitry Andric             STT_FUNC, 0, isec);
5770b57cec5SDimitry Andric   addSymbol("$x", STT_NOTYPE, 0, isec);
5780b57cec5SDimitry Andric }
5790b57cec5SDimitry Andric 
5800b57cec5SDimitry Andric // ARM Target Thunks
getARMThunkDestVA(const Symbol & s)5810b57cec5SDimitry Andric static uint64_t getARMThunkDestVA(const Symbol &s) {
5820b57cec5SDimitry Andric   uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
5830b57cec5SDimitry Andric   return SignExtend64<32>(v);
5840b57cec5SDimitry Andric }
5850b57cec5SDimitry Andric 
5860b57cec5SDimitry Andric // This function returns true if the target is not Thumb and is within 2^26, and
5870b57cec5SDimitry Andric // it has not previously returned false (see comment for mayUseShortThunk).
getMayUseShortThunk()5880b57cec5SDimitry Andric bool ARMThunk::getMayUseShortThunk() {
5890b57cec5SDimitry Andric   if (!mayUseShortThunk)
5900b57cec5SDimitry Andric     return false;
5910b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
5920b57cec5SDimitry Andric   if (s & 1) {
5930b57cec5SDimitry Andric     mayUseShortThunk = false;
5940b57cec5SDimitry Andric     return false;
5950b57cec5SDimitry Andric   }
5960b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
5970b57cec5SDimitry Andric   int64_t offset = s - p - 8;
5980b57cec5SDimitry Andric   mayUseShortThunk = llvm::isInt<26>(offset);
5990b57cec5SDimitry Andric   return mayUseShortThunk;
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric 
writeTo(uint8_t * buf)6020b57cec5SDimitry Andric void ARMThunk::writeTo(uint8_t *buf) {
6030b57cec5SDimitry Andric   if (!getMayUseShortThunk()) {
6040b57cec5SDimitry Andric     writeLong(buf);
6050b57cec5SDimitry Andric     return;
6060b57cec5SDimitry Andric   }
6070b57cec5SDimitry Andric 
6080b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
6090b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
6100b57cec5SDimitry Andric   int64_t offset = s - p - 8;
61106c3fb27SDimitry Andric   write32(buf, 0xea000000); // b S
6125ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_JUMP24, offset);
6130b57cec5SDimitry Andric }
6140b57cec5SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const6150b57cec5SDimitry Andric bool ARMThunk::isCompatibleWith(const InputSection &isec,
6160b57cec5SDimitry Andric                                 const Relocation &rel) const {
617bdd1243dSDimitry Andric   // v4T does not have BLX, so also deny R_ARM_THM_CALL
618bdd1243dSDimitry Andric   if (!config->armHasBlx && rel.type == R_ARM_THM_CALL)
619bdd1243dSDimitry Andric     return false;
620bdd1243dSDimitry Andric 
6210b57cec5SDimitry Andric   // Thumb branch relocations can't use BLX
6220b57cec5SDimitry Andric   return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
6230b57cec5SDimitry Andric }
6240b57cec5SDimitry Andric 
625bdd1243dSDimitry Andric // This function returns true if:
626bdd1243dSDimitry Andric // the target is Thumb
627bdd1243dSDimitry Andric // && is within branch range
628bdd1243dSDimitry Andric // && this function has not previously returned false
629bdd1243dSDimitry Andric //    (see comment for mayUseShortThunk)
630bdd1243dSDimitry Andric // && the arch supports Thumb branch range extension.
getMayUseShortThunk()6310b57cec5SDimitry Andric bool ThumbThunk::getMayUseShortThunk() {
632bdd1243dSDimitry Andric   if (!mayUseShortThunk || !config->armJ1J2BranchEncoding)
6330b57cec5SDimitry Andric     return false;
6340b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
6350b57cec5SDimitry Andric   if ((s & 1) == 0) {
6360b57cec5SDimitry Andric     mayUseShortThunk = false;
6370b57cec5SDimitry Andric     return false;
6380b57cec5SDimitry Andric   }
6390b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~1;
6400b57cec5SDimitry Andric   int64_t offset = s - p - 4;
6410b57cec5SDimitry Andric   mayUseShortThunk = llvm::isInt<25>(offset);
6420b57cec5SDimitry Andric   return mayUseShortThunk;
6430b57cec5SDimitry Andric }
6440b57cec5SDimitry Andric 
writeTo(uint8_t * buf)6450b57cec5SDimitry Andric void ThumbThunk::writeTo(uint8_t *buf) {
6460b57cec5SDimitry Andric   if (!getMayUseShortThunk()) {
6470b57cec5SDimitry Andric     writeLong(buf);
6480b57cec5SDimitry Andric     return;
6490b57cec5SDimitry Andric   }
6500b57cec5SDimitry Andric 
6510b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
6520b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
6530b57cec5SDimitry Andric   int64_t offset = s - p - 4;
65406c3fb27SDimitry Andric   write16(buf + 0, 0xf000); // b.w S
65506c3fb27SDimitry Andric   write16(buf + 2, 0xb000);
6565ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset);
6570b57cec5SDimitry Andric }
6580b57cec5SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const6590b57cec5SDimitry Andric bool ThumbThunk::isCompatibleWith(const InputSection &isec,
6600b57cec5SDimitry Andric                                   const Relocation &rel) const {
661bdd1243dSDimitry Andric   // v4T does not have BLX, so also deny R_ARM_CALL
662bdd1243dSDimitry Andric   if (!config->armHasBlx && rel.type == R_ARM_CALL)
663bdd1243dSDimitry Andric     return false;
664bdd1243dSDimitry Andric 
6650b57cec5SDimitry Andric   // ARM branch relocations can't use BLX
6660b57cec5SDimitry Andric   return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
6670b57cec5SDimitry Andric }
6680b57cec5SDimitry Andric 
writeLong(uint8_t * buf)6690b57cec5SDimitry Andric void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
67006c3fb27SDimitry Andric   write32(buf + 0, 0xe300c000); // movw ip,:lower16:S
67106c3fb27SDimitry Andric   write32(buf + 4, 0xe340c000); // movt ip,:upper16:S
67206c3fb27SDimitry Andric   write32(buf + 8, 0xe12fff1c); // bx   ip
6730b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
6745ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s);
6755ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s);
6760b57cec5SDimitry Andric }
6770b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)6780b57cec5SDimitry Andric void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
67904eeddc0SDimitry Andric   addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()),
6800b57cec5SDimitry Andric             STT_FUNC, 0, isec);
6810b57cec5SDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
6820b57cec5SDimitry Andric }
6830b57cec5SDimitry Andric 
writeLong(uint8_t * buf)6840b57cec5SDimitry Andric void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
68506c3fb27SDimitry Andric   write16(buf + 0, 0xf240); // movw ip, :lower16:S
68606c3fb27SDimitry Andric   write16(buf + 2, 0x0c00);
68706c3fb27SDimitry Andric   write16(buf + 4, 0xf2c0); // movt ip, :upper16:S
68806c3fb27SDimitry Andric   write16(buf + 6, 0x0c00);
68906c3fb27SDimitry Andric   write16(buf + 8, 0x4760); // bx   ip
6900b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
6915ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s);
6925ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s);
6930b57cec5SDimitry Andric }
6940b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)6950b57cec5SDimitry Andric void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
69604eeddc0SDimitry Andric   addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()),
6970b57cec5SDimitry Andric             STT_FUNC, 1, isec);
6980b57cec5SDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
6990b57cec5SDimitry Andric }
7000b57cec5SDimitry Andric 
writeLong(uint8_t * buf)7010b57cec5SDimitry Andric void ARMV7PILongThunk::writeLong(uint8_t *buf) {
70206c3fb27SDimitry Andric   write32(buf + 0, 0xe30fcff0);   // P:  movw ip,:lower16:S - (P + (L1-P) + 8)
70306c3fb27SDimitry Andric   write32(buf + 4, 0xe340c000);   //     movt ip,:upper16:S - (P + (L1-P) + 8)
70406c3fb27SDimitry Andric   write32(buf + 8, 0xe08cc00f);   // L1: add  ip, ip, pc
70506c3fb27SDimitry Andric   write32(buf + 12, 0xe12fff1c);  //     bx   ip
7060b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
7070b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
7080b57cec5SDimitry Andric   int64_t offset = s - p - 16;
7095ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset);
7105ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset);
7110b57cec5SDimitry Andric }
7120b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)7130b57cec5SDimitry Andric void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
71404eeddc0SDimitry Andric   addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()),
71504eeddc0SDimitry Andric             STT_FUNC, 0, isec);
7160b57cec5SDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
7170b57cec5SDimitry Andric }
7180b57cec5SDimitry Andric 
writeLong(uint8_t * buf)7190b57cec5SDimitry Andric void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
72006c3fb27SDimitry Andric   write16(buf + 0, 0xf64f);   // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
72106c3fb27SDimitry Andric   write16(buf + 2, 0x7cf4);
72206c3fb27SDimitry Andric   write16(buf + 4, 0xf2c0);   //     movt ip,:upper16:S - (P + (L1-P) + 4)
72306c3fb27SDimitry Andric   write16(buf + 6, 0x0c00);
72406c3fb27SDimitry Andric   write16(buf + 8, 0x44fc);   // L1: add  ip, pc
72506c3fb27SDimitry Andric   write16(buf + 10, 0x4760);  //     bx   ip
7260b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
7270b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
7280b57cec5SDimitry Andric   int64_t offset = s - p - 12;
7295ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset);
7305ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset);
7310b57cec5SDimitry Andric }
7320b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)7330b57cec5SDimitry Andric void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
73404eeddc0SDimitry Andric   addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()),
7350b57cec5SDimitry Andric             STT_FUNC, 1, isec);
7360b57cec5SDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
7370b57cec5SDimitry Andric }
7380b57cec5SDimitry Andric 
writeLong(uint8_t * buf)7390b57cec5SDimitry Andric void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
7400b57cec5SDimitry Andric   // Most Thumb instructions cannot access the high registers r8 - r15. As the
7410b57cec5SDimitry Andric   // only register we can corrupt is r12 we must instead spill a low register
7420b57cec5SDimitry Andric   // to the stack to use as a scratch register. We push r1 even though we
7430b57cec5SDimitry Andric   // don't need to get some space to use for the return address.
74406c3fb27SDimitry Andric   write16(buf + 0, 0xb403);   // push {r0, r1} ; Obtain scratch registers
74506c3fb27SDimitry Andric   write16(buf + 2, 0x4801);   // ldr r0, [pc, #4] ; L1
74606c3fb27SDimitry Andric   write16(buf + 4, 0x9001);   // str r0, [sp, #4] ; SP + 4 = S
74706c3fb27SDimitry Andric   write16(buf + 6, 0xbd01);   // pop {r0, pc} ; restore r0 and branch to dest
74806c3fb27SDimitry Andric   write32(buf + 8, 0x00000000);   // L1: .word S
7490b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
7505ffd83dbSDimitry Andric   target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
7510b57cec5SDimitry Andric }
7520b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)7530b57cec5SDimitry Andric void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
75404eeddc0SDimitry Andric   addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()),
7550b57cec5SDimitry Andric             STT_FUNC, 1, isec);
7560b57cec5SDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
75706c3fb27SDimitry Andric   if (!getMayUseShortThunk())
7580b57cec5SDimitry Andric     addSymbol("$d", STT_NOTYPE, 8, isec);
7590b57cec5SDimitry Andric }
7600b57cec5SDimitry Andric 
writeLong(uint8_t * buf)76106c3fb27SDimitry Andric void ThumbV6MABSXOLongThunk::writeLong(uint8_t *buf) {
76206c3fb27SDimitry Andric   // Most Thumb instructions cannot access the high registers r8 - r15. As the
76306c3fb27SDimitry Andric   // only register we can corrupt is r12 we must instead spill a low register
76406c3fb27SDimitry Andric   // to the stack to use as a scratch register. We push r1 even though we
76506c3fb27SDimitry Andric   // don't need to get some space to use for the return address.
76606c3fb27SDimitry Andric   write16(buf + 0, 0xb403);  // push {r0, r1} ; Obtain scratch registers
76706c3fb27SDimitry Andric   write16(buf + 2, 0x2000);  // movs r0, :upper8_15:S
76806c3fb27SDimitry Andric   write16(buf + 4, 0x0200);  // lsls r0, r0, #8
76906c3fb27SDimitry Andric   write16(buf + 6, 0x3000);  // adds r0, :upper0_7:S
77006c3fb27SDimitry Andric   write16(buf + 8, 0x0200);  // lsls r0, r0, #8
77106c3fb27SDimitry Andric   write16(buf + 10, 0x3000); // adds r0, :lower8_15:S
77206c3fb27SDimitry Andric   write16(buf + 12, 0x0200); // lsls r0, r0, #8
77306c3fb27SDimitry Andric   write16(buf + 14, 0x3000); // adds r0, :lower0_7:S
77406c3fb27SDimitry Andric   write16(buf + 16, 0x9001); // str r0, [sp, #4] ; SP + 4 = S
77506c3fb27SDimitry Andric   write16(buf + 18, 0xbd01); // pop {r0, pc} ; restore r0 and branch to dest
77606c3fb27SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
77706c3fb27SDimitry Andric   target->relocateNoSym(buf + 2, R_ARM_THM_ALU_ABS_G3, s);
77806c3fb27SDimitry Andric   target->relocateNoSym(buf + 6, R_ARM_THM_ALU_ABS_G2_NC, s);
77906c3fb27SDimitry Andric   target->relocateNoSym(buf + 10, R_ARM_THM_ALU_ABS_G1_NC, s);
78006c3fb27SDimitry Andric   target->relocateNoSym(buf + 14, R_ARM_THM_ALU_ABS_G0_NC, s);
78106c3fb27SDimitry Andric }
78206c3fb27SDimitry Andric 
addSymbols(ThunkSection & isec)78306c3fb27SDimitry Andric void ThumbV6MABSXOLongThunk::addSymbols(ThunkSection &isec) {
78406c3fb27SDimitry Andric   addSymbol(saver().save("__Thumbv6MABSXOLongThunk_" + destination.getName()),
78506c3fb27SDimitry Andric             STT_FUNC, 1, isec);
78606c3fb27SDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
78706c3fb27SDimitry Andric }
78806c3fb27SDimitry Andric 
writeLong(uint8_t * buf)7890b57cec5SDimitry Andric void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
7900b57cec5SDimitry Andric   // Most Thumb instructions cannot access the high registers r8 - r15. As the
7910b57cec5SDimitry Andric   // only register we can corrupt is ip (r12) we must instead spill a low
7920b57cec5SDimitry Andric   // register to the stack to use as a scratch register.
79306c3fb27SDimitry Andric   write16(buf + 0, 0xb401);   // P:  push {r0}        ; Obtain scratch register
79406c3fb27SDimitry Andric   write16(buf + 2, 0x4802);   //     ldr r0, [pc, #8] ; L2
79506c3fb27SDimitry Andric   write16(buf + 4, 0x4684);   //     mov ip, r0       ; high to low register
79606c3fb27SDimitry Andric   write16(buf + 6, 0xbc01);   //     pop {r0}         ; restore scratch register
79706c3fb27SDimitry Andric   write16(buf + 8, 0x44e7);   // L1: add pc, ip       ; transfer control
79806c3fb27SDimitry Andric   write16(buf + 10, 0x46c0);  //     nop              ; pad to 4-byte boundary
79906c3fb27SDimitry Andric   write32(buf + 12, 0x00000000);  // L2: .word S - (P + (L1 - P) + 4)
8000b57cec5SDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
8010b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
8025ffd83dbSDimitry Andric   target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
8030b57cec5SDimitry Andric }
8040b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)8050b57cec5SDimitry Andric void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
80604eeddc0SDimitry Andric   addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()),
8070b57cec5SDimitry Andric             STT_FUNC, 1, isec);
8080b57cec5SDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
80906c3fb27SDimitry Andric   if (!getMayUseShortThunk())
8100b57cec5SDimitry Andric     addSymbol("$d", STT_NOTYPE, 12, isec);
8110b57cec5SDimitry Andric }
8120b57cec5SDimitry Andric 
writeLong(uint8_t * buf)813bdd1243dSDimitry Andric void ARMV5LongLdrPcThunk::writeLong(uint8_t *buf) {
81406c3fb27SDimitry Andric   write32(buf + 0, 0xe51ff004); // ldr pc, [pc,#-4] ; L1
81506c3fb27SDimitry Andric   write32(buf + 4, 0x00000000); // L1: .word S
816bdd1243dSDimitry Andric   target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
817bdd1243dSDimitry Andric }
818bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)819bdd1243dSDimitry Andric void ARMV5LongLdrPcThunk::addSymbols(ThunkSection &isec) {
820bdd1243dSDimitry Andric   addSymbol(saver().save("__ARMv5LongLdrPcThunk_" + destination.getName()),
821bdd1243dSDimitry Andric             STT_FUNC, 0, isec);
822bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
82306c3fb27SDimitry Andric   if (!getMayUseShortThunk())
824bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 4, isec);
825bdd1243dSDimitry Andric }
826bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)827bdd1243dSDimitry Andric void ARMV4ABSLongBXThunk::writeLong(uint8_t *buf) {
82806c3fb27SDimitry Andric   write32(buf + 0, 0xe59fc000); // ldr r12, [pc] ; L1
82906c3fb27SDimitry Andric   write32(buf + 4, 0xe12fff1c); // bx r12
83006c3fb27SDimitry Andric   write32(buf + 8, 0x00000000); // L1: .word S
831bdd1243dSDimitry Andric   target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination));
832bdd1243dSDimitry Andric }
833bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)834bdd1243dSDimitry Andric void ARMV4ABSLongBXThunk::addSymbols(ThunkSection &isec) {
835bdd1243dSDimitry Andric   addSymbol(saver().save("__ARMv4ABSLongBXThunk_" + destination.getName()),
836bdd1243dSDimitry Andric             STT_FUNC, 0, isec);
837bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
83806c3fb27SDimitry Andric   if (!getMayUseShortThunk())
839bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 8, isec);
840bdd1243dSDimitry Andric }
841bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)842bdd1243dSDimitry Andric void ThumbV4ABSLongBXThunk::writeLong(uint8_t *buf) {
84306c3fb27SDimitry Andric   write16(buf + 0, 0x4778); // bx pc
84406c3fb27SDimitry Andric   write16(buf + 2, 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc
84506c3fb27SDimitry Andric   write32(buf + 4, 0xe51ff004); // ldr pc, [pc, #-4] ; L1
84606c3fb27SDimitry Andric   write32(buf + 8, 0x00000000); // L1: .word S
847bdd1243dSDimitry Andric   target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination));
848bdd1243dSDimitry Andric }
849bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)850bdd1243dSDimitry Andric void ThumbV4ABSLongBXThunk::addSymbols(ThunkSection &isec) {
851bdd1243dSDimitry Andric   addSymbol(saver().save("__Thumbv4ABSLongBXThunk_" + destination.getName()),
852bdd1243dSDimitry Andric             STT_FUNC, 1, isec);
853bdd1243dSDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
854bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 4, isec);
85506c3fb27SDimitry Andric   if (!getMayUseShortThunk())
856bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 8, isec);
857bdd1243dSDimitry Andric }
858bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)859bdd1243dSDimitry Andric void ThumbV4ABSLongThunk::writeLong(uint8_t *buf) {
86006c3fb27SDimitry Andric   write16(buf + 0, 0x4778); // bx pc
86106c3fb27SDimitry Andric   write16(buf + 2, 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc
86206c3fb27SDimitry Andric   write32(buf + 4, 0xe59fc000); // ldr r12, [pc] ; L1
86306c3fb27SDimitry Andric   write32(buf + 8, 0xe12fff1c); // bx r12
86406c3fb27SDimitry Andric   write32(buf + 12, 0x00000000); // L1: .word S
865bdd1243dSDimitry Andric   target->relocateNoSym(buf + 12, R_ARM_ABS32, getARMThunkDestVA(destination));
866bdd1243dSDimitry Andric }
867bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)868bdd1243dSDimitry Andric void ThumbV4ABSLongThunk::addSymbols(ThunkSection &isec) {
869bdd1243dSDimitry Andric   addSymbol(saver().save("__Thumbv4ABSLongThunk_" + destination.getName()),
870bdd1243dSDimitry Andric             STT_FUNC, 1, isec);
871bdd1243dSDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
872bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 4, isec);
87306c3fb27SDimitry Andric   if (!getMayUseShortThunk())
874bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 12, isec);
875bdd1243dSDimitry Andric }
876bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)877bdd1243dSDimitry Andric void ARMV4PILongBXThunk::writeLong(uint8_t *buf) {
87806c3fb27SDimitry Andric   write32(buf + 0, 0xe59fc004); // P:  ldr ip, [pc,#4] ; L2
87906c3fb27SDimitry Andric   write32(buf + 4, 0xe08fc00c);	// L1: add ip, pc, ip
88006c3fb27SDimitry Andric   write32(buf + 8, 0xe12fff1c);	//     bx ip
88106c3fb27SDimitry Andric   write32(buf + 12, 0x00000000); // L2: .word S - (P + (L1 - P) + 8)
882bdd1243dSDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
883bdd1243dSDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
884bdd1243dSDimitry Andric   target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
885bdd1243dSDimitry Andric }
886bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)887bdd1243dSDimitry Andric void ARMV4PILongBXThunk::addSymbols(ThunkSection &isec) {
888bdd1243dSDimitry Andric   addSymbol(saver().save("__ARMv4PILongBXThunk_" + destination.getName()),
889bdd1243dSDimitry Andric             STT_FUNC, 0, isec);
890bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
89106c3fb27SDimitry Andric   if (!getMayUseShortThunk())
892bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 12, isec);
893bdd1243dSDimitry Andric }
894bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)895bdd1243dSDimitry Andric void ARMV4PILongThunk::writeLong(uint8_t *buf) {
89606c3fb27SDimitry Andric   write32(buf + 0, 0xe59fc000); // P:  ldr ip, [pc] ; L2
89706c3fb27SDimitry Andric   write32(buf + 4, 0xe08ff00c); // L1: add pc, pc, r12
89806c3fb27SDimitry Andric   write32(buf + 8, 0x00000000); // L2: .word S - (P + (L1 - P) + 8)
899bdd1243dSDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
900bdd1243dSDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
901bdd1243dSDimitry Andric   target->relocateNoSym(buf + 8, R_ARM_REL32, s - p - 12);
902bdd1243dSDimitry Andric }
903bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)904bdd1243dSDimitry Andric void ARMV4PILongThunk::addSymbols(ThunkSection &isec) {
905bdd1243dSDimitry Andric   addSymbol(saver().save("__ARMv4PILongThunk_" + destination.getName()),
906bdd1243dSDimitry Andric             STT_FUNC, 0, isec);
907bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 0, isec);
90806c3fb27SDimitry Andric   if (!getMayUseShortThunk())
909bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 8, isec);
910bdd1243dSDimitry Andric }
911bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)912bdd1243dSDimitry Andric void ThumbV4PILongBXThunk::writeLong(uint8_t *buf) {
91306c3fb27SDimitry Andric   write16(buf + 0, 0x4778); // P:  bx pc
91406c3fb27SDimitry Andric   write16(buf + 2, 0xe7fd); //     b #-6 ; Arm recommended sequence to follow bx pc
91506c3fb27SDimitry Andric   write32(buf + 4, 0xe59fc000); //     ldr r12, [pc] ; L2
91606c3fb27SDimitry Andric   write32(buf + 8, 0xe08cf00f); // L1: add pc, r12, pc
91706c3fb27SDimitry Andric   write32(buf + 12, 0x00000000); // L2: .word S - (P + (L1 - P) + 8)
918bdd1243dSDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
919bdd1243dSDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
920bdd1243dSDimitry Andric   target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 16);
921bdd1243dSDimitry Andric }
922bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)923bdd1243dSDimitry Andric void ThumbV4PILongBXThunk::addSymbols(ThunkSection &isec) {
924bdd1243dSDimitry Andric   addSymbol(saver().save("__Thumbv4PILongBXThunk_" + destination.getName()),
925bdd1243dSDimitry Andric             STT_FUNC, 1, isec);
926bdd1243dSDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
927bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 4, isec);
92806c3fb27SDimitry Andric   if (!getMayUseShortThunk())
929bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 12, isec);
930bdd1243dSDimitry Andric }
931bdd1243dSDimitry Andric 
writeLong(uint8_t * buf)932bdd1243dSDimitry Andric void ThumbV4PILongThunk::writeLong(uint8_t *buf) {
93306c3fb27SDimitry Andric   write16(buf + 0, 0x4778); // P:  bx pc
93406c3fb27SDimitry Andric   write16(buf + 2, 0xe7fd); //     b #-6 ; Arm recommended sequence to follow bx pc
93506c3fb27SDimitry Andric   write32(buf + 4, 0xe59fc004); //     ldr ip, [pc,#4] ; L2
93606c3fb27SDimitry Andric   write32(buf + 8, 0xe08fc00c); // L1: add ip, pc, ip
93706c3fb27SDimitry Andric   write32(buf + 12, 0xe12fff1c); //     bx ip
93806c3fb27SDimitry Andric   write32(buf + 16, 0x00000000); // L2: .word S - (P + (L1 - P) + 8)
939bdd1243dSDimitry Andric   uint64_t s = getARMThunkDestVA(destination);
940bdd1243dSDimitry Andric   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
941bdd1243dSDimitry Andric   target->relocateNoSym(buf + 16, R_ARM_REL32, s - p - 16);
942bdd1243dSDimitry Andric }
943bdd1243dSDimitry Andric 
addSymbols(ThunkSection & isec)944bdd1243dSDimitry Andric void ThumbV4PILongThunk::addSymbols(ThunkSection &isec) {
945bdd1243dSDimitry Andric   addSymbol(saver().save("__Thumbv4PILongThunk_" + destination.getName()),
946bdd1243dSDimitry Andric             STT_FUNC, 1, isec);
947bdd1243dSDimitry Andric   addSymbol("$t", STT_NOTYPE, 0, isec);
948bdd1243dSDimitry Andric   addSymbol("$a", STT_NOTYPE, 4, isec);
94906c3fb27SDimitry Andric   if (!getMayUseShortThunk())
950bdd1243dSDimitry Andric     addSymbol("$d", STT_NOTYPE, 16, isec);
951bdd1243dSDimitry Andric }
952bdd1243dSDimitry Andric 
95306c3fb27SDimitry Andric // Use the long jump which covers a range up to 8MiB.
writeTo(uint8_t * buf)95406c3fb27SDimitry Andric void AVRThunk::writeTo(uint8_t *buf) {
95506c3fb27SDimitry Andric   write32(buf, 0x940c); // jmp func
95606c3fb27SDimitry Andric   target->relocateNoSym(buf, R_AVR_CALL, destination.getVA());
95706c3fb27SDimitry Andric }
95806c3fb27SDimitry Andric 
addSymbols(ThunkSection & isec)95906c3fb27SDimitry Andric void AVRThunk::addSymbols(ThunkSection &isec) {
96006c3fb27SDimitry Andric   addSymbol(saver().save("__AVRThunk_" + destination.getName()), STT_FUNC, 0,
96106c3fb27SDimitry Andric             isec);
96206c3fb27SDimitry Andric }
96306c3fb27SDimitry Andric 
9640b57cec5SDimitry Andric // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)9650b57cec5SDimitry Andric void MipsThunk::writeTo(uint8_t *buf) {
9660b57cec5SDimitry Andric   uint64_t s = destination.getVA();
9670b57cec5SDimitry Andric   write32(buf, 0x3c190000); // lui   $25, %hi(func)
9680b57cec5SDimitry Andric   write32(buf + 4, 0x08000000 | (s >> 2)); // j     func
9690b57cec5SDimitry Andric   write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
9700b57cec5SDimitry Andric   write32(buf + 12, 0x00000000); // nop
9715ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_MIPS_HI16, s);
9725ffd83dbSDimitry Andric   target->relocateNoSym(buf + 8, R_MIPS_LO16, s);
9730b57cec5SDimitry Andric }
9740b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)9750b57cec5SDimitry Andric void MipsThunk::addSymbols(ThunkSection &isec) {
97604eeddc0SDimitry Andric   addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
9770b57cec5SDimitry Andric             isec);
9780b57cec5SDimitry Andric }
9790b57cec5SDimitry Andric 
getTargetInputSection() const9800b57cec5SDimitry Andric InputSection *MipsThunk::getTargetInputSection() const {
9810b57cec5SDimitry Andric   auto &dr = cast<Defined>(destination);
9820b57cec5SDimitry Andric   return dyn_cast<InputSection>(dr.section);
9830b57cec5SDimitry Andric }
9840b57cec5SDimitry Andric 
9850b57cec5SDimitry Andric // Write microMIPS R2-R5 LA25 thunk code
9860b57cec5SDimitry Andric // to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)9870b57cec5SDimitry Andric void MicroMipsThunk::writeTo(uint8_t *buf) {
9880b57cec5SDimitry Andric   uint64_t s = destination.getVA();
9890b57cec5SDimitry Andric   write16(buf, 0x41b9);       // lui   $25, %hi(func)
9900b57cec5SDimitry Andric   write16(buf + 4, 0xd400);   // j     func
9910b57cec5SDimitry Andric   write16(buf + 8, 0x3339);   // addiu $25, $25, %lo(func)
9920b57cec5SDimitry Andric   write16(buf + 12, 0x0c00);  // nop
9935ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
9945ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s);
9955ffd83dbSDimitry Andric   target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s);
9960b57cec5SDimitry Andric }
9970b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)9980b57cec5SDimitry Andric void MicroMipsThunk::addSymbols(ThunkSection &isec) {
99904eeddc0SDimitry Andric   Defined *d =
100004eeddc0SDimitry Andric       addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
100104eeddc0SDimitry Andric                 STT_FUNC, 0, isec);
10020b57cec5SDimitry Andric   d->stOther |= STO_MIPS_MICROMIPS;
10030b57cec5SDimitry Andric }
10040b57cec5SDimitry Andric 
getTargetInputSection() const10050b57cec5SDimitry Andric InputSection *MicroMipsThunk::getTargetInputSection() const {
10060b57cec5SDimitry Andric   auto &dr = cast<Defined>(destination);
10070b57cec5SDimitry Andric   return dyn_cast<InputSection>(dr.section);
10080b57cec5SDimitry Andric }
10090b57cec5SDimitry Andric 
10100b57cec5SDimitry Andric // Write microMIPS R6 LA25 thunk code
10110b57cec5SDimitry Andric // to call PIC function from the non-PIC one.
writeTo(uint8_t * buf)10120b57cec5SDimitry Andric void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
10130b57cec5SDimitry Andric   uint64_t s = destination.getVA();
10140b57cec5SDimitry Andric   uint64_t p = getThunkTargetSym()->getVA();
10150b57cec5SDimitry Andric   write16(buf, 0x1320);       // lui   $25, %hi(func)
10160b57cec5SDimitry Andric   write16(buf + 4, 0x3339);   // addiu $25, $25, %lo(func)
10170b57cec5SDimitry Andric   write16(buf + 8, 0x9400);   // bc    func
10185ffd83dbSDimitry Andric   target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
10195ffd83dbSDimitry Andric   target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s);
10205ffd83dbSDimitry Andric   target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
10210b57cec5SDimitry Andric }
10220b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)10230b57cec5SDimitry Andric void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
102404eeddc0SDimitry Andric   Defined *d =
102504eeddc0SDimitry Andric       addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
102604eeddc0SDimitry Andric                 STT_FUNC, 0, isec);
10270b57cec5SDimitry Andric   d->stOther |= STO_MIPS_MICROMIPS;
10280b57cec5SDimitry Andric }
10290b57cec5SDimitry Andric 
getTargetInputSection() const10300b57cec5SDimitry Andric InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
10310b57cec5SDimitry Andric   auto &dr = cast<Defined>(destination);
10320b57cec5SDimitry Andric   return dyn_cast<InputSection>(dr.section);
10330b57cec5SDimitry Andric }
10340b57cec5SDimitry Andric 
writePPC32PltCallStub(uint8_t * buf,uint64_t gotPltVA,const InputFile * file,int64_t addend)10355ffd83dbSDimitry Andric void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
1036480093f4SDimitry Andric                                 const InputFile *file, int64_t addend) {
10370b57cec5SDimitry Andric   if (!config->isPic) {
1038480093f4SDimitry Andric     write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
1039480093f4SDimitry Andric     write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA);        // lwz r11,l(r11)
10400b57cec5SDimitry Andric     write32(buf + 8, 0x7d6903a6);                             // mtctr r11
10410b57cec5SDimitry Andric     write32(buf + 12, 0x4e800420);                            // bctr
10420b57cec5SDimitry Andric     return;
10430b57cec5SDimitry Andric   }
10440b57cec5SDimitry Andric   uint32_t offset;
10450b57cec5SDimitry Andric   if (addend >= 0x8000) {
10460b57cec5SDimitry Andric     // The stub loads an address relative to r30 (.got2+Addend). Addend is
10470b57cec5SDimitry Andric     // almost always 0x8000. The address of .got2 is different in another object
10480b57cec5SDimitry Andric     // file, so a stub cannot be shared.
10490eae32dcSDimitry Andric     offset = gotPltVA -
10500eae32dcSDimitry Andric              (in.ppc32Got2->getParent()->getVA() +
10510eae32dcSDimitry Andric               (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend);
10520b57cec5SDimitry Andric   } else {
10530b57cec5SDimitry Andric     // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
10540b57cec5SDimitry Andric     // currently the address of .got).
1055480093f4SDimitry Andric     offset = gotPltVA - in.got->getVA();
10560b57cec5SDimitry Andric   }
10570b57cec5SDimitry Andric   uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
10580b57cec5SDimitry Andric   if (ha == 0) {
10590b57cec5SDimitry Andric     write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30)
10600b57cec5SDimitry Andric     write32(buf + 4, 0x7d6903a6);     // mtctr r11
10610b57cec5SDimitry Andric     write32(buf + 8, 0x4e800420);     // bctr
10620b57cec5SDimitry Andric     write32(buf + 12, 0x60000000);    // nop
10630b57cec5SDimitry Andric   } else {
10640b57cec5SDimitry Andric     write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha
10650b57cec5SDimitry Andric     write32(buf + 4, 0x816b0000 | l);  // lwz r11,l(r11)
10660b57cec5SDimitry Andric     write32(buf + 8, 0x7d6903a6);      // mtctr r11
10670b57cec5SDimitry Andric     write32(buf + 12, 0x4e800420);     // bctr
10680b57cec5SDimitry Andric   }
10690b57cec5SDimitry Andric }
10700b57cec5SDimitry Andric 
writeTo(uint8_t * buf)1071480093f4SDimitry Andric void PPC32PltCallStub::writeTo(uint8_t *buf) {
1072480093f4SDimitry Andric   writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend);
1073480093f4SDimitry Andric }
1074480093f4SDimitry Andric 
addSymbols(ThunkSection & isec)10750b57cec5SDimitry Andric void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
10760b57cec5SDimitry Andric   std::string buf;
10770b57cec5SDimitry Andric   raw_string_ostream os(buf);
10780b57cec5SDimitry Andric   os << format_hex_no_prefix(addend, 8);
10790b57cec5SDimitry Andric   if (!config->isPic)
10800b57cec5SDimitry Andric     os << ".plt_call32.";
10810b57cec5SDimitry Andric   else if (addend >= 0x8000)
10820b57cec5SDimitry Andric     os << ".got2.plt_pic32.";
10830b57cec5SDimitry Andric   else
10840b57cec5SDimitry Andric     os << ".plt_pic32.";
10850b57cec5SDimitry Andric   os << destination.getName();
108604eeddc0SDimitry Andric   addSymbol(saver().save(os.str()), STT_FUNC, 0, isec);
10870b57cec5SDimitry Andric }
10880b57cec5SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const10890b57cec5SDimitry Andric bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
10900b57cec5SDimitry Andric                                         const Relocation &rel) const {
10910b57cec5SDimitry Andric   return !config->isPic || (isec.file == file && rel.addend == addend);
10920b57cec5SDimitry Andric }
10930b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)109413138422SDimitry Andric void PPC32LongThunk::addSymbols(ThunkSection &isec) {
109504eeddc0SDimitry Andric   addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0,
109613138422SDimitry Andric             isec);
109713138422SDimitry Andric }
109813138422SDimitry Andric 
writeTo(uint8_t * buf)109913138422SDimitry Andric void PPC32LongThunk::writeTo(uint8_t *buf) {
110013138422SDimitry Andric   auto ha = [](uint32_t v) -> uint16_t { return (v + 0x8000) >> 16; };
110113138422SDimitry Andric   auto lo = [](uint32_t v) -> uint16_t { return v; };
110213138422SDimitry Andric   uint32_t d = destination.getVA(addend);
110313138422SDimitry Andric   if (config->isPic) {
110413138422SDimitry Andric     uint32_t off = d - (getThunkTargetSym()->getVA() + 8);
110513138422SDimitry Andric     write32(buf + 0, 0x7c0802a6);            // mflr r12,0
110613138422SDimitry Andric     write32(buf + 4, 0x429f0005);            // bcl r20,r31,.+4
110713138422SDimitry Andric     write32(buf + 8, 0x7d8802a6);            // mtctr r12
110813138422SDimitry Andric     write32(buf + 12, 0x3d8c0000 | ha(off)); // addis r12,r12,off@ha
110913138422SDimitry Andric     write32(buf + 16, 0x398c0000 | lo(off)); // addi r12,r12,off@l
111013138422SDimitry Andric     write32(buf + 20, 0x7c0803a6);           // mtlr r0
111113138422SDimitry Andric     buf += 24;
111213138422SDimitry Andric   } else {
111313138422SDimitry Andric     write32(buf + 0, 0x3d800000 | ha(d));    // lis r12,d@ha
111413138422SDimitry Andric     write32(buf + 4, 0x398c0000 | lo(d));    // addi r12,r12,d@l
111513138422SDimitry Andric     buf += 8;
111613138422SDimitry Andric   }
111713138422SDimitry Andric   write32(buf + 0, 0x7d8903a6);              // mtctr r12
111813138422SDimitry Andric   write32(buf + 4, 0x4e800420);              // bctr
111913138422SDimitry Andric }
112013138422SDimitry Andric 
writePPC64LoadAndBranch(uint8_t * buf,int64_t offset)11215ffd83dbSDimitry Andric void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
11220b57cec5SDimitry Andric   uint16_t offHa = (offset + 0x8000) >> 16;
11230b57cec5SDimitry Andric   uint16_t offLo = offset & 0xffff;
11240b57cec5SDimitry Andric 
11250b57cec5SDimitry Andric   write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
11260b57cec5SDimitry Andric   write32(buf + 4, 0xe98c0000 | offLo); // ld    r12, OffLo(r12)
11270b57cec5SDimitry Andric   write32(buf + 8, 0x7d8903a6);         // mtctr r12
11280b57cec5SDimitry Andric   write32(buf + 12, 0x4e800420);        // bctr
11290b57cec5SDimitry Andric }
11300b57cec5SDimitry Andric 
writeTo(uint8_t * buf)11310b57cec5SDimitry Andric void PPC64PltCallStub::writeTo(uint8_t *buf) {
11320b57cec5SDimitry Andric   int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
11330b57cec5SDimitry Andric   // Save the TOC pointer to the save-slot reserved in the call frame.
11340b57cec5SDimitry Andric   write32(buf + 0, 0xf8410018); // std     r2,24(r1)
1135480093f4SDimitry Andric   writePPC64LoadAndBranch(buf + 4, offset);
11360b57cec5SDimitry Andric }
11370b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)11380b57cec5SDimitry Andric void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
113904eeddc0SDimitry Andric   Defined *s = addSymbol(saver().save("__plt_" + destination.getName()),
114004eeddc0SDimitry Andric                          STT_FUNC, 0, isec);
11415f757f3fSDimitry Andric   s->setNeedsTocRestore(true);
1142480093f4SDimitry Andric   s->file = destination.file;
11430b57cec5SDimitry Andric }
11440b57cec5SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1145e8d8bef9SDimitry Andric bool PPC64PltCallStub::isCompatibleWith(const InputSection &isec,
1146e8d8bef9SDimitry Andric                                         const Relocation &rel) const {
1147e8d8bef9SDimitry Andric   return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
1148e8d8bef9SDimitry Andric }
1149e8d8bef9SDimitry Andric 
writeTo(uint8_t * buf)11505ffd83dbSDimitry Andric void PPC64R2SaveStub::writeTo(uint8_t *buf) {
1151e8d8bef9SDimitry Andric   const int64_t offset = computeOffset();
11525ffd83dbSDimitry Andric   write32(buf + 0, 0xf8410018); // std  r2,24(r1)
1153e8d8bef9SDimitry Andric   // The branch offset needs to fit in 26 bits.
1154e8d8bef9SDimitry Andric   if (getMayUseShortThunk()) {
11555ffd83dbSDimitry Andric     write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b    <offset>
1156e8d8bef9SDimitry Andric   } else if (isInt<34>(offset)) {
1157fe6060f1SDimitry Andric     int nextInstOffset;
1158fe6060f1SDimitry Andric     uint64_t tocOffset = destination.getVA() - getPPC64TocBase();
1159fe6060f1SDimitry Andric     if (tocOffset >> 16 > 0) {
1160fe6060f1SDimitry Andric       const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff);
1161753f127fSDimitry Andric       const uint64_t addis =
1162753f127fSDimitry Andric           ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff);
1163fe6060f1SDimitry Andric       write32(buf + 4, addis); // addis r12, r2 , top of offset
1164fe6060f1SDimitry Andric       write32(buf + 8, addi);  // addi  r12, r12, bottom of offset
1165fe6060f1SDimitry Andric       nextInstOffset = 12;
1166fe6060f1SDimitry Andric     } else {
1167fe6060f1SDimitry Andric       const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff);
1168fe6060f1SDimitry Andric       write32(buf + 4, addi); // addi r12, r2, offset
1169fe6060f1SDimitry Andric       nextInstOffset = 8;
1170fe6060f1SDimitry Andric     }
1171fe6060f1SDimitry Andric     write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
1172fe6060f1SDimitry Andric     write32(buf + nextInstOffset + 4, BCTR);  // bctr
1173e8d8bef9SDimitry Andric   } else {
1174e8d8bef9SDimitry Andric     in.ppc64LongBranchTarget->addEntry(&destination, addend);
1175e8d8bef9SDimitry Andric     const int64_t offsetFromTOC =
1176e8d8bef9SDimitry Andric         in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
1177e8d8bef9SDimitry Andric         getPPC64TocBase();
1178e8d8bef9SDimitry Andric     writePPC64LoadAndBranch(buf + 4, offsetFromTOC);
1179e8d8bef9SDimitry Andric   }
11805ffd83dbSDimitry Andric }
11815ffd83dbSDimitry Andric 
addSymbols(ThunkSection & isec)11825ffd83dbSDimitry Andric void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
118304eeddc0SDimitry Andric   Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()),
11845ffd83dbSDimitry Andric                          STT_FUNC, 0, isec);
11855f757f3fSDimitry Andric   s->setNeedsTocRestore(true);
11865ffd83dbSDimitry Andric }
11875ffd83dbSDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1188fe6060f1SDimitry Andric bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec,
1189fe6060f1SDimitry Andric                                        const Relocation &rel) const {
1190fe6060f1SDimitry Andric   return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
1191fe6060f1SDimitry Andric }
1192fe6060f1SDimitry Andric 
writeTo(uint8_t * buf)1193e8d8bef9SDimitry Andric void PPC64R12SetupStub::writeTo(uint8_t *buf) {
119406c3fb27SDimitry Andric   int64_t offset = (gotPlt ? destination.getGotPltVA() : destination.getVA()) -
119506c3fb27SDimitry Andric                    getThunkTargetSym()->getVA();
1196e8d8bef9SDimitry Andric   if (!isInt<34>(offset))
1197e8d8bef9SDimitry Andric     reportRangeError(buf, offset, 34, destination, "R12 setup stub offset");
1198fe6060f1SDimitry Andric 
1199fe6060f1SDimitry Andric   int nextInstOffset;
120006c3fb27SDimitry Andric   if (config->power10Stubs) {
120106c3fb27SDimitry Andric     const uint64_t imm = (((offset >> 16) & 0x3ffff) << 32) | (offset & 0xffff);
120206c3fb27SDimitry Andric     // pld 12, func@plt@pcrel or  paddi r12, 0, func@pcrel
120306c3fb27SDimitry Andric     writePrefixedInstruction(
120406c3fb27SDimitry Andric         buf, (gotPlt ? PLD_R12_NO_DISP : PADDI_R12_NO_DISP) | imm);
1205fe6060f1SDimitry Andric     nextInstOffset = 8;
120606c3fb27SDimitry Andric   } else {
120706c3fb27SDimitry Andric     uint32_t off = offset - 8;
120806c3fb27SDimitry Andric     write32(buf + 0, 0x7d8802a6);                     // mflr 12
120906c3fb27SDimitry Andric     write32(buf + 4, 0x429f0005);                     // bcl 20,31,.+4
121006c3fb27SDimitry Andric     write32(buf + 8, 0x7d6802a6);                     // mflr 11
121106c3fb27SDimitry Andric     write32(buf + 12, 0x7d8803a6);                    // mtlr 12
121206c3fb27SDimitry Andric     write32(buf + 16,
121306c3fb27SDimitry Andric             0x3d8b0000 | ((off + 0x8000) >> 16));     // addis 12,11,off@ha
121406c3fb27SDimitry Andric     if (gotPlt)
121506c3fb27SDimitry Andric       write32(buf + 20, 0xe98c0000 | (off & 0xffff)); // ld 12, off@l(12)
121606c3fb27SDimitry Andric     else
121706c3fb27SDimitry Andric       write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi 12,12,off@l
121806c3fb27SDimitry Andric     nextInstOffset = 24;
1219fe6060f1SDimitry Andric   }
1220fe6060f1SDimitry Andric   write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
1221fe6060f1SDimitry Andric   write32(buf + nextInstOffset + 4, BCTR);  // bctr
1222e8d8bef9SDimitry Andric }
1223e8d8bef9SDimitry Andric 
addSymbols(ThunkSection & isec)1224e8d8bef9SDimitry Andric void PPC64R12SetupStub::addSymbols(ThunkSection &isec) {
122506c3fb27SDimitry Andric   addSymbol(saver().save((gotPlt ? "__plt_pcrel_" : "__gep_setup_") +
122606c3fb27SDimitry Andric                          destination.getName()),
122706c3fb27SDimitry Andric             STT_FUNC, 0, isec);
1228e8d8bef9SDimitry Andric }
1229e8d8bef9SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1230fe6060f1SDimitry Andric bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec,
1231fe6060f1SDimitry Andric                                          const Relocation &rel) const {
1232fe6060f1SDimitry Andric   return rel.type == R_PPC64_REL24_NOTOC;
1233fe6060f1SDimitry Andric }
1234fe6060f1SDimitry Andric 
writeTo(uint8_t * buf)12350b57cec5SDimitry Andric void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
1236480093f4SDimitry Andric   int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
1237480093f4SDimitry Andric                    getPPC64TocBase();
1238480093f4SDimitry Andric   writePPC64LoadAndBranch(buf, offset);
12390b57cec5SDimitry Andric }
12400b57cec5SDimitry Andric 
addSymbols(ThunkSection & isec)12410b57cec5SDimitry Andric void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
124204eeddc0SDimitry Andric   addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0,
12430b57cec5SDimitry Andric             isec);
12440b57cec5SDimitry Andric }
12450b57cec5SDimitry Andric 
isCompatibleWith(const InputSection & isec,const Relocation & rel) const1246e8d8bef9SDimitry Andric bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
1247e8d8bef9SDimitry Andric                                             const Relocation &rel) const {
1248e8d8bef9SDimitry Andric   return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
1249e8d8bef9SDimitry Andric }
1250e8d8bef9SDimitry Andric 
Thunk(Symbol & d,int64_t a)125106c3fb27SDimitry Andric Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {
125206c3fb27SDimitry Andric   destination.thunkAccessed = true;
125306c3fb27SDimitry Andric }
12540b57cec5SDimitry Andric 
12550b57cec5SDimitry Andric Thunk::~Thunk() = default;
12560b57cec5SDimitry Andric 
addThunkAArch64(RelType type,Symbol & s,int64_t a)1257480093f4SDimitry Andric static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
12585ffd83dbSDimitry Andric   if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
12595ffd83dbSDimitry Andric       type != R_AARCH64_PLT32)
12600b57cec5SDimitry Andric     fatal("unrecognized relocation type");
12610b57cec5SDimitry Andric   if (config->picThunk)
1262480093f4SDimitry Andric     return make<AArch64ADRPThunk>(s, a);
1263480093f4SDimitry Andric   return make<AArch64ABSLongThunk>(s, a);
12640b57cec5SDimitry Andric }
12650b57cec5SDimitry Andric 
1266bdd1243dSDimitry Andric // Creates a thunk for long branches or Thumb-ARM interworking.
1267bdd1243dSDimitry Andric // Arm Architectures v4t does not support Thumb2 technology, and does not
1268bdd1243dSDimitry Andric // support BLX or LDR Arm/Thumb state switching. This means that
1269bdd1243dSDimitry Andric // - MOVT and MOVW instructions cannot be used.
1270bdd1243dSDimitry Andric // - We can't rewrite BL in place to BLX. We will need thunks.
1271bdd1243dSDimitry Andric //
1272bdd1243dSDimitry Andric // TODO: use B for short Thumb->Arm thunks instead of LDR (this doesn't work for
1273bdd1243dSDimitry Andric //       Arm->Thumb, as in Arm state no BX PC trick; it doesn't switch state).
addThunkArmv4(RelType reloc,Symbol & s,int64_t a)1274bdd1243dSDimitry Andric static Thunk *addThunkArmv4(RelType reloc, Symbol &s, int64_t a) {
1275bdd1243dSDimitry Andric   bool thumb_target = s.getVA(a) & 1;
1276bdd1243dSDimitry Andric 
1277bdd1243dSDimitry Andric   switch (reloc) {
1278bdd1243dSDimitry Andric   case R_ARM_PC24:
1279bdd1243dSDimitry Andric   case R_ARM_PLT32:
1280bdd1243dSDimitry Andric   case R_ARM_JUMP24:
1281bdd1243dSDimitry Andric   case R_ARM_CALL:
1282bdd1243dSDimitry Andric     if (config->picThunk) {
1283bdd1243dSDimitry Andric       if (thumb_target)
1284bdd1243dSDimitry Andric         return make<ARMV4PILongBXThunk>(s, a);
1285bdd1243dSDimitry Andric       return make<ARMV4PILongThunk>(s, a);
1286bdd1243dSDimitry Andric     }
1287bdd1243dSDimitry Andric     if (thumb_target)
1288bdd1243dSDimitry Andric       return make<ARMV4ABSLongBXThunk>(s, a);
1289bdd1243dSDimitry Andric     return make<ARMV5LongLdrPcThunk>(s, a);
1290bdd1243dSDimitry Andric   case R_ARM_THM_CALL:
1291bdd1243dSDimitry Andric     if (config->picThunk) {
1292bdd1243dSDimitry Andric       if (thumb_target)
1293bdd1243dSDimitry Andric         return make<ThumbV4PILongThunk>(s, a);
1294bdd1243dSDimitry Andric       return make<ThumbV4PILongBXThunk>(s, a);
1295bdd1243dSDimitry Andric     }
1296bdd1243dSDimitry Andric     if (thumb_target)
1297bdd1243dSDimitry Andric       return make<ThumbV4ABSLongThunk>(s, a);
1298bdd1243dSDimitry Andric     return make<ThumbV4ABSLongBXThunk>(s, a);
1299bdd1243dSDimitry Andric   }
1300bdd1243dSDimitry Andric   fatal("relocation " + toString(reloc) + " to " + toString(s) +
1301bdd1243dSDimitry Andric         " not supported for Armv4 or Armv4T target");
1302bdd1243dSDimitry Andric }
1303bdd1243dSDimitry Andric 
1304bdd1243dSDimitry Andric // Creates a thunk for Thumb-ARM interworking compatible with Armv5 and Armv6.
1305bdd1243dSDimitry Andric // Arm Architectures v5 and v6 do not support Thumb2 technology. This means that
13060b57cec5SDimitry Andric // - MOVT and MOVW instructions cannot be used
13070b57cec5SDimitry Andric // - Only Thumb relocation that can generate a Thunk is a BL, this can always
13080b57cec5SDimitry Andric //   be transformed into a BLX
addThunkArmv5v6(RelType reloc,Symbol & s,int64_t a)1309bdd1243dSDimitry Andric static Thunk *addThunkArmv5v6(RelType reloc, Symbol &s, int64_t a) {
13100b57cec5SDimitry Andric   switch (reloc) {
13110b57cec5SDimitry Andric   case R_ARM_PC24:
13120b57cec5SDimitry Andric   case R_ARM_PLT32:
13130b57cec5SDimitry Andric   case R_ARM_JUMP24:
13140b57cec5SDimitry Andric   case R_ARM_CALL:
13150b57cec5SDimitry Andric   case R_ARM_THM_CALL:
13160b57cec5SDimitry Andric     if (config->picThunk)
1317bdd1243dSDimitry Andric       return make<ARMV4PILongBXThunk>(s, a);
1318bdd1243dSDimitry Andric     return make<ARMV5LongLdrPcThunk>(s, a);
13190b57cec5SDimitry Andric   }
13200b57cec5SDimitry Andric   fatal("relocation " + toString(reloc) + " to " + toString(s) +
13210b57cec5SDimitry Andric         " not supported for Armv5 or Armv6 targets");
13220b57cec5SDimitry Andric }
13230b57cec5SDimitry Andric 
13240b57cec5SDimitry Andric // Create a thunk for Thumb long branch on V6-M.
13250b57cec5SDimitry Andric // Arm Architecture v6-M only supports Thumb instructions. This means
13260b57cec5SDimitry Andric // - MOVT and MOVW instructions cannot be used.
13270b57cec5SDimitry Andric // - Only a limited number of instructions can access registers r8 and above
13280b57cec5SDimitry Andric // - No interworking support is needed (all Thumb).
addThunkV6M(const InputSection & isec,RelType reloc,Symbol & s,int64_t a)132906c3fb27SDimitry Andric static Thunk *addThunkV6M(const InputSection &isec, RelType reloc, Symbol &s,
133006c3fb27SDimitry Andric                           int64_t a) {
133106c3fb27SDimitry Andric   const bool isPureCode = isec.getParent()->flags & SHF_ARM_PURECODE;
13320b57cec5SDimitry Andric   switch (reloc) {
13330b57cec5SDimitry Andric   case R_ARM_THM_JUMP19:
13340b57cec5SDimitry Andric   case R_ARM_THM_JUMP24:
13350b57cec5SDimitry Andric   case R_ARM_THM_CALL:
133606c3fb27SDimitry Andric     if (config->isPic) {
133706c3fb27SDimitry Andric       if (!isPureCode)
1338fe6060f1SDimitry Andric         return make<ThumbV6MPILongThunk>(s, a);
133906c3fb27SDimitry Andric 
134006c3fb27SDimitry Andric       fatal("relocation " + toString(reloc) + " to " + toString(s) +
134106c3fb27SDimitry Andric             " not supported for Armv6-M targets for position independant"
134206c3fb27SDimitry Andric             " and execute only code");
134306c3fb27SDimitry Andric     }
134406c3fb27SDimitry Andric     if (isPureCode)
134506c3fb27SDimitry Andric       return make<ThumbV6MABSXOLongThunk>(s, a);
1346fe6060f1SDimitry Andric     return make<ThumbV6MABSLongThunk>(s, a);
13470b57cec5SDimitry Andric   }
13480b57cec5SDimitry Andric   fatal("relocation " + toString(reloc) + " to " + toString(s) +
13490b57cec5SDimitry Andric         " not supported for Armv6-M targets");
13500b57cec5SDimitry Andric }
13510b57cec5SDimitry Andric 
13520b57cec5SDimitry Andric // Creates a thunk for Thumb-ARM interworking or branch range extension.
addThunkArm(const InputSection & isec,RelType reloc,Symbol & s,int64_t a)135306c3fb27SDimitry Andric static Thunk *addThunkArm(const InputSection &isec, RelType reloc, Symbol &s,
135406c3fb27SDimitry Andric                           int64_t a) {
13550b57cec5SDimitry Andric   // Decide which Thunk is needed based on:
13560b57cec5SDimitry Andric   // Available instruction set
13570b57cec5SDimitry Andric   // - An Arm Thunk can only be used if Arm state is available.
13580b57cec5SDimitry Andric   // - A Thumb Thunk can only be used if Thumb state is available.
13590b57cec5SDimitry Andric   // - Can only use a Thunk if it uses instructions that the Target supports.
13600b57cec5SDimitry Andric   // Relocation is branch or branch and link
13610b57cec5SDimitry Andric   // - Branch instructions cannot change state, can only select Thunk that
13620b57cec5SDimitry Andric   //   starts in the same state as the caller.
13630b57cec5SDimitry Andric   // - Branch and link relocations can change state, can select Thunks from
13640b57cec5SDimitry Andric   //   either Arm or Thumb.
13650b57cec5SDimitry Andric   // Position independent Thunks if we require position independent code.
136606c3fb27SDimitry Andric   // Execute Only Thunks if the output section is execute only code.
13670b57cec5SDimitry Andric 
13680b57cec5SDimitry Andric   // Handle architectures that have restrictions on the instructions that they
13690b57cec5SDimitry Andric   // can use in Thunks. The flags below are set by reading the BuildAttributes
13700b57cec5SDimitry Andric   // of the input objects. InputFiles.cpp contains the mapping from ARM
13710b57cec5SDimitry Andric   // architecture to flag.
13720b57cec5SDimitry Andric   if (!config->armHasMovtMovw) {
1373bdd1243dSDimitry Andric     if (config->armJ1J2BranchEncoding)
137406c3fb27SDimitry Andric       return addThunkV6M(isec, reloc, s, a);
1375bdd1243dSDimitry Andric     if (config->armHasBlx)
1376bdd1243dSDimitry Andric       return addThunkArmv5v6(reloc, s, a);
1377bdd1243dSDimitry Andric     return addThunkArmv4(reloc, s, a);
13780b57cec5SDimitry Andric   }
13790b57cec5SDimitry Andric 
13800b57cec5SDimitry Andric   switch (reloc) {
13810b57cec5SDimitry Andric   case R_ARM_PC24:
13820b57cec5SDimitry Andric   case R_ARM_PLT32:
13830b57cec5SDimitry Andric   case R_ARM_JUMP24:
13840b57cec5SDimitry Andric   case R_ARM_CALL:
13850b57cec5SDimitry Andric     if (config->picThunk)
1386fe6060f1SDimitry Andric       return make<ARMV7PILongThunk>(s, a);
1387fe6060f1SDimitry Andric     return make<ARMV7ABSLongThunk>(s, a);
13880b57cec5SDimitry Andric   case R_ARM_THM_JUMP19:
13890b57cec5SDimitry Andric   case R_ARM_THM_JUMP24:
13900b57cec5SDimitry Andric   case R_ARM_THM_CALL:
13910b57cec5SDimitry Andric     if (config->picThunk)
1392fe6060f1SDimitry Andric       return make<ThumbV7PILongThunk>(s, a);
1393fe6060f1SDimitry Andric     return make<ThumbV7ABSLongThunk>(s, a);
13940b57cec5SDimitry Andric   }
13950b57cec5SDimitry Andric   fatal("unrecognized relocation type");
13960b57cec5SDimitry Andric }
13970b57cec5SDimitry Andric 
addThunkAVR(RelType type,Symbol & s,int64_t a)139806c3fb27SDimitry Andric static Thunk *addThunkAVR(RelType type, Symbol &s, int64_t a) {
139906c3fb27SDimitry Andric   switch (type) {
140006c3fb27SDimitry Andric   case R_AVR_LO8_LDI_GS:
140106c3fb27SDimitry Andric   case R_AVR_HI8_LDI_GS:
140206c3fb27SDimitry Andric     return make<AVRThunk>(s, a);
140306c3fb27SDimitry Andric   default:
140406c3fb27SDimitry Andric     fatal("unrecognized relocation type " + toString(type));
140506c3fb27SDimitry Andric   }
140606c3fb27SDimitry Andric }
140706c3fb27SDimitry Andric 
addThunkMips(RelType type,Symbol & s)14080b57cec5SDimitry Andric static Thunk *addThunkMips(RelType type, Symbol &s) {
14090b57cec5SDimitry Andric   if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
14100b57cec5SDimitry Andric     return make<MicroMipsR6Thunk>(s);
14110b57cec5SDimitry Andric   if (s.stOther & STO_MIPS_MICROMIPS)
14120b57cec5SDimitry Andric     return make<MicroMipsThunk>(s);
14130b57cec5SDimitry Andric   return make<MipsThunk>(s);
14140b57cec5SDimitry Andric }
14150b57cec5SDimitry Andric 
addThunkPPC32(const InputSection & isec,const Relocation & rel,Symbol & s)1416480093f4SDimitry Andric static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel,
1417480093f4SDimitry Andric                             Symbol &s) {
141813138422SDimitry Andric   assert((rel.type == R_PPC_LOCAL24PC || rel.type == R_PPC_REL24 ||
141913138422SDimitry Andric           rel.type == R_PPC_PLTREL24) &&
14200b57cec5SDimitry Andric          "unexpected relocation type for thunk");
142113138422SDimitry Andric   if (s.isInPlt())
14220b57cec5SDimitry Andric     return make<PPC32PltCallStub>(isec, rel, s);
142313138422SDimitry Andric   return make<PPC32LongThunk>(s, rel.addend);
14240b57cec5SDimitry Andric }
14250b57cec5SDimitry Andric 
addThunkPPC64(RelType type,Symbol & s,int64_t a)1426480093f4SDimitry Andric static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
1427e8d8bef9SDimitry Andric   assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 ||
1428e8d8bef9SDimitry Andric           type == R_PPC64_REL24_NOTOC) &&
14295ffd83dbSDimitry Andric          "unexpected relocation type for thunk");
143006c3fb27SDimitry Andric 
143106c3fb27SDimitry Andric   // If we are emitting stubs for NOTOC relocations, we need to tell
143206c3fb27SDimitry Andric   // the PLT resolver that there can be multiple TOCs.
143306c3fb27SDimitry Andric   if (type == R_PPC64_REL24_NOTOC)
143406c3fb27SDimitry Andric     getPPC64TargetInfo()->ppc64DynamicSectionOpt = 0x2;
143506c3fb27SDimitry Andric 
14360b57cec5SDimitry Andric   if (s.isInPlt())
143706c3fb27SDimitry Andric     return type == R_PPC64_REL24_NOTOC
143806c3fb27SDimitry Andric                ? (Thunk *)make<PPC64R12SetupStub>(s, /*gotPlt=*/true)
1439e8d8bef9SDimitry Andric                : (Thunk *)make<PPC64PltCallStub>(s);
14400b57cec5SDimitry Andric 
14415ffd83dbSDimitry Andric   // This check looks at the st_other bits of the callee. If the value is 1
1442e8d8bef9SDimitry Andric   // then the callee clobbers the TOC and we need an R2 save stub when RelType
1443e8d8bef9SDimitry Andric   // is R_PPC64_REL14 or R_PPC64_REL24.
1444e8d8bef9SDimitry Andric   if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1)
1445e8d8bef9SDimitry Andric     return make<PPC64R2SaveStub>(s, a);
1446e8d8bef9SDimitry Andric 
1447e8d8bef9SDimitry Andric   if (type == R_PPC64_REL24_NOTOC)
144806c3fb27SDimitry Andric     return make<PPC64R12SetupStub>(s, /*gotPlt=*/false);
14495ffd83dbSDimitry Andric 
14500b57cec5SDimitry Andric   if (config->picThunk)
1451480093f4SDimitry Andric     return make<PPC64PILongBranchThunk>(s, a);
14520b57cec5SDimitry Andric 
1453480093f4SDimitry Andric   return make<PPC64PDLongBranchThunk>(s, a);
14540b57cec5SDimitry Andric }
14550b57cec5SDimitry Andric 
addThunk(const InputSection & isec,Relocation & rel)14565ffd83dbSDimitry Andric Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
14570b57cec5SDimitry Andric   Symbol &s = *rel.sym;
1458480093f4SDimitry Andric   int64_t a = rel.addend;
14590b57cec5SDimitry Andric 
146006c3fb27SDimitry Andric   switch (config->emachine) {
146106c3fb27SDimitry Andric   case EM_AARCH64:
1462480093f4SDimitry Andric     return addThunkAArch64(rel.type, s, a);
146306c3fb27SDimitry Andric   case EM_ARM:
146406c3fb27SDimitry Andric     return addThunkArm(isec, rel.type, s, a);
146506c3fb27SDimitry Andric   case EM_AVR:
146606c3fb27SDimitry Andric     return addThunkAVR(rel.type, s, a);
146706c3fb27SDimitry Andric   case EM_MIPS:
14680b57cec5SDimitry Andric     return addThunkMips(rel.type, s);
146906c3fb27SDimitry Andric   case EM_PPC:
14700b57cec5SDimitry Andric     return addThunkPPC32(isec, rel, s);
147106c3fb27SDimitry Andric   case EM_PPC64:
1472480093f4SDimitry Andric     return addThunkPPC64(rel.type, s, a);
147306c3fb27SDimitry Andric   default:
147406c3fb27SDimitry Andric     llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
147506c3fb27SDimitry Andric   }
14760b57cec5SDimitry Andric }
1477