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" 250b57cec5SDimitry Andric #include "InputSection.h" 260b57cec5SDimitry Andric #include "OutputSections.h" 270b57cec5SDimitry Andric #include "Symbols.h" 280b57cec5SDimitry Andric #include "SyntheticSections.h" 290b57cec5SDimitry Andric #include "Target.h" 300b57cec5SDimitry Andric #include "lld/Common/ErrorHandler.h" 310b57cec5SDimitry Andric #include "lld/Common/Memory.h" 320b57cec5SDimitry Andric #include "llvm/BinaryFormat/ELF.h" 330b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 340b57cec5SDimitry Andric #include "llvm/Support/Endian.h" 350b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 360b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 370b57cec5SDimitry Andric #include <cstdint> 380b57cec5SDimitry Andric #include <cstring> 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric using namespace llvm; 410b57cec5SDimitry Andric using namespace llvm::object; 420b57cec5SDimitry Andric using namespace llvm::ELF; 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric namespace lld { 450b57cec5SDimitry Andric namespace elf { 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric namespace { 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric // AArch64 long range Thunks 500b57cec5SDimitry Andric class AArch64ABSLongThunk final : public Thunk { 510b57cec5SDimitry Andric public: 52480093f4SDimitry Andric AArch64ABSLongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 530b57cec5SDimitry Andric uint32_t size() override { return 16; } 540b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 550b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 560b57cec5SDimitry Andric }; 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric class AArch64ADRPThunk final : public Thunk { 590b57cec5SDimitry Andric public: 60480093f4SDimitry Andric AArch64ADRPThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 610b57cec5SDimitry Andric uint32_t size() override { return 12; } 620b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 630b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 640b57cec5SDimitry Andric }; 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric // Base class for ARM thunks. 670b57cec5SDimitry Andric // 680b57cec5SDimitry Andric // An ARM thunk may be either short or long. A short thunk is simply a branch 690b57cec5SDimitry Andric // (B) instruction, and it may be used to call ARM functions when the distance 700b57cec5SDimitry Andric // from the thunk to the target is less than 32MB. Long thunks can branch to any 710b57cec5SDimitry Andric // virtual address and can switch between ARM and Thumb, and they are 720b57cec5SDimitry Andric // implemented in the derived classes. This class tries to create a short thunk 730b57cec5SDimitry Andric // if the target is in range, otherwise it creates a long thunk. 740b57cec5SDimitry Andric class ARMThunk : public Thunk { 750b57cec5SDimitry Andric public: 76480093f4SDimitry Andric ARMThunk(Symbol &dest) : Thunk(dest, 0) {} 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric bool getMayUseShortThunk(); 790b57cec5SDimitry Andric uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } 800b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 810b57cec5SDimitry Andric bool isCompatibleWith(const InputSection &isec, 820b57cec5SDimitry Andric const Relocation &rel) const override; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric // Returns the size of a long thunk. 850b57cec5SDimitry Andric virtual uint32_t sizeLong() = 0; 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric // Writes a long thunk to Buf. 880b57cec5SDimitry Andric virtual void writeLong(uint8_t *buf) = 0; 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric private: 910b57cec5SDimitry Andric // This field tracks whether all previously considered layouts would allow 920b57cec5SDimitry Andric // this thunk to be short. If we have ever needed a long thunk, we always 930b57cec5SDimitry Andric // create a long thunk, even if the thunk may be short given the current 940b57cec5SDimitry Andric // distance to the target. We do this because transitioning from long to short 950b57cec5SDimitry Andric // can create layout oscillations in certain corner cases which would prevent 960b57cec5SDimitry Andric // the layout from converging. 970b57cec5SDimitry Andric bool mayUseShortThunk = true; 980b57cec5SDimitry Andric }; 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric // Base class for Thumb-2 thunks. 1010b57cec5SDimitry Andric // 1020b57cec5SDimitry Andric // This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction 1030b57cec5SDimitry Andric // which has a range of 16MB. 1040b57cec5SDimitry Andric class ThumbThunk : public Thunk { 1050b57cec5SDimitry Andric public: 106480093f4SDimitry Andric ThumbThunk(Symbol &dest) : Thunk(dest, 0) { alignment = 2; } 1070b57cec5SDimitry Andric 1080b57cec5SDimitry Andric bool getMayUseShortThunk(); 1090b57cec5SDimitry Andric uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } 1100b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 1110b57cec5SDimitry Andric bool isCompatibleWith(const InputSection &isec, 1120b57cec5SDimitry Andric const Relocation &rel) const override; 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric // Returns the size of a long thunk. 1150b57cec5SDimitry Andric virtual uint32_t sizeLong() = 0; 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric // Writes a long thunk to Buf. 1180b57cec5SDimitry Andric virtual void writeLong(uint8_t *buf) = 0; 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric private: 1210b57cec5SDimitry Andric // See comment in ARMThunk above. 1220b57cec5SDimitry Andric bool mayUseShortThunk = true; 1230b57cec5SDimitry Andric }; 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric // Specific ARM Thunk implementations. The naming convention is: 1260b57cec5SDimitry Andric // Source State, TargetState, Target Requirement, ABS or PI, Range 1270b57cec5SDimitry Andric class ARMV7ABSLongThunk final : public ARMThunk { 1280b57cec5SDimitry Andric public: 1290b57cec5SDimitry Andric ARMV7ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric uint32_t sizeLong() override { return 12; } 1320b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1330b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1340b57cec5SDimitry Andric }; 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric class ARMV7PILongThunk final : public ARMThunk { 1370b57cec5SDimitry Andric public: 1380b57cec5SDimitry Andric ARMV7PILongThunk(Symbol &dest) : ARMThunk(dest) {} 1390b57cec5SDimitry Andric 1400b57cec5SDimitry Andric uint32_t sizeLong() override { return 16; } 1410b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1420b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1430b57cec5SDimitry Andric }; 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric class ThumbV7ABSLongThunk final : public ThumbThunk { 1460b57cec5SDimitry Andric public: 1470b57cec5SDimitry Andric ThumbV7ABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric uint32_t sizeLong() override { return 10; } 1500b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1510b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1520b57cec5SDimitry Andric }; 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric class ThumbV7PILongThunk final : public ThumbThunk { 1550b57cec5SDimitry Andric public: 1560b57cec5SDimitry Andric ThumbV7PILongThunk(Symbol &dest) : ThumbThunk(dest) {} 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric uint32_t sizeLong() override { return 12; } 1590b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1600b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1610b57cec5SDimitry Andric }; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric // Implementations of Thunks for older Arm architectures that do not support 1640b57cec5SDimitry Andric // the movt/movw instructions. These thunks require at least Architecture v5 1650b57cec5SDimitry Andric // as used on processors such as the Arm926ej-s. There are no Thumb entry 1660b57cec5SDimitry Andric // points as there is no Thumb branch instruction on these architecture that 1670b57cec5SDimitry Andric // can result in a thunk 1680b57cec5SDimitry Andric class ARMV5ABSLongThunk final : public ARMThunk { 1690b57cec5SDimitry Andric public: 1700b57cec5SDimitry Andric ARMV5ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric uint32_t sizeLong() override { return 8; } 1730b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1740b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1750b57cec5SDimitry Andric bool isCompatibleWith(const InputSection &isec, 1760b57cec5SDimitry Andric const Relocation &rel) const override; 1770b57cec5SDimitry Andric }; 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric class ARMV5PILongThunk final : public ARMThunk { 1800b57cec5SDimitry Andric public: 1810b57cec5SDimitry Andric ARMV5PILongThunk(Symbol &dest) : ARMThunk(dest) {} 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric uint32_t sizeLong() override { return 16; } 1840b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1850b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1860b57cec5SDimitry Andric bool isCompatibleWith(const InputSection &isec, 1870b57cec5SDimitry Andric const Relocation &rel) const override; 1880b57cec5SDimitry Andric }; 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted 1910b57cec5SDimitry Andric class ThumbV6MABSLongThunk final : public ThumbThunk { 1920b57cec5SDimitry Andric public: 1930b57cec5SDimitry Andric ThumbV6MABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric uint32_t sizeLong() override { return 12; } 1960b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 1970b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 1980b57cec5SDimitry Andric }; 1990b57cec5SDimitry Andric 2000b57cec5SDimitry Andric class ThumbV6MPILongThunk final : public ThumbThunk { 2010b57cec5SDimitry Andric public: 2020b57cec5SDimitry Andric ThumbV6MPILongThunk(Symbol &dest) : ThumbThunk(dest) {} 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric uint32_t sizeLong() override { return 16; } 2050b57cec5SDimitry Andric void writeLong(uint8_t *buf) override; 2060b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2070b57cec5SDimitry Andric }; 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric // MIPS LA25 thunk 2100b57cec5SDimitry Andric class MipsThunk final : public Thunk { 2110b57cec5SDimitry Andric public: 212480093f4SDimitry Andric MipsThunk(Symbol &dest) : Thunk(dest, 0) {} 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric uint32_t size() override { return 16; } 2150b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2160b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2170b57cec5SDimitry Andric InputSection *getTargetInputSection() const override; 2180b57cec5SDimitry Andric }; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric // microMIPS R2-R5 LA25 thunk 2210b57cec5SDimitry Andric class MicroMipsThunk final : public Thunk { 2220b57cec5SDimitry Andric public: 223480093f4SDimitry Andric MicroMipsThunk(Symbol &dest) : Thunk(dest, 0) {} 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric uint32_t size() override { return 14; } 2260b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2270b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2280b57cec5SDimitry Andric InputSection *getTargetInputSection() const override; 2290b57cec5SDimitry Andric }; 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric // microMIPS R6 LA25 thunk 2320b57cec5SDimitry Andric class MicroMipsR6Thunk final : public Thunk { 2330b57cec5SDimitry Andric public: 234480093f4SDimitry Andric MicroMipsR6Thunk(Symbol &dest) : Thunk(dest, 0) {} 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric uint32_t size() override { return 12; } 2370b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2380b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2390b57cec5SDimitry Andric InputSection *getTargetInputSection() const override; 2400b57cec5SDimitry Andric }; 2410b57cec5SDimitry Andric 2420b57cec5SDimitry Andric class PPC32PltCallStub final : public Thunk { 2430b57cec5SDimitry Andric public: 244480093f4SDimitry Andric // For R_PPC_PLTREL24, Thunk::addend records the addend which will be used to 245480093f4SDimitry Andric // decide the offsets in the call stub. 246480093f4SDimitry Andric PPC32PltCallStub(const InputSection &isec, const Relocation &rel, 247480093f4SDimitry Andric Symbol &dest) 248480093f4SDimitry Andric : Thunk(dest, rel.type == R_PPC_PLTREL24 ? rel.addend : 0), 2490b57cec5SDimitry Andric file(isec.file) {} 2500b57cec5SDimitry Andric uint32_t size() override { return 16; } 2510b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2520b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2530b57cec5SDimitry Andric bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override; 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric private: 2560b57cec5SDimitry Andric // Records the call site of the call stub. 2570b57cec5SDimitry Andric const InputFile *file; 2580b57cec5SDimitry Andric }; 2590b57cec5SDimitry Andric 2600b57cec5SDimitry Andric // PPC64 Plt call stubs. 2610b57cec5SDimitry Andric // Any call site that needs to call through a plt entry needs a call stub in 2620b57cec5SDimitry Andric // the .text section. The call stub is responsible for: 2630b57cec5SDimitry Andric // 1) Saving the toc-pointer to the stack. 2640b57cec5SDimitry Andric // 2) Loading the target functions address from the procedure linkage table into 2650b57cec5SDimitry Andric // r12 for use by the target functions global entry point, and into the count 2660b57cec5SDimitry Andric // register. 267480093f4SDimitry Andric // 3) Transferring control to the target function through an indirect branch. 2680b57cec5SDimitry Andric class PPC64PltCallStub final : public Thunk { 2690b57cec5SDimitry Andric public: 270480093f4SDimitry Andric PPC64PltCallStub(Symbol &dest) : Thunk(dest, 0) {} 2710b57cec5SDimitry Andric uint32_t size() override { return 20; } 2720b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2730b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2740b57cec5SDimitry Andric }; 2750b57cec5SDimitry Andric 2760b57cec5SDimitry Andric // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte 2770b57cec5SDimitry Andric // alignment. This gives a possible 26 bits of 'reach'. If the call offset is 2780b57cec5SDimitry Andric // larger then that we need to emit a long-branch thunk. The target address 2790b57cec5SDimitry Andric // of the callee is stored in a table to be accessed TOC-relative. Since the 2800b57cec5SDimitry Andric // call must be local (a non-local call will have a PltCallStub instead) the 2810b57cec5SDimitry Andric // table stores the address of the callee's local entry point. For 2820b57cec5SDimitry Andric // position-independent code a corresponding relative dynamic relocation is 2830b57cec5SDimitry Andric // used. 2840b57cec5SDimitry Andric class PPC64LongBranchThunk : public Thunk { 2850b57cec5SDimitry Andric public: 2860b57cec5SDimitry Andric uint32_t size() override { return 16; } 2870b57cec5SDimitry Andric void writeTo(uint8_t *buf) override; 2880b57cec5SDimitry Andric void addSymbols(ThunkSection &isec) override; 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric protected: 291480093f4SDimitry Andric PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} 2920b57cec5SDimitry Andric }; 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric class PPC64PILongBranchThunk final : public PPC64LongBranchThunk { 2950b57cec5SDimitry Andric public: 296480093f4SDimitry Andric PPC64PILongBranchThunk(Symbol &dest, int64_t addend) 297480093f4SDimitry Andric : PPC64LongBranchThunk(dest, addend) { 2980b57cec5SDimitry Andric assert(!dest.isPreemptible); 299480093f4SDimitry Andric if (Optional<uint32_t> index = 300480093f4SDimitry Andric in.ppc64LongBranchTarget->addEntry(&dest, addend)) { 3010b57cec5SDimitry Andric mainPart->relaDyn->addReloc( 302480093f4SDimitry Andric {target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8), 303480093f4SDimitry Andric true, &dest, 304480093f4SDimitry Andric addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)}); 305480093f4SDimitry Andric } 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric }; 3080b57cec5SDimitry Andric 3090b57cec5SDimitry Andric class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk { 3100b57cec5SDimitry Andric public: 311480093f4SDimitry Andric PPC64PDLongBranchThunk(Symbol &dest, int64_t addend) 312480093f4SDimitry Andric : PPC64LongBranchThunk(dest, addend) { 313480093f4SDimitry Andric in.ppc64LongBranchTarget->addEntry(&dest, addend); 3140b57cec5SDimitry Andric } 3150b57cec5SDimitry Andric }; 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric } // end anonymous namespace 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value, 3200b57cec5SDimitry Andric InputSectionBase §ion) { 3210b57cec5SDimitry Andric Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section); 3220b57cec5SDimitry Andric syms.push_back(d); 3230b57cec5SDimitry Andric return d; 3240b57cec5SDimitry Andric } 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric void Thunk::setOffset(uint64_t newOffset) { 3270b57cec5SDimitry Andric for (Defined *d : syms) 3280b57cec5SDimitry Andric d->value = d->value - offset + newOffset; 3290b57cec5SDimitry Andric offset = newOffset; 3300b57cec5SDimitry Andric } 3310b57cec5SDimitry Andric 3320b57cec5SDimitry Andric // AArch64 long range Thunks 3330b57cec5SDimitry Andric 334480093f4SDimitry Andric static uint64_t getAArch64ThunkDestVA(const Symbol &s, int64_t a) { 335480093f4SDimitry Andric uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(a); 3360b57cec5SDimitry Andric return v; 3370b57cec5SDimitry Andric } 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric void AArch64ABSLongThunk::writeTo(uint8_t *buf) { 3400b57cec5SDimitry Andric const uint8_t data[] = { 3410b57cec5SDimitry Andric 0x50, 0x00, 0x00, 0x58, // ldr x16, L0 3420b57cec5SDimitry Andric 0x00, 0x02, 0x1f, 0xd6, // br x16 3430b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00, // L0: .xword S 3440b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00, 3450b57cec5SDimitry Andric }; 346480093f4SDimitry Andric uint64_t s = getAArch64ThunkDestVA(destination, addend); 3470b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 3480b57cec5SDimitry Andric target->relocateOne(buf + 8, R_AARCH64_ABS64, s); 3490b57cec5SDimitry Andric } 3500b57cec5SDimitry Andric 3510b57cec5SDimitry Andric void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { 3520b57cec5SDimitry Andric addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()), 3530b57cec5SDimitry Andric STT_FUNC, 0, isec); 3540b57cec5SDimitry Andric addSymbol("$x", STT_NOTYPE, 0, isec); 3550b57cec5SDimitry Andric addSymbol("$d", STT_NOTYPE, 8, isec); 3560b57cec5SDimitry Andric } 3570b57cec5SDimitry Andric 3580b57cec5SDimitry Andric // This Thunk has a maximum range of 4Gb, this is sufficient for all programs 3590b57cec5SDimitry Andric // using the small code model, including pc-relative ones. At time of writing 3600b57cec5SDimitry Andric // clang and gcc do not support the large code model for position independent 3610b57cec5SDimitry Andric // code so it is safe to use this for position independent thunks without 3620b57cec5SDimitry Andric // worrying about the destination being more than 4Gb away. 3630b57cec5SDimitry Andric void AArch64ADRPThunk::writeTo(uint8_t *buf) { 3640b57cec5SDimitry Andric const uint8_t data[] = { 3650b57cec5SDimitry Andric 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest) 3660b57cec5SDimitry Andric 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest) 3670b57cec5SDimitry Andric 0x00, 0x02, 0x1f, 0xd6, // br x16 3680b57cec5SDimitry Andric }; 369480093f4SDimitry Andric uint64_t s = getAArch64ThunkDestVA(destination, addend); 3700b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 3710b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 3720b57cec5SDimitry Andric target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, 3730b57cec5SDimitry Andric getAArch64Page(s) - getAArch64Page(p)); 3740b57cec5SDimitry Andric target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s); 3750b57cec5SDimitry Andric } 3760b57cec5SDimitry Andric 3770b57cec5SDimitry Andric void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { 3780b57cec5SDimitry Andric addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC, 3790b57cec5SDimitry Andric 0, isec); 3800b57cec5SDimitry Andric addSymbol("$x", STT_NOTYPE, 0, isec); 3810b57cec5SDimitry Andric } 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric // ARM Target Thunks 3840b57cec5SDimitry Andric static uint64_t getARMThunkDestVA(const Symbol &s) { 3850b57cec5SDimitry Andric uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(); 3860b57cec5SDimitry Andric return SignExtend64<32>(v); 3870b57cec5SDimitry Andric } 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric // This function returns true if the target is not Thumb and is within 2^26, and 3900b57cec5SDimitry Andric // it has not previously returned false (see comment for mayUseShortThunk). 3910b57cec5SDimitry Andric bool ARMThunk::getMayUseShortThunk() { 3920b57cec5SDimitry Andric if (!mayUseShortThunk) 3930b57cec5SDimitry Andric return false; 3940b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 3950b57cec5SDimitry Andric if (s & 1) { 3960b57cec5SDimitry Andric mayUseShortThunk = false; 3970b57cec5SDimitry Andric return false; 3980b57cec5SDimitry Andric } 3990b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 4000b57cec5SDimitry Andric int64_t offset = s - p - 8; 4010b57cec5SDimitry Andric mayUseShortThunk = llvm::isInt<26>(offset); 4020b57cec5SDimitry Andric return mayUseShortThunk; 4030b57cec5SDimitry Andric } 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric void ARMThunk::writeTo(uint8_t *buf) { 4060b57cec5SDimitry Andric if (!getMayUseShortThunk()) { 4070b57cec5SDimitry Andric writeLong(buf); 4080b57cec5SDimitry Andric return; 4090b57cec5SDimitry Andric } 4100b57cec5SDimitry Andric 4110b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 4120b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 4130b57cec5SDimitry Andric int64_t offset = s - p - 8; 4140b57cec5SDimitry Andric const uint8_t data[] = { 4150b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0xea, // b S 4160b57cec5SDimitry Andric }; 4170b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 4180b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_JUMP24, offset); 4190b57cec5SDimitry Andric } 4200b57cec5SDimitry Andric 4210b57cec5SDimitry Andric bool ARMThunk::isCompatibleWith(const InputSection &isec, 4220b57cec5SDimitry Andric const Relocation &rel) const { 4230b57cec5SDimitry Andric // Thumb branch relocations can't use BLX 4240b57cec5SDimitry Andric return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; 4250b57cec5SDimitry Andric } 4260b57cec5SDimitry Andric 4270b57cec5SDimitry Andric // This function returns true if the target is Thumb and is within 2^25, and 4280b57cec5SDimitry Andric // it has not previously returned false (see comment for mayUseShortThunk). 4290b57cec5SDimitry Andric bool ThumbThunk::getMayUseShortThunk() { 4300b57cec5SDimitry Andric if (!mayUseShortThunk) 4310b57cec5SDimitry Andric return false; 4320b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 4330b57cec5SDimitry Andric if ((s & 1) == 0) { 4340b57cec5SDimitry Andric mayUseShortThunk = false; 4350b57cec5SDimitry Andric return false; 4360b57cec5SDimitry Andric } 4370b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA() & ~1; 4380b57cec5SDimitry Andric int64_t offset = s - p - 4; 4390b57cec5SDimitry Andric mayUseShortThunk = llvm::isInt<25>(offset); 4400b57cec5SDimitry Andric return mayUseShortThunk; 4410b57cec5SDimitry Andric } 4420b57cec5SDimitry Andric 4430b57cec5SDimitry Andric void ThumbThunk::writeTo(uint8_t *buf) { 4440b57cec5SDimitry Andric if (!getMayUseShortThunk()) { 4450b57cec5SDimitry Andric writeLong(buf); 4460b57cec5SDimitry Andric return; 4470b57cec5SDimitry Andric } 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 4500b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 4510b57cec5SDimitry Andric int64_t offset = s - p - 4; 4520b57cec5SDimitry Andric const uint8_t data[] = { 4530b57cec5SDimitry Andric 0x00, 0xf0, 0x00, 0xb0, // b.w S 4540b57cec5SDimitry Andric }; 4550b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 4560b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_THM_JUMP24, offset); 4570b57cec5SDimitry Andric } 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric bool ThumbThunk::isCompatibleWith(const InputSection &isec, 4600b57cec5SDimitry Andric const Relocation &rel) const { 4610b57cec5SDimitry Andric // ARM branch relocations can't use BLX 4620b57cec5SDimitry Andric return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32; 4630b57cec5SDimitry Andric } 4640b57cec5SDimitry Andric 4650b57cec5SDimitry Andric void ARMV7ABSLongThunk::writeLong(uint8_t *buf) { 4660b57cec5SDimitry Andric const uint8_t data[] = { 4670b57cec5SDimitry Andric 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S 4680b57cec5SDimitry Andric 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S 4690b57cec5SDimitry Andric 0x1c, 0xff, 0x2f, 0xe1, // bx ip 4700b57cec5SDimitry Andric }; 4710b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 4720b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 4730b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s); 4740b57cec5SDimitry Andric target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s); 4750b57cec5SDimitry Andric } 4760b57cec5SDimitry Andric 4770b57cec5SDimitry Andric void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { 4780b57cec5SDimitry Andric addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()), 4790b57cec5SDimitry Andric STT_FUNC, 0, isec); 4800b57cec5SDimitry Andric addSymbol("$a", STT_NOTYPE, 0, isec); 4810b57cec5SDimitry Andric } 4820b57cec5SDimitry Andric 4830b57cec5SDimitry Andric void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) { 4840b57cec5SDimitry Andric const uint8_t data[] = { 4850b57cec5SDimitry Andric 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S 4860b57cec5SDimitry Andric 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S 4870b57cec5SDimitry Andric 0x60, 0x47, // bx ip 4880b57cec5SDimitry Andric }; 4890b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 4900b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 4910b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_THM_MOVW_ABS_NC, s); 4920b57cec5SDimitry Andric target->relocateOne(buf + 4, R_ARM_THM_MOVT_ABS, s); 4930b57cec5SDimitry Andric } 4940b57cec5SDimitry Andric 4950b57cec5SDimitry Andric void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { 4960b57cec5SDimitry Andric addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()), 4970b57cec5SDimitry Andric STT_FUNC, 1, isec); 4980b57cec5SDimitry Andric addSymbol("$t", STT_NOTYPE, 0, isec); 4990b57cec5SDimitry Andric } 5000b57cec5SDimitry Andric 5010b57cec5SDimitry Andric void ARMV7PILongThunk::writeLong(uint8_t *buf) { 5020b57cec5SDimitry Andric const uint8_t data[] = { 5030b57cec5SDimitry Andric 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8) 5040b57cec5SDimitry Andric 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8) 5050b57cec5SDimitry Andric 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 5060b57cec5SDimitry Andric 0x1c, 0xff, 0x2f, 0xe1, // bx ip 5070b57cec5SDimitry Andric }; 5080b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 5090b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 5100b57cec5SDimitry Andric int64_t offset = s - p - 16; 5110b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 5120b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset); 5130b57cec5SDimitry Andric target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset); 5140b57cec5SDimitry Andric } 5150b57cec5SDimitry Andric 5160b57cec5SDimitry Andric void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { 5170b57cec5SDimitry Andric addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC, 5180b57cec5SDimitry Andric 0, isec); 5190b57cec5SDimitry Andric addSymbol("$a", STT_NOTYPE, 0, isec); 5200b57cec5SDimitry Andric } 5210b57cec5SDimitry Andric 5220b57cec5SDimitry Andric void ThumbV7PILongThunk::writeLong(uint8_t *buf) { 5230b57cec5SDimitry Andric const uint8_t data[] = { 5240b57cec5SDimitry Andric 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 5250b57cec5SDimitry Andric 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) 5260b57cec5SDimitry Andric 0xfc, 0x44, // L1: add ip, pc 5270b57cec5SDimitry Andric 0x60, 0x47, // bx ip 5280b57cec5SDimitry Andric }; 5290b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 5300b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA() & ~0x1; 5310b57cec5SDimitry Andric int64_t offset = s - p - 12; 5320b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 5330b57cec5SDimitry Andric target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset); 5340b57cec5SDimitry Andric target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset); 5350b57cec5SDimitry Andric } 5360b57cec5SDimitry Andric 5370b57cec5SDimitry Andric void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { 5380b57cec5SDimitry Andric addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()), 5390b57cec5SDimitry Andric STT_FUNC, 1, isec); 5400b57cec5SDimitry Andric addSymbol("$t", STT_NOTYPE, 0, isec); 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric 5430b57cec5SDimitry Andric void ARMV5ABSLongThunk::writeLong(uint8_t *buf) { 5440b57cec5SDimitry Andric const uint8_t data[] = { 5450b57cec5SDimitry Andric 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1 5460b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00, // L1: .word S 5470b57cec5SDimitry Andric }; 5480b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 5490b57cec5SDimitry Andric target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); 5500b57cec5SDimitry Andric } 5510b57cec5SDimitry Andric 5520b57cec5SDimitry Andric void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { 5530b57cec5SDimitry Andric addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()), 5540b57cec5SDimitry Andric STT_FUNC, 0, isec); 5550b57cec5SDimitry Andric addSymbol("$a", STT_NOTYPE, 0, isec); 5560b57cec5SDimitry Andric addSymbol("$d", STT_NOTYPE, 4, isec); 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric 5590b57cec5SDimitry Andric bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec, 5600b57cec5SDimitry Andric const Relocation &rel) const { 5610b57cec5SDimitry Andric // Thumb branch relocations can't use BLX 5620b57cec5SDimitry Andric return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; 5630b57cec5SDimitry Andric } 5640b57cec5SDimitry Andric 5650b57cec5SDimitry Andric void ARMV5PILongThunk::writeLong(uint8_t *buf) { 5660b57cec5SDimitry Andric const uint8_t data[] = { 5670b57cec5SDimitry Andric 0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2 5680b57cec5SDimitry Andric 0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip 5690b57cec5SDimitry Andric 0x1c, 0xff, 0x2f, 0xe1, // bx ip 5700b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8) 5710b57cec5SDimitry Andric }; 5720b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 5730b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA() & ~0x1; 5740b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 5750b57cec5SDimitry Andric target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); 5760b57cec5SDimitry Andric } 5770b57cec5SDimitry Andric 5780b57cec5SDimitry Andric void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { 5790b57cec5SDimitry Andric addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC, 5800b57cec5SDimitry Andric 0, isec); 5810b57cec5SDimitry Andric addSymbol("$a", STT_NOTYPE, 0, isec); 5820b57cec5SDimitry Andric addSymbol("$d", STT_NOTYPE, 12, isec); 5830b57cec5SDimitry Andric } 5840b57cec5SDimitry Andric 5850b57cec5SDimitry Andric bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec, 5860b57cec5SDimitry Andric const Relocation &rel) const { 5870b57cec5SDimitry Andric // Thumb branch relocations can't use BLX 5880b57cec5SDimitry Andric return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; 5890b57cec5SDimitry Andric } 5900b57cec5SDimitry Andric 5910b57cec5SDimitry Andric void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { 5920b57cec5SDimitry Andric // Most Thumb instructions cannot access the high registers r8 - r15. As the 5930b57cec5SDimitry Andric // only register we can corrupt is r12 we must instead spill a low register 5940b57cec5SDimitry Andric // to the stack to use as a scratch register. We push r1 even though we 5950b57cec5SDimitry Andric // don't need to get some space to use for the return address. 5960b57cec5SDimitry Andric const uint8_t data[] = { 5970b57cec5SDimitry Andric 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers 5980b57cec5SDimitry Andric 0x01, 0x48, // ldr r0, [pc, #4] ; L1 5990b57cec5SDimitry Andric 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S 6000b57cec5SDimitry Andric 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest 6010b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00 // L1: .word S 6020b57cec5SDimitry Andric }; 6030b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 6040b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 6050b57cec5SDimitry Andric target->relocateOne(buf + 8, R_ARM_ABS32, s); 6060b57cec5SDimitry Andric } 6070b57cec5SDimitry Andric 6080b57cec5SDimitry Andric void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { 6090b57cec5SDimitry Andric addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()), 6100b57cec5SDimitry Andric STT_FUNC, 1, isec); 6110b57cec5SDimitry Andric addSymbol("$t", STT_NOTYPE, 0, isec); 6120b57cec5SDimitry Andric addSymbol("$d", STT_NOTYPE, 8, isec); 6130b57cec5SDimitry Andric } 6140b57cec5SDimitry Andric 6150b57cec5SDimitry Andric void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { 6160b57cec5SDimitry Andric // Most Thumb instructions cannot access the high registers r8 - r15. As the 6170b57cec5SDimitry Andric // only register we can corrupt is ip (r12) we must instead spill a low 6180b57cec5SDimitry Andric // register to the stack to use as a scratch register. 6190b57cec5SDimitry Andric const uint8_t data[] = { 6200b57cec5SDimitry Andric 0x01, 0xb4, // P: push {r0} ; Obtain scratch register 6210b57cec5SDimitry Andric 0x02, 0x48, // ldr r0, [pc, #8] ; L2 6220b57cec5SDimitry Andric 0x84, 0x46, // mov ip, r0 ; high to low register 6230b57cec5SDimitry Andric 0x01, 0xbc, // pop {r0} ; restore scratch register 6240b57cec5SDimitry Andric 0xe7, 0x44, // L1: add pc, ip ; transfer control 6250b57cec5SDimitry Andric 0xc0, 0x46, // nop ; pad to 4-byte boundary 6260b57cec5SDimitry Andric 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4) 6270b57cec5SDimitry Andric }; 6280b57cec5SDimitry Andric uint64_t s = getARMThunkDestVA(destination); 6290b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA() & ~0x1; 6300b57cec5SDimitry Andric memcpy(buf, data, sizeof(data)); 6310b57cec5SDimitry Andric target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); 6320b57cec5SDimitry Andric } 6330b57cec5SDimitry Andric 6340b57cec5SDimitry Andric void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { 6350b57cec5SDimitry Andric addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()), 6360b57cec5SDimitry Andric STT_FUNC, 1, isec); 6370b57cec5SDimitry Andric addSymbol("$t", STT_NOTYPE, 0, isec); 6380b57cec5SDimitry Andric addSymbol("$d", STT_NOTYPE, 12, isec); 6390b57cec5SDimitry Andric } 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. 6420b57cec5SDimitry Andric void MipsThunk::writeTo(uint8_t *buf) { 6430b57cec5SDimitry Andric uint64_t s = destination.getVA(); 6440b57cec5SDimitry Andric write32(buf, 0x3c190000); // lui $25, %hi(func) 6450b57cec5SDimitry Andric write32(buf + 4, 0x08000000 | (s >> 2)); // j func 6460b57cec5SDimitry Andric write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func) 6470b57cec5SDimitry Andric write32(buf + 12, 0x00000000); // nop 6480b57cec5SDimitry Andric target->relocateOne(buf, R_MIPS_HI16, s); 6490b57cec5SDimitry Andric target->relocateOne(buf + 8, R_MIPS_LO16, s); 6500b57cec5SDimitry Andric } 6510b57cec5SDimitry Andric 6520b57cec5SDimitry Andric void MipsThunk::addSymbols(ThunkSection &isec) { 6530b57cec5SDimitry Andric addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, 6540b57cec5SDimitry Andric isec); 6550b57cec5SDimitry Andric } 6560b57cec5SDimitry Andric 6570b57cec5SDimitry Andric InputSection *MipsThunk::getTargetInputSection() const { 6580b57cec5SDimitry Andric auto &dr = cast<Defined>(destination); 6590b57cec5SDimitry Andric return dyn_cast<InputSection>(dr.section); 6600b57cec5SDimitry Andric } 6610b57cec5SDimitry Andric 6620b57cec5SDimitry Andric // Write microMIPS R2-R5 LA25 thunk code 6630b57cec5SDimitry Andric // to call PIC function from the non-PIC one. 6640b57cec5SDimitry Andric void MicroMipsThunk::writeTo(uint8_t *buf) { 6650b57cec5SDimitry Andric uint64_t s = destination.getVA(); 6660b57cec5SDimitry Andric write16(buf, 0x41b9); // lui $25, %hi(func) 6670b57cec5SDimitry Andric write16(buf + 4, 0xd400); // j func 6680b57cec5SDimitry Andric write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func) 6690b57cec5SDimitry Andric write16(buf + 12, 0x0c00); // nop 6700b57cec5SDimitry Andric target->relocateOne(buf, R_MICROMIPS_HI16, s); 6710b57cec5SDimitry Andric target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s); 6720b57cec5SDimitry Andric target->relocateOne(buf + 8, R_MICROMIPS_LO16, s); 6730b57cec5SDimitry Andric } 6740b57cec5SDimitry Andric 6750b57cec5SDimitry Andric void MicroMipsThunk::addSymbols(ThunkSection &isec) { 6760b57cec5SDimitry Andric Defined *d = addSymbol( 6770b57cec5SDimitry Andric saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); 6780b57cec5SDimitry Andric d->stOther |= STO_MIPS_MICROMIPS; 6790b57cec5SDimitry Andric } 6800b57cec5SDimitry Andric 6810b57cec5SDimitry Andric InputSection *MicroMipsThunk::getTargetInputSection() const { 6820b57cec5SDimitry Andric auto &dr = cast<Defined>(destination); 6830b57cec5SDimitry Andric return dyn_cast<InputSection>(dr.section); 6840b57cec5SDimitry Andric } 6850b57cec5SDimitry Andric 6860b57cec5SDimitry Andric // Write microMIPS R6 LA25 thunk code 6870b57cec5SDimitry Andric // to call PIC function from the non-PIC one. 6880b57cec5SDimitry Andric void MicroMipsR6Thunk::writeTo(uint8_t *buf) { 6890b57cec5SDimitry Andric uint64_t s = destination.getVA(); 6900b57cec5SDimitry Andric uint64_t p = getThunkTargetSym()->getVA(); 6910b57cec5SDimitry Andric write16(buf, 0x1320); // lui $25, %hi(func) 6920b57cec5SDimitry Andric write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func) 6930b57cec5SDimitry Andric write16(buf + 8, 0x9400); // bc func 6940b57cec5SDimitry Andric target->relocateOne(buf, R_MICROMIPS_HI16, s); 6950b57cec5SDimitry Andric target->relocateOne(buf + 4, R_MICROMIPS_LO16, s); 6960b57cec5SDimitry Andric target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12); 6970b57cec5SDimitry Andric } 6980b57cec5SDimitry Andric 6990b57cec5SDimitry Andric void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { 7000b57cec5SDimitry Andric Defined *d = addSymbol( 7010b57cec5SDimitry Andric saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); 7020b57cec5SDimitry Andric d->stOther |= STO_MIPS_MICROMIPS; 7030b57cec5SDimitry Andric } 7040b57cec5SDimitry Andric 7050b57cec5SDimitry Andric InputSection *MicroMipsR6Thunk::getTargetInputSection() const { 7060b57cec5SDimitry Andric auto &dr = cast<Defined>(destination); 7070b57cec5SDimitry Andric return dyn_cast<InputSection>(dr.section); 7080b57cec5SDimitry Andric } 7090b57cec5SDimitry Andric 710480093f4SDimitry Andric void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, 711480093f4SDimitry Andric const InputFile *file, int64_t addend) { 7120b57cec5SDimitry Andric if (!config->isPic) { 713480093f4SDimitry Andric write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha 714480093f4SDimitry Andric write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11) 7150b57cec5SDimitry Andric write32(buf + 8, 0x7d6903a6); // mtctr r11 7160b57cec5SDimitry Andric write32(buf + 12, 0x4e800420); // bctr 7170b57cec5SDimitry Andric return; 7180b57cec5SDimitry Andric } 7190b57cec5SDimitry Andric uint32_t offset; 7200b57cec5SDimitry Andric if (addend >= 0x8000) { 7210b57cec5SDimitry Andric // The stub loads an address relative to r30 (.got2+Addend). Addend is 7220b57cec5SDimitry Andric // almost always 0x8000. The address of .got2 is different in another object 7230b57cec5SDimitry Andric // file, so a stub cannot be shared. 724480093f4SDimitry Andric offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() + 7250b57cec5SDimitry Andric file->ppc32Got2OutSecOff + addend); 7260b57cec5SDimitry Andric } else { 7270b57cec5SDimitry Andric // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is 7280b57cec5SDimitry Andric // currently the address of .got). 729480093f4SDimitry Andric offset = gotPltVA - in.got->getVA(); 7300b57cec5SDimitry Andric } 7310b57cec5SDimitry Andric uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset; 7320b57cec5SDimitry Andric if (ha == 0) { 7330b57cec5SDimitry Andric write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30) 7340b57cec5SDimitry Andric write32(buf + 4, 0x7d6903a6); // mtctr r11 7350b57cec5SDimitry Andric write32(buf + 8, 0x4e800420); // bctr 7360b57cec5SDimitry Andric write32(buf + 12, 0x60000000); // nop 7370b57cec5SDimitry Andric } else { 7380b57cec5SDimitry Andric write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha 7390b57cec5SDimitry Andric write32(buf + 4, 0x816b0000 | l); // lwz r11,l(r11) 7400b57cec5SDimitry Andric write32(buf + 8, 0x7d6903a6); // mtctr r11 7410b57cec5SDimitry Andric write32(buf + 12, 0x4e800420); // bctr 7420b57cec5SDimitry Andric } 7430b57cec5SDimitry Andric } 7440b57cec5SDimitry Andric 745480093f4SDimitry Andric void PPC32PltCallStub::writeTo(uint8_t *buf) { 746480093f4SDimitry Andric writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend); 747480093f4SDimitry Andric } 748480093f4SDimitry Andric 7490b57cec5SDimitry Andric void PPC32PltCallStub::addSymbols(ThunkSection &isec) { 7500b57cec5SDimitry Andric std::string buf; 7510b57cec5SDimitry Andric raw_string_ostream os(buf); 7520b57cec5SDimitry Andric os << format_hex_no_prefix(addend, 8); 7530b57cec5SDimitry Andric if (!config->isPic) 7540b57cec5SDimitry Andric os << ".plt_call32."; 7550b57cec5SDimitry Andric else if (addend >= 0x8000) 7560b57cec5SDimitry Andric os << ".got2.plt_pic32."; 7570b57cec5SDimitry Andric else 7580b57cec5SDimitry Andric os << ".plt_pic32."; 7590b57cec5SDimitry Andric os << destination.getName(); 7600b57cec5SDimitry Andric addSymbol(saver.save(os.str()), STT_FUNC, 0, isec); 7610b57cec5SDimitry Andric } 7620b57cec5SDimitry Andric 7630b57cec5SDimitry Andric bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, 7640b57cec5SDimitry Andric const Relocation &rel) const { 7650b57cec5SDimitry Andric return !config->isPic || (isec.file == file && rel.addend == addend); 7660b57cec5SDimitry Andric } 7670b57cec5SDimitry Andric 768480093f4SDimitry Andric void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { 7690b57cec5SDimitry Andric uint16_t offHa = (offset + 0x8000) >> 16; 7700b57cec5SDimitry Andric uint16_t offLo = offset & 0xffff; 7710b57cec5SDimitry Andric 7720b57cec5SDimitry Andric write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa 7730b57cec5SDimitry Andric write32(buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12) 7740b57cec5SDimitry Andric write32(buf + 8, 0x7d8903a6); // mtctr r12 7750b57cec5SDimitry Andric write32(buf + 12, 0x4e800420); // bctr 7760b57cec5SDimitry Andric } 7770b57cec5SDimitry Andric 7780b57cec5SDimitry Andric void PPC64PltCallStub::writeTo(uint8_t *buf) { 7790b57cec5SDimitry Andric int64_t offset = destination.getGotPltVA() - getPPC64TocBase(); 7800b57cec5SDimitry Andric // Save the TOC pointer to the save-slot reserved in the call frame. 7810b57cec5SDimitry Andric write32(buf + 0, 0xf8410018); // std r2,24(r1) 782480093f4SDimitry Andric writePPC64LoadAndBranch(buf + 4, offset); 7830b57cec5SDimitry Andric } 7840b57cec5SDimitry Andric 7850b57cec5SDimitry Andric void PPC64PltCallStub::addSymbols(ThunkSection &isec) { 7860b57cec5SDimitry Andric Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC, 7870b57cec5SDimitry Andric 0, isec); 7880b57cec5SDimitry Andric s->needsTocRestore = true; 789480093f4SDimitry Andric s->file = destination.file; 7900b57cec5SDimitry Andric } 7910b57cec5SDimitry Andric 7920b57cec5SDimitry Andric void PPC64LongBranchThunk::writeTo(uint8_t *buf) { 793480093f4SDimitry Andric int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - 794480093f4SDimitry Andric getPPC64TocBase(); 795480093f4SDimitry Andric writePPC64LoadAndBranch(buf, offset); 7960b57cec5SDimitry Andric } 7970b57cec5SDimitry Andric 7980b57cec5SDimitry Andric void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { 7990b57cec5SDimitry Andric addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0, 8000b57cec5SDimitry Andric isec); 8010b57cec5SDimitry Andric } 8020b57cec5SDimitry Andric 803480093f4SDimitry Andric Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {} 8040b57cec5SDimitry Andric 8050b57cec5SDimitry Andric Thunk::~Thunk() = default; 8060b57cec5SDimitry Andric 807480093f4SDimitry Andric static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { 8080b57cec5SDimitry Andric if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) 8090b57cec5SDimitry Andric fatal("unrecognized relocation type"); 8100b57cec5SDimitry Andric if (config->picThunk) 811480093f4SDimitry Andric return make<AArch64ADRPThunk>(s, a); 812480093f4SDimitry Andric return make<AArch64ABSLongThunk>(s, a); 8130b57cec5SDimitry Andric } 8140b57cec5SDimitry Andric 8150b57cec5SDimitry Andric // Creates a thunk for Thumb-ARM interworking. 8160b57cec5SDimitry Andric // Arm Architectures v5 and v6 do not support Thumb2 technology. This means 8170b57cec5SDimitry Andric // - MOVT and MOVW instructions cannot be used 8180b57cec5SDimitry Andric // - Only Thumb relocation that can generate a Thunk is a BL, this can always 8190b57cec5SDimitry Andric // be transformed into a BLX 8200b57cec5SDimitry Andric static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) { 8210b57cec5SDimitry Andric switch (reloc) { 8220b57cec5SDimitry Andric case R_ARM_PC24: 8230b57cec5SDimitry Andric case R_ARM_PLT32: 8240b57cec5SDimitry Andric case R_ARM_JUMP24: 8250b57cec5SDimitry Andric case R_ARM_CALL: 8260b57cec5SDimitry Andric case R_ARM_THM_CALL: 8270b57cec5SDimitry Andric if (config->picThunk) 8280b57cec5SDimitry Andric return make<ARMV5PILongThunk>(s); 8290b57cec5SDimitry Andric return make<ARMV5ABSLongThunk>(s); 8300b57cec5SDimitry Andric } 8310b57cec5SDimitry Andric fatal("relocation " + toString(reloc) + " to " + toString(s) + 8320b57cec5SDimitry Andric " not supported for Armv5 or Armv6 targets"); 8330b57cec5SDimitry Andric } 8340b57cec5SDimitry Andric 8350b57cec5SDimitry Andric // Create a thunk for Thumb long branch on V6-M. 8360b57cec5SDimitry Andric // Arm Architecture v6-M only supports Thumb instructions. This means 8370b57cec5SDimitry Andric // - MOVT and MOVW instructions cannot be used. 8380b57cec5SDimitry Andric // - Only a limited number of instructions can access registers r8 and above 8390b57cec5SDimitry Andric // - No interworking support is needed (all Thumb). 8400b57cec5SDimitry Andric static Thunk *addThunkV6M(RelType reloc, Symbol &s) { 8410b57cec5SDimitry Andric switch (reloc) { 8420b57cec5SDimitry Andric case R_ARM_THM_JUMP19: 8430b57cec5SDimitry Andric case R_ARM_THM_JUMP24: 8440b57cec5SDimitry Andric case R_ARM_THM_CALL: 8450b57cec5SDimitry Andric if (config->isPic) 8460b57cec5SDimitry Andric return make<ThumbV6MPILongThunk>(s); 8470b57cec5SDimitry Andric return make<ThumbV6MABSLongThunk>(s); 8480b57cec5SDimitry Andric } 8490b57cec5SDimitry Andric fatal("relocation " + toString(reloc) + " to " + toString(s) + 8500b57cec5SDimitry Andric " not supported for Armv6-M targets"); 8510b57cec5SDimitry Andric } 8520b57cec5SDimitry Andric 8530b57cec5SDimitry Andric // Creates a thunk for Thumb-ARM interworking or branch range extension. 8540b57cec5SDimitry Andric static Thunk *addThunkArm(RelType reloc, Symbol &s) { 8550b57cec5SDimitry Andric // Decide which Thunk is needed based on: 8560b57cec5SDimitry Andric // Available instruction set 8570b57cec5SDimitry Andric // - An Arm Thunk can only be used if Arm state is available. 8580b57cec5SDimitry Andric // - A Thumb Thunk can only be used if Thumb state is available. 8590b57cec5SDimitry Andric // - Can only use a Thunk if it uses instructions that the Target supports. 8600b57cec5SDimitry Andric // Relocation is branch or branch and link 8610b57cec5SDimitry Andric // - Branch instructions cannot change state, can only select Thunk that 8620b57cec5SDimitry Andric // starts in the same state as the caller. 8630b57cec5SDimitry Andric // - Branch and link relocations can change state, can select Thunks from 8640b57cec5SDimitry Andric // either Arm or Thumb. 8650b57cec5SDimitry Andric // Position independent Thunks if we require position independent code. 8660b57cec5SDimitry Andric 8670b57cec5SDimitry Andric // Handle architectures that have restrictions on the instructions that they 8680b57cec5SDimitry Andric // can use in Thunks. The flags below are set by reading the BuildAttributes 8690b57cec5SDimitry Andric // of the input objects. InputFiles.cpp contains the mapping from ARM 8700b57cec5SDimitry Andric // architecture to flag. 8710b57cec5SDimitry Andric if (!config->armHasMovtMovw) { 8720b57cec5SDimitry Andric if (!config->armJ1J2BranchEncoding) 8730b57cec5SDimitry Andric return addThunkPreArmv7(reloc, s); 8740b57cec5SDimitry Andric return addThunkV6M(reloc, s); 8750b57cec5SDimitry Andric } 8760b57cec5SDimitry Andric 8770b57cec5SDimitry Andric switch (reloc) { 8780b57cec5SDimitry Andric case R_ARM_PC24: 8790b57cec5SDimitry Andric case R_ARM_PLT32: 8800b57cec5SDimitry Andric case R_ARM_JUMP24: 8810b57cec5SDimitry Andric case R_ARM_CALL: 8820b57cec5SDimitry Andric if (config->picThunk) 8830b57cec5SDimitry Andric return make<ARMV7PILongThunk>(s); 8840b57cec5SDimitry Andric return make<ARMV7ABSLongThunk>(s); 8850b57cec5SDimitry Andric case R_ARM_THM_JUMP19: 8860b57cec5SDimitry Andric case R_ARM_THM_JUMP24: 8870b57cec5SDimitry Andric case R_ARM_THM_CALL: 8880b57cec5SDimitry Andric if (config->picThunk) 8890b57cec5SDimitry Andric return make<ThumbV7PILongThunk>(s); 8900b57cec5SDimitry Andric return make<ThumbV7ABSLongThunk>(s); 8910b57cec5SDimitry Andric } 8920b57cec5SDimitry Andric fatal("unrecognized relocation type"); 8930b57cec5SDimitry Andric } 8940b57cec5SDimitry Andric 8950b57cec5SDimitry Andric static Thunk *addThunkMips(RelType type, Symbol &s) { 8960b57cec5SDimitry Andric if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6()) 8970b57cec5SDimitry Andric return make<MicroMipsR6Thunk>(s); 8980b57cec5SDimitry Andric if (s.stOther & STO_MIPS_MICROMIPS) 8990b57cec5SDimitry Andric return make<MicroMipsThunk>(s); 9000b57cec5SDimitry Andric return make<MipsThunk>(s); 9010b57cec5SDimitry Andric } 9020b57cec5SDimitry Andric 903480093f4SDimitry Andric static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, 904480093f4SDimitry Andric Symbol &s) { 9050b57cec5SDimitry Andric assert((rel.type == R_PPC_REL24 || rel.type == R_PPC_PLTREL24) && 9060b57cec5SDimitry Andric "unexpected relocation type for thunk"); 9070b57cec5SDimitry Andric return make<PPC32PltCallStub>(isec, rel, s); 9080b57cec5SDimitry Andric } 9090b57cec5SDimitry Andric 910480093f4SDimitry Andric static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { 9110b57cec5SDimitry Andric assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk"); 9120b57cec5SDimitry Andric if (s.isInPlt()) 9130b57cec5SDimitry Andric return make<PPC64PltCallStub>(s); 9140b57cec5SDimitry Andric 9150b57cec5SDimitry Andric if (config->picThunk) 916480093f4SDimitry Andric return make<PPC64PILongBranchThunk>(s, a); 9170b57cec5SDimitry Andric 918480093f4SDimitry Andric return make<PPC64PDLongBranchThunk>(s, a); 9190b57cec5SDimitry Andric } 9200b57cec5SDimitry Andric 9210b57cec5SDimitry Andric Thunk *addThunk(const InputSection &isec, Relocation &rel) { 9220b57cec5SDimitry Andric Symbol &s = *rel.sym; 923480093f4SDimitry Andric int64_t a = rel.addend; 9240b57cec5SDimitry Andric 9250b57cec5SDimitry Andric if (config->emachine == EM_AARCH64) 926480093f4SDimitry Andric return addThunkAArch64(rel.type, s, a); 9270b57cec5SDimitry Andric 9280b57cec5SDimitry Andric if (config->emachine == EM_ARM) 9290b57cec5SDimitry Andric return addThunkArm(rel.type, s); 9300b57cec5SDimitry Andric 9310b57cec5SDimitry Andric if (config->emachine == EM_MIPS) 9320b57cec5SDimitry Andric return addThunkMips(rel.type, s); 9330b57cec5SDimitry Andric 9340b57cec5SDimitry Andric if (config->emachine == EM_PPC) 9350b57cec5SDimitry Andric return addThunkPPC32(isec, rel, s); 9360b57cec5SDimitry Andric 9370b57cec5SDimitry Andric if (config->emachine == EM_PPC64) 938480093f4SDimitry Andric return addThunkPPC64(rel.type, s, a); 9390b57cec5SDimitry Andric 9400b57cec5SDimitry Andric llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC"); 9410b57cec5SDimitry Andric } 9420b57cec5SDimitry Andric 9430b57cec5SDimitry Andric } // end namespace elf 9440b57cec5SDimitry Andric } // end namespace lld 945