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 uint64_t V = S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>(); 104 return SignExtend64<32>(V); 105 } 106 107 template <class ELFT> 108 void ARMToThumbV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const { 109 const uint8_t Data[] = { 110 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S 111 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S 112 0x1c, 0xff, 0x2f, 0xe1, // bx ip 113 }; 114 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 115 memcpy(Buf, Data, sizeof(Data)); 116 Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S); 117 Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S); 118 } 119 120 template <class ELFT> 121 void ThumbToARMV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const { 122 const uint8_t Data[] = { 123 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S 124 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S 125 0x60, 0x47, // bx ip 126 }; 127 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 128 memcpy(Buf, Data, sizeof(Data)); 129 Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S); 130 Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S); 131 } 132 133 template <class ELFT> 134 void ARMToThumbV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const { 135 const uint8_t Data[] = { 136 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8) 137 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8) 138 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 139 0x1c, 0xff, 0x2f, 0xe1, // bx r12 140 }; 141 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 142 uint64_t P = this->getVA(); 143 memcpy(Buf, Data, sizeof(Data)); 144 Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16); 145 Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12); 146 } 147 148 template <class ELFT> 149 void ThumbToARMV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const { 150 const uint8_t Data[] = { 151 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 152 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4) 153 0xfc, 0x44, // L1: add r12, pc 154 0x60, 0x47, // bx r12 155 }; 156 uint64_t S = getARMThunkDestVA<ELFT>(this->Destination); 157 uint64_t P = this->getVA(); 158 memcpy(Buf, Data, sizeof(Data)); 159 Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12); 160 Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8); 161 } 162 163 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. 164 template <class ELFT> void MipsThunk<ELFT>::writeTo(uint8_t *Buf) const { 165 const endianness E = ELFT::TargetEndianness; 166 167 uint64_t S = this->Destination.template getVA<ELFT>(); 168 write32<E>(Buf, 0x3c190000); // lui $25, %hi(func) 169 write32<E>(Buf + 4, 0x08000000 | (S >> 2)); // j func 170 write32<E>(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func) 171 write32<E>(Buf + 12, 0x00000000); // nop 172 Target->relocateOne(Buf, R_MIPS_HI16, S); 173 Target->relocateOne(Buf + 8, R_MIPS_LO16, S); 174 } 175 176 template <class ELFT> 177 Thunk<ELFT>::Thunk(const SymbolBody &D, const InputSection<ELFT> &O) 178 : Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {} 179 180 template <class ELFT> typename ELFT::uint Thunk<ELFT>::getVA() const { 181 return Owner.OutSec->getVA() + Owner.OutSecOff + Offset; 182 } 183 184 template <class ELFT> Thunk<ELFT>::~Thunk() {} 185 186 // Creates a thunk for Thumb-ARM interworking. 187 template <class ELFT> 188 static Thunk<ELFT> *createThunkArm(uint32_t Reloc, SymbolBody &S, 189 InputSection<ELFT> &IS) { 190 // ARM relocations need ARM to Thumb interworking Thunks. 191 // Thumb relocations need Thumb to ARM relocations. 192 // Use position independent Thunks if we require position independent code. 193 BumpPtrAllocator &Alloc = IS.getFile()->Alloc; 194 switch (Reloc) { 195 case R_ARM_PC24: 196 case R_ARM_PLT32: 197 case R_ARM_JUMP24: 198 if (Config->Pic) 199 return new (Alloc) ARMToThumbV7PILongThunk<ELFT>(S, IS); 200 return new (Alloc) ARMToThumbV7ABSLongThunk<ELFT>(S, IS); 201 case R_ARM_THM_JUMP19: 202 case R_ARM_THM_JUMP24: 203 if (Config->Pic) 204 return new (Alloc) ThumbToARMV7PILongThunk<ELFT>(S, IS); 205 return new (Alloc) ThumbToARMV7ABSLongThunk<ELFT>(S, IS); 206 } 207 fatal("unrecognized relocation type"); 208 } 209 210 template <class ELFT> 211 static void addThunkARM(uint32_t Reloc, SymbolBody &S, InputSection<ELFT> &IS) { 212 // Only one Thunk supported per symbol. 213 if (S.hasThunk<ELFT>()) 214 return; 215 216 // ARM Thunks are added to the same InputSection as the relocation. This 217 // isn't strictly necessary but it makes it more likely that a limited range 218 // branch can reach the Thunk, and it makes Thunks to the PLT section easier 219 Thunk<ELFT> *T = createThunkArm(Reloc, S, IS); 220 IS.addThunk(T); 221 if (auto *Sym = dyn_cast<DefinedRegular<ELFT>>(&S)) 222 Sym->ThunkData = T; 223 else if (auto *Sym = dyn_cast<SharedSymbol<ELFT>>(&S)) 224 Sym->ThunkData = T; 225 else 226 fatal("symbol not DefinedRegular or Shared"); 227 } 228 229 template <class ELFT> 230 static void addThunkMips(uint32_t RelocType, SymbolBody &S, 231 InputSection<ELFT> &IS) { 232 // Only one Thunk supported per symbol. 233 if (S.hasThunk<ELFT>()) 234 return; 235 236 // Mips Thunks are added to the InputSection defining S. 237 auto *R = cast<DefinedRegular<ELFT>>(&S); 238 auto *Sec = cast<InputSection<ELFT>>(R->Section); 239 auto *T = new (IS.getFile()->Alloc) MipsThunk<ELFT>(S, *Sec); 240 Sec->addThunk(T); 241 R->ThunkData = T; 242 } 243 244 template <class ELFT> 245 void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &IS) { 246 if (Config->EMachine == EM_ARM) 247 addThunkARM<ELFT>(RelocType, S, IS); 248 else if (Config->EMachine == EM_MIPS) 249 addThunkMips<ELFT>(RelocType, S, IS); 250 else 251 llvm_unreachable("add Thunk only supported for ARM and Mips"); 252 } 253 254 template void addThunk<ELF32LE>(uint32_t, SymbolBody &, 255 InputSection<ELF32LE> &); 256 template void addThunk<ELF32BE>(uint32_t, SymbolBody &, 257 InputSection<ELF32BE> &); 258 template void addThunk<ELF64LE>(uint32_t, SymbolBody &, 259 InputSection<ELF64LE> &); 260 template void addThunk<ELF64BE>(uint32_t, SymbolBody &, 261 InputSection<ELF64BE> &); 262 263 template class Thunk<ELF32LE>; 264 template class Thunk<ELF32BE>; 265 template class Thunk<ELF64LE>; 266 template class Thunk<ELF64BE>; 267 268 } // namespace elf 269 } // namespace lld 270