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: 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: 6906c3fb27SDimitry Andric AArch64ABSLongThunk(Symbol &dest, int64_t addend) 7006c3fb27SDimitry Andric : AArch64Thunk(dest, addend) {} 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: 8006c3fb27SDimitry Andric AArch64ADRPThunk(Symbol &dest, int64_t addend) : AArch64Thunk(dest, addend) {} 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: 98fe6060f1SDimitry Andric ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric bool getMayUseShortThunk(); 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: 128fe6060f1SDimitry Andric ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) { 129fe6060f1SDimitry Andric alignment = 2; 130fe6060f1SDimitry Andric } 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric bool getMayUseShortThunk(); 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: 153fe6060f1SDimitry Andric ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 1540b57cec5SDimitry Andric 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: 162fe6060f1SDimitry Andric ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 1630b57cec5SDimitry Andric 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: 171fe6060f1SDimitry Andric ThumbV7ABSLongThunk(Symbol &dest, int64_t addend) 172fe6060f1SDimitry Andric : ThumbThunk(dest, addend) {} 1730b57cec5SDimitry Andric 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: 181fe6060f1SDimitry Andric ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} 1820b57cec5SDimitry Andric 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: 191fe6060f1SDimitry Andric ThumbV6MABSLongThunk(Symbol &dest, int64_t addend) 192fe6060f1SDimitry Andric : ThumbThunk(dest, addend) {} 1930b57cec5SDimitry Andric 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: 20106c3fb27SDimitry Andric ThumbV6MABSXOLongThunk(Symbol &dest, int64_t addend) 20206c3fb27SDimitry Andric : ThumbThunk(dest, addend) {} 20306c3fb27SDimitry Andric 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: 211fe6060f1SDimitry Andric ThumbV6MPILongThunk(Symbol &dest, int64_t addend) 212fe6060f1SDimitry Andric : ThumbThunk(dest, addend) {} 2130b57cec5SDimitry Andric 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: 229bdd1243dSDimitry Andric ARMV5LongLdrPcThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 230bdd1243dSDimitry Andric 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: 240bdd1243dSDimitry Andric ARMV4PILongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 241bdd1243dSDimitry Andric 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: 249bdd1243dSDimitry Andric ARMV4PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 250bdd1243dSDimitry Andric 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: 258bdd1243dSDimitry Andric ThumbV4PILongBXThunk(Symbol &dest, int64_t addend) 259bdd1243dSDimitry Andric : ThumbThunk(dest, addend) {} 260bdd1243dSDimitry Andric 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: 268bdd1243dSDimitry Andric ThumbV4PILongThunk(Symbol &dest, int64_t addend) 269bdd1243dSDimitry Andric : ThumbThunk(dest, addend) {} 270bdd1243dSDimitry Andric 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: 278bdd1243dSDimitry Andric ARMV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} 279bdd1243dSDimitry Andric 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: 287bdd1243dSDimitry Andric ThumbV4ABSLongBXThunk(Symbol &dest, int64_t addend) 288bdd1243dSDimitry Andric : ThumbThunk(dest, addend) {} 289bdd1243dSDimitry Andric 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: 297bdd1243dSDimitry Andric ThumbV4ABSLongThunk(Symbol &dest, int64_t addend) 298bdd1243dSDimitry Andric : ThumbThunk(dest, addend) {} 299bdd1243dSDimitry Andric 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: 30906c3fb27SDimitry Andric AVRThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 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: 318480093f4SDimitry Andric MipsThunk(Symbol &dest) : Thunk(dest, 0) {} 3190b57cec5SDimitry Andric 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: 329480093f4SDimitry Andric MicroMipsThunk(Symbol &dest) : Thunk(dest, 0) {} 3300b57cec5SDimitry Andric 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: 340480093f4SDimitry Andric MicroMipsR6Thunk(Symbol &dest) : Thunk(dest, 0) {} 3410b57cec5SDimitry Andric 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. 352480093f4SDimitry Andric PPC32PltCallStub(const InputSection &isec, const Relocation &rel, 353480093f4SDimitry Andric Symbol &dest) 35413138422SDimitry Andric : Thunk(dest, rel.addend), file(isec.file) {} 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: 36713138422SDimitry Andric PPC32LongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 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: 383480093f4SDimitry Andric PPC64PltCallStub(Symbol &dest) : Thunk(dest, 0) {} 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: 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. 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 } 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; 425e8d8bef9SDimitry 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: 43606c3fb27SDimitry Andric PPC64R12SetupStub(Symbol &dest, bool gotPlt) 43706c3fb27SDimitry Andric : Thunk(dest, 0), gotPlt(gotPlt) { 43806c3fb27SDimitry Andric alignment = 16; 43906c3fb27SDimitry Andric } 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: 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: 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: 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: 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 4950b57cec5SDimitry Andric Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value, 4960b57cec5SDimitry Andric InputSectionBase §ion) { 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 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. 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 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 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. 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 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. 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 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 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). 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 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 6150b57cec5SDimitry 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. 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 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 6590b57cec5SDimitry 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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. 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 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 9800b57cec5SDimitry 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. 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 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 10050b57cec5SDimitry 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. 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 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 10300b57cec5SDimitry Andric InputSection *MicroMipsR6Thunk::getTargetInputSection() const { 10310b57cec5SDimitry Andric auto &dr = cast<Defined>(destination); 10320b57cec5SDimitry Andric return dyn_cast<InputSection>(dr.section); 10330b57cec5SDimitry Andric } 10340b57cec5SDimitry Andric 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 1071480093f4SDimitry Andric void PPC32PltCallStub::writeTo(uint8_t *buf) { 1072480093f4SDimitry Andric writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend); 1073480093f4SDimitry Andric } 1074480093f4SDimitry Andric 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 10890b57cec5SDimitry 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 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 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 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 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 11380b57cec5SDimitry Andric void PPC64PltCallStub::addSymbols(ThunkSection &isec) { 113904eeddc0SDimitry Andric Defined *s = addSymbol(saver().save("__plt_" + destination.getName()), 114004eeddc0SDimitry Andric STT_FUNC, 0, isec); 11410b57cec5SDimitry Andric s->needsTocRestore = true; 1142480093f4SDimitry Andric s->file = destination.file; 11430b57cec5SDimitry Andric } 11440b57cec5SDimitry Andric 1145e8d8bef9SDimitry 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 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 11825ffd83dbSDimitry Andric void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { 118304eeddc0SDimitry Andric Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()), 11845ffd83dbSDimitry Andric STT_FUNC, 0, isec); 11855ffd83dbSDimitry Andric s->needsTocRestore = true; 11865ffd83dbSDimitry Andric } 11875ffd83dbSDimitry Andric 1188fe6060f1SDimitry 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 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 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 1230fe6060f1SDimitry 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 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 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 1246e8d8bef9SDimitry 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 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 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). 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 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). 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. 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 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 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 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 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 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