1 //===- Thunks.cpp --------------------------------------------------------===// 2 // 3 // The LLVM Linker 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===---------------------------------------------------------------------===// 9 // 10 // This file contains Thunk subclasses. 11 // 12 // A thunk is a small piece of code written after an input section 13 // which is used to jump between "incompatible" functions 14 // such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions. 15 // 16 // If a jump target is too far and its address doesn't fit to a 17 // short jump instruction, we need to create a thunk too, but we 18 // haven't supported it yet. 19 // 20 // i386 and x86-64 don't need thunks. 21 // 22 //===---------------------------------------------------------------------===// 23 24 #include "Thunks.h" 25 #include "Error.h" 26 #include "InputFiles.h" 27 #include "InputSection.h" 28 #include "OutputSections.h" 29 #include "Symbols.h" 30 #include "Target.h" 31 #include "llvm/Support/Allocator.h" 32 33 #include "llvm/Object/ELF.h" 34 #include "llvm/Support/ELF.h" 35 #include "llvm/Support/Endian.h" 36 37 using namespace llvm; 38 using namespace llvm::object; 39 using namespace llvm::support::endian; 40 using namespace llvm::ELF; 41 42 namespace lld { 43 namespace elf { 44 45 namespace { 46 // Specific ARM Thunk implementations. The naming convention is: 47 // Source State, TargetState, Target Requirement, ABS or PI, Range 48 template <class ELFT> 49 class ARMToThumbV7ABSLongThunk final : public Thunk<ELFT> { 50 public: 51 ARMToThumbV7ABSLongThunk(const SymbolBody &Dest, 52 const InputSection<ELFT> &Owner) 53 : Thunk<ELFT>(Dest, Owner) {} 54 55 uint32_t size() const override { return 12; } 56 void writeTo(uint8_t *Buf) const override; 57 }; 58 59 template <class ELFT> class ARMToThumbV7PILongThunk final : public Thunk<ELFT> { 60 public: 61 ARMToThumbV7PILongThunk(const SymbolBody &Dest, 62 const InputSection<ELFT> &Owner) 63 : Thunk<ELFT>(Dest, Owner) {} 64 65 uint32_t size() const override { return 16; } 66 void writeTo(uint8_t *Buf) const override; 67 }; 68 69 template <class ELFT> 70 class ThumbToARMV7ABSLongThunk final : public Thunk<ELFT> { 71 public: 72 ThumbToARMV7ABSLongThunk(const SymbolBody &Dest, 73 const InputSection<ELFT> &Owner) 74 : Thunk<ELFT>(Dest, Owner) {} 75 76 uint32_t size() const override { return 10; } 77 void writeTo(uint8_t *Buf) const override; 78 }; 79 80 template <class ELFT> class ThumbToARMV7PILongThunk final : public Thunk<ELFT> { 81 public: 82 ThumbToARMV7PILongThunk(const SymbolBody &Dest, 83 const InputSection<ELFT> &Owner) 84 : Thunk<ELFT>(Dest, Owner) {} 85 86 uint32_t size() const override { return 12; } 87 void writeTo(uint8_t *Buf) const override; 88 }; 89 90 // MIPS LA25 thunk 91 template <class ELFT> class MipsThunk final : public Thunk<ELFT> { 92 public: 93 MipsThunk(const SymbolBody &Dest, const InputSection<ELFT> &Owner) 94 : Thunk<ELFT>(Dest, Owner) {} 95 96 uint32_t size() const override { return 16; } 97 void writeTo(uint8_t *Buf) const override; 98 }; 99 } // anonymous namespace 100 101 // ARM Target Thunks 102 template <class ELFT> static uint64_t getARMThunkDestVA(const SymbolBody &S) { 103 return S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>(); 104 } 105 106 template <class ELFT> 107 void ARMToThumbV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const { 108 const uint8_t Data[] = { 109 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S 110 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S 111 0x1c, 0xff, 0x2f, 0xe1, // bx ip 112 }; 113 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 114 memcpy(Buf, Data, sizeof(Data)); 115 Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S); 116 Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S); 117 } 118 119 template <class ELFT> 120 void ThumbToARMV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const { 121 const uint8_t Data[] = { 122 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S 123 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S 124 0x60, 0x47, // bx ip 125 }; 126 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 127 memcpy(Buf, Data, sizeof(Data)); 128 Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S); 129 Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S); 130 } 131 132 template <class ELFT> 133 void ARMToThumbV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const { 134 const uint8_t Data[] = { 135 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8) 136 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8) 137 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 138 0x1c, 0xff, 0x2f, 0xe1, // bx r12 139 }; 140 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 141 uint64_t P = this->getVA(); 142 memcpy(Buf, Data, sizeof(Data)); 143 Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16); 144 Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12); 145 } 146 147 template <class ELFT> 148 void ThumbToARMV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const { 149 const uint8_t Data[] = { 150 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 151 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4) 152 0xfc, 0x44, // L1: add r12, pc 153 0x60, 0x47, // bx r12 154 }; 155 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 156 uint64_t P = this->getVA(); 157 memcpy(Buf, Data, sizeof(Data)); 158 Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12); 159 Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8); 160 } 161 162 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. 163 template <class ELFT> void MipsThunk<ELFT>::writeTo(uint8_t *Buf) const { 164 const endianness E = ELFT::TargetEndianness; 165 166 uint64_t S = this->Destination.template getVA<ELFT>(); 167 write32<E>(Buf, 0x3c190000); // lui $25, %hi(func) 168 write32<E>(Buf + 4, 0x08000000 | (S >> 2)); // j func 169 write32<E>(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func) 170 write32<E>(Buf + 12, 0x00000000); // nop 171 Target->relocateOne(Buf, R_MIPS_HI16, S); 172 Target->relocateOne(Buf + 8, R_MIPS_LO16, S); 173 } 174 175 template <class ELFT> 176 Thunk<ELFT>::Thunk(const SymbolBody &D, const InputSection<ELFT> &O) 177 : Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {} 178 179 template <class ELFT> typename ELFT::uint Thunk<ELFT>::getVA() const { 180 return Owner.OutSec->getVA() + Owner.OutSecOff + Offset; 181 } 182 183 template <class ELFT> Thunk<ELFT>::~Thunk() {} 184 185 // Creates a thunk for Thumb-ARM interworking. 186 template <class ELFT> 187 static Thunk<ELFT> *createThunkArm(uint32_t Reloc, SymbolBody &S, 188 InputSection<ELFT> &IS) { 189 // ARM relocations need ARM to Thumb interworking Thunks. 190 // Thumb relocations need Thumb to ARM relocations. 191 // Use position independent Thunks if we require position independent code. 192 BumpPtrAllocator &Alloc = IS.getFile()->Alloc; 193 switch (Reloc) { 194 case R_ARM_PC24: 195 case R_ARM_PLT32: 196 case R_ARM_JUMP24: 197 if (Config->Pic) 198 return new (Alloc) ARMToThumbV7PILongThunk<ELFT>(S, IS); 199 return new (Alloc) ARMToThumbV7ABSLongThunk<ELFT>(S, IS); 200 case R_ARM_THM_JUMP19: 201 case R_ARM_THM_JUMP24: 202 if (Config->Pic) 203 return new (Alloc) ThumbToARMV7PILongThunk<ELFT>(S, IS); 204 return new (Alloc) ThumbToARMV7ABSLongThunk<ELFT>(S, IS); 205 } 206 fatal("unrecognized relocation type"); 207 } 208 209 template <class ELFT> 210 static void addThunkARM(uint32_t Reloc, SymbolBody &S, InputSection<ELFT> &IS) { 211 // Only one Thunk supported per symbol. 212 if (S.hasThunk<ELFT>()) 213 return; 214 215 // ARM Thunks are added to the same InputSection as the relocation. This 216 // isn't strictly necessary but it makes it more likely that a limited range 217 // branch can reach the Thunk, and it makes Thunks to the PLT section easier 218 Thunk<ELFT> *T = createThunkArm(Reloc, S, IS); 219 IS.addThunk(T); 220 if (auto *Sym = dyn_cast<DefinedRegular<ELFT>>(&S)) 221 Sym->ThunkData = T; 222 else if (auto *Sym = dyn_cast<SharedSymbol<ELFT>>(&S)) 223 Sym->ThunkData = T; 224 else 225 fatal("symbol not DefinedRegular or Shared"); 226 } 227 228 template <class ELFT> 229 static void addThunkMips(uint32_t RelocType, SymbolBody &S, 230 InputSection<ELFT> &IS) { 231 // Only one Thunk supported per symbol. 232 if (S.hasThunk<ELFT>()) 233 return; 234 235 // Mips Thunks are added to the InputSection defining S. 236 auto *R = cast<DefinedRegular<ELFT>>(&S); 237 auto *Sec = cast<InputSection<ELFT>>(R->Section); 238 auto *T = new (IS.getFile()->Alloc) MipsThunk<ELFT>(S, *Sec); 239 Sec->addThunk(T); 240 R->ThunkData = T; 241 } 242 243 template <class ELFT> 244 void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &IS) { 245 if (Config->EMachine == EM_ARM) 246 addThunkARM<ELFT>(RelocType, S, IS); 247 else if (Config->EMachine == EM_MIPS) 248 addThunkMips<ELFT>(RelocType, S, IS); 249 else 250 llvm_unreachable("add Thunk only supported for ARM and Mips"); 251 } 252 253 template void addThunk<ELF32LE>(uint32_t, SymbolBody &, 254 InputSection<ELF32LE> &); 255 template void addThunk<ELF32BE>(uint32_t, SymbolBody &, 256 InputSection<ELF32BE> &); 257 template void addThunk<ELF64LE>(uint32_t, SymbolBody &, 258 InputSection<ELF64LE> &); 259 template void addThunk<ELF64BE>(uint32_t, SymbolBody &, 260 InputSection<ELF64BE> &); 261 262 template class Thunk<ELF32LE>; 263 template class Thunk<ELF32BE>; 264 template class Thunk<ELF64LE>; 265 template class Thunk<ELF64BE>; 266 267 } // namespace elf 268 } // namespace lld 269