Coverage Report

Created: 2018-10-20 06:24

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/ELF/Thunks.cpp
Line
Count
Source (jump to first uncovered line)
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 "Config.h"
26
#include "InputSection.h"
27
#include "OutputSections.h"
28
#include "Symbols.h"
29
#include "SyntheticSections.h"
30
#include "Target.h"
31
#include "lld/Common/ErrorHandler.h"
32
#include "lld/Common/Memory.h"
33
#include "llvm/BinaryFormat/ELF.h"
34
#include "llvm/Support/Casting.h"
35
#include "llvm/Support/Endian.h"
36
#include "llvm/Support/ErrorHandling.h"
37
#include "llvm/Support/MathExtras.h"
38
#include <cstdint>
39
#include <cstring>
40
41
using namespace llvm;
42
using namespace llvm::object;
43
using namespace llvm::ELF;
44
45
namespace lld {
46
namespace elf {
47
48
namespace {
49
50
// AArch64 long range Thunks
51
class AArch64ABSLongThunk final : public Thunk {
52
public:
53
6
  AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
54
12
  uint32_t size() override { return 16; }
55
  void writeTo(uint8_t *Buf) override;
56
  void addSymbols(ThunkSection &IS) override;
57
};
58
59
class AArch64ADRPThunk final : public Thunk {
60
public:
61
3
  AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
62
6
  uint32_t size() override { return 12; }
63
  void writeTo(uint8_t *Buf) override;
64
  void addSymbols(ThunkSection &IS) override;
65
};
66
67
// Base class for ARM thunks.
68
//
69
// An ARM thunk may be either short or long. A short thunk is simply a branch
70
// (B) instruction, and it may be used to call ARM functions when the distance
71
// from the thunk to the target is less than 32MB. Long thunks can branch to any
72
// virtual address and can switch between ARM and Thumb, and they are
73
// implemented in the derived classes. This class tries to create a short thunk
74
// if the target is in range, otherwise it creates a long thunk.
75
class ARMThunk : public Thunk {
76
public:
77
25
  ARMThunk(Symbol &Dest) : Thunk(Dest) {}
78
79
  bool mayUseShortThunk();
80
51
  uint32_t size() override { return mayUseShortThunk() ? 
48
:
sizeLong()43
; }
81
  void writeTo(uint8_t *Buf) override;
82
  bool isCompatibleWith(RelType Type) const override;
83
84
  // Returns the size of a long thunk.
85
  virtual uint32_t sizeLong() = 0;
86
87
  // Writes a long thunk to Buf.
88
  virtual void writeLong(uint8_t *Buf) = 0;
89
90
private:
91
  // This field tracks whether all previously considered layouts would allow
92
  // this thunk to be short. If we have ever needed a long thunk, we always
93
  // create a long thunk, even if the thunk may be short given the current
94
  // distance to the target. We do this because transitioning from long to short
95
  // can create layout oscillations in certain corner cases which would prevent
96
  // the layout from converging.
97
  bool MayUseShortThunk = true;
98
};
99
100
// Base class for Thumb-2 thunks.
101
//
102
// This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
103
// which has a range of 16MB.
104
class ThumbThunk : public Thunk {
105
public:
106
66
  ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
107
108
  bool mayUseShortThunk();
109
138
  uint32_t size() override { return mayUseShortThunk() ? 
442
:
sizeLong()96
; }
110
  void writeTo(uint8_t *Buf) override;
111
  bool isCompatibleWith(RelType Type) const override;
112
113
  // Returns the size of a long thunk.
114
  virtual uint32_t sizeLong() = 0;
115
116
  // Writes a long thunk to Buf.
117
  virtual void writeLong(uint8_t *Buf) = 0;
118
119
private:
120
  // See comment in ARMThunk above.
121
  bool MayUseShortThunk = true;
122
};
123
124
// Specific ARM Thunk implementations. The naming convention is:
125
// Source State, TargetState, Target Requirement, ABS or PI, Range
126
class ARMV7ABSLongThunk final : public ARMThunk {
127
public:
128
16
  ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
129
130
27
  uint32_t sizeLong() override { return 12; }
131
  void writeLong(uint8_t *Buf) override;
132
  void addSymbols(ThunkSection &IS) override;
133
};
134
135
class ARMV7PILongThunk final : public ARMThunk {
136
public:
137
5
  ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
138
139
10
  uint32_t sizeLong() override { return 16; }
140
  void writeLong(uint8_t *Buf) override;
141
  void addSymbols(ThunkSection &IS) override;
142
};
143
144
class ThumbV7ABSLongThunk final : public ThumbThunk {
145
public:
146
48
  ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
147
148
58
  uint32_t sizeLong() override { return 10; }
149
  void writeLong(uint8_t *Buf) override;
150
  void addSymbols(ThunkSection &IS) override;
151
};
152
153
class ThumbV7PILongThunk final : public ThumbThunk {
154
public:
155
18
  ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
156
157
38
  uint32_t sizeLong() override { return 12; }
158
  void writeLong(uint8_t *Buf) override;
159
  void addSymbols(ThunkSection &IS) override;
160
};
161
162
// Implementations of Thunks for older Arm architectures that do not support
163
// the movt/movw instructions. These thunks require at least Architecture v5
164
// as used on processors such as the Arm926ej-s. There are no Thumb entry
165
// points as there is no Thumb branch instruction on these architecture that
166
// can result in a thunk
167
class ARMV5ABSLongThunk final : public ARMThunk {
168
public:
169
3
  ARMV5ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
170
171
4
  uint32_t sizeLong() override { return 8; }
172
  void writeLong(uint8_t *Buf) override;
173
  void addSymbols(ThunkSection &IS) override;
174
  bool isCompatibleWith(uint32_t RelocType) const override;
175
};
176
177
class ARMV5PILongThunk final : public ARMThunk {
178
public:
179
1
  ARMV5PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
180
181
2
  uint32_t sizeLong() override { return 16; }
182
  void writeLong(uint8_t *Buf) override;
183
  void addSymbols(ThunkSection &IS) override;
184
  bool isCompatibleWith(uint32_t RelocType) const override;
185
};
186
187
// MIPS LA25 thunk
188
class MipsThunk final : public Thunk {
189
public:
190
29
  MipsThunk(Symbol &Dest) : Thunk(Dest) {}
191
192
105
  uint32_t size() override { return 16; }
193
  void writeTo(uint8_t *Buf) override;
194
  void addSymbols(ThunkSection &IS) override;
195
  InputSection *getTargetInputSection() const override;
196
};
197
198
// microMIPS R2-R5 LA25 thunk
199
class MicroMipsThunk final : public Thunk {
200
public:
201
2
  MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
202
203
8
  uint32_t size() override { return 14; }
204
  void writeTo(uint8_t *Buf) override;
205
  void addSymbols(ThunkSection &IS) override;
206
  InputSection *getTargetInputSection() const override;
207
};
208
209
// microMIPS R6 LA25 thunk
210
class MicroMipsR6Thunk final : public Thunk {
211
public:
212
2
  MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
213
214
8
  uint32_t size() override { return 12; }
215
  void writeTo(uint8_t *Buf) override;
216
  void addSymbols(ThunkSection &IS) override;
217
  InputSection *getTargetInputSection() const override;
218
};
219
220
221
// PPC64 Plt call stubs.
222
// Any call site that needs to call through a plt entry needs a call stub in
223
// the .text section. The call stub is responsible for:
224
// 1) Saving the toc-pointer to the stack.
225
// 2) Loading the target functions address from the procedure linkage table into
226
//    r12 for use by the target functions global entry point, and into the count
227
//    register.
228
// 3) Transfering control to the target function through an indirect branch.
229
class PPC64PltCallStub final : public Thunk {
230
public:
231
29
  PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
232
58
  uint32_t size() override { return 20; }
233
  void writeTo(uint8_t *Buf) override;
234
  void addSymbols(ThunkSection &IS) override;
235
};
236
237
} // end anonymous namespace
238
239
Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
240
272
                          InputSectionBase &Section) {
241
272
  Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
242
272
  Syms.push_back(D);
243
272
  return D;
244
272
}
245
246
386
void Thunk::setOffset(uint64_t NewOffset) {
247
386
  for (Defined *D : Syms)
248
613
    D->Value = D->Value - Offset + NewOffset;
249
386
  Offset = NewOffset;
250
386
}
251
252
// AArch64 long range Thunks
253
254
9
static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
255
9
  uint64_t V = S.isInPlt() ? 
S.getPltVA()1
:
S.getVA()8
;
256
9
  return V;
257
9
}
258
259
6
void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
260
6
  const uint8_t Data[] = {
261
6
    0x50, 0x00, 0x00, 0x58, //     ldr x16, L0
262
6
    0x00, 0x02, 0x1f, 0xd6, //     br  x16
263
6
    0x00, 0x00, 0x00, 0x00, // L0: .xword S
264
6
    0x00, 0x00, 0x00, 0x00,
265
6
  };
266
6
  uint64_t S = getAArch64ThunkDestVA(Destination);
267
6
  memcpy(Buf, Data, sizeof(Data));
268
6
  Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
269
6
}
270
271
6
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
272
6
  addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
273
6
            STT_FUNC, 0, IS);
274
6
  addSymbol("$x", STT_NOTYPE, 0, IS);
275
6
  addSymbol("$d", STT_NOTYPE, 8, IS);
276
6
}
277
278
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
279
// using the small code model, including pc-relative ones. At time of writing
280
// clang and gcc do not support the large code model for position independent
281
// code so it is safe to use this for position independent thunks without
282
// worrying about the destination being more than 4Gb away.
283
3
void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
284
3
  const uint8_t Data[] = {
285
3
      0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
286
3
      0x10, 0x02, 0x00, 0x91, // add  x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
287
3
      0x00, 0x02, 0x1f, 0xd6, // br   x16
288
3
  };
289
3
  uint64_t S = getAArch64ThunkDestVA(Destination);
290
3
  uint64_t P = getThunkTargetSym()->getVA();
291
3
  memcpy(Buf, Data, sizeof(Data));
292
3
  Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
293
3
                      getAArch64Page(S) - getAArch64Page(P));
294
3
  Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
295
3
}
296
297
3
void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
298
3
  addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
299
3
            0, IS);
300
3
  addSymbol("$x", STT_NOTYPE, 0, IS);
301
3
}
302
303
// ARM Target Thunks
304
232
static uint64_t getARMThunkDestVA(const Symbol &S) {
305
232
  uint64_t V = S.isInPlt() ? 
S.getPltVA()28
:
S.getVA()204
;
306
232
  return SignExtend64<32>(V);
307
232
}
308
309
// This function returns true if the target is not Thumb and is within 2^26, and
310
// it has not previously returned false (see comment for MayUseShortThunk).
311
76
bool ARMThunk::mayUseShortThunk() {
312
76
  if (!MayUseShortThunk)
313
43
    return false;
314
33
  uint64_t S = getARMThunkDestVA(Destination);
315
33
  if (S & 1) {
316
14
    MayUseShortThunk = false;
317
14
    return false;
318
14
  }
319
19
  uint64_t P = getThunkTargetSym()->getVA();
320
19
  int64_t Offset = S - P - 8;
321
19
  MayUseShortThunk = llvm::isInt<26>(Offset);
322
19
  return MayUseShortThunk;
323
19
}
324
325
25
void ARMThunk::writeTo(uint8_t *Buf) {
326
25
  if (!mayUseShortThunk()) {
327
21
    writeLong(Buf);
328
21
    return;
329
21
  }
330
4
331
4
  uint64_t S = getARMThunkDestVA(Destination);
332
4
  uint64_t P = getThunkTargetSym()->getVA();
333
4
  int64_t Offset = S - P - 8;
334
4
  const uint8_t Data[] = {
335
4
    0x00, 0x00, 0x00, 0xea, // b S
336
4
  };
337
4
  memcpy(Buf, Data, sizeof(Data));
338
4
  Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
339
4
}
340
341
4
bool ARMThunk::isCompatibleWith(RelType Type) const {
342
4
  // Thumb branch relocations can't use BLX
343
4
  return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
344
4
}
345
346
// This function returns true if the target is Thumb and is within 2^25, and
347
// it has not previously returned false (see comment for MayUseShortThunk).
348
204
bool ThumbThunk::mayUseShortThunk() {
349
204
  if (!MayUseShortThunk)
350
96
    return false;
351
108
  uint64_t S = getARMThunkDestVA(Destination);
352
108
  if ((S & 1) == 0) {
353
26
    MayUseShortThunk = false;
354
26
    return false;
355
26
  }
356
82
  uint64_t P = getThunkTargetSym()->getVA() & ~1;
357
82
  int64_t Offset = S - P - 4;
358
82
  MayUseShortThunk = llvm::isInt<25>(Offset);
359
82
  return MayUseShortThunk;
360
82
}
361
362
66
void ThumbThunk::writeTo(uint8_t *Buf) {
363
66
  if (!mayUseShortThunk()) {
364
46
    writeLong(Buf);
365
46
    return;
366
46
  }
367
20
368
20
  uint64_t S = getARMThunkDestVA(Destination);
369
20
  uint64_t P = getThunkTargetSym()->getVA();
370
20
  int64_t Offset = S - P - 4;
371
20
  const uint8_t Data[] = {
372
20
      0x00, 0xf0, 0x00, 0xb0, // b.w S
373
20
  };
374
20
  memcpy(Buf, Data, sizeof(Data));
375
20
  Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
376
20
}
377
378
33
bool ThumbThunk::isCompatibleWith(RelType Type) const {
379
33
  // ARM branch relocations can't use BLX
380
33
  return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
381
33
}
382
383
13
void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
384
13
  const uint8_t Data[] = {
385
13
      0x00, 0xc0, 0x00, 0xe3, // movw         ip,:lower16:S
386
13
      0x00, 0xc0, 0x40, 0xe3, // movt         ip,:upper16:S
387
13
      0x1c, 0xff, 0x2f, 0xe1, // bx   ip
388
13
  };
389
13
  uint64_t S = getARMThunkDestVA(Destination);
390
13
  memcpy(Buf, Data, sizeof(Data));
391
13
  Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
392
13
  Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
393
13
}
394
395
16
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
396
16
  addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
397
16
            STT_FUNC, 0, IS);
398
16
  addSymbol("$a", STT_NOTYPE, 0, IS);
399
16
}
400
401
28
void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
402
28
  const uint8_t Data[] = {
403
28
      0x40, 0xf2, 0x00, 0x0c, // movw         ip, :lower16:S
404
28
      0xc0, 0xf2, 0x00, 0x0c, // movt         ip, :upper16:S
405
28
      0x60, 0x47,             // bx   ip
406
28
  };
407
28
  uint64_t S = getARMThunkDestVA(Destination);
408
28
  memcpy(Buf, Data, sizeof(Data));
409
28
  Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
410
28
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
411
28
}
412
413
48
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
414
48
  addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
415
48
            STT_FUNC, 1, IS);
416
48
  addSymbol("$t", STT_NOTYPE, 0, IS);
417
48
}
418
419
5
void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
420
5
  const uint8_t Data[] = {
421
5
      0xf0, 0xcf, 0x0f, 0xe3, // P:  movw ip,:lower16:S - (P + (L1-P) + 8)
422
5
      0x00, 0xc0, 0x40, 0xe3, //     movt ip,:upper16:S - (P + (L1-P) + 8)
423
5
      0x0f, 0xc0, 0x8c, 0xe0, // L1: add  ip, ip, pc
424
5
      0x1c, 0xff, 0x2f, 0xe1, //     bx   ip
425
5
  };
426
5
  uint64_t S = getARMThunkDestVA(Destination);
427
5
  uint64_t P = getThunkTargetSym()->getVA();
428
5
  uint64_t Offset = S - P - 16;
429
5
  memcpy(Buf, Data, sizeof(Data));
430
5
  Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
431
5
  Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
432
5
}
433
434
5
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
435
5
  addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
436
5
            0, IS);
437
5
  addSymbol("$a", STT_NOTYPE, 0, IS);
438
5
}
439
440
18
void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
441
18
  const uint8_t Data[] = {
442
18
      0x4f, 0xf6, 0xf4, 0x7c, // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
443
18
      0xc0, 0xf2, 0x00, 0x0c, //     movt ip,:upper16:S - (P + (L1-P) + 4)
444
18
      0xfc, 0x44,             // L1: add  ip, pc
445
18
      0x60, 0x47,             //     bx   ip
446
18
  };
447
18
  uint64_t S = getARMThunkDestVA(Destination);
448
18
  uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
449
18
  uint64_t Offset = S - P - 12;
450
18
  memcpy(Buf, Data, sizeof(Data));
451
18
  Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
452
18
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
453
18
}
454
455
18
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
456
18
  addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
457
18
            STT_FUNC, 1, IS);
458
18
  addSymbol("$t", STT_NOTYPE, 0, IS);
459
18
}
460
461
2
void ARMV5ABSLongThunk::writeLong(uint8_t *Buf) {
462
2
  const uint8_t Data[] = {
463
2
      0x04, 0xf0, 0x1f, 0xe5, //     ldr pc, [pc,#-4] ; L1
464
2
      0x00, 0x00, 0x00, 0x00, // L1: .word S
465
2
  };
466
2
  memcpy(Buf, Data, sizeof(Data));
467
2
  Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination));
468
2
}
469
470
3
void ARMV5ABSLongThunk::addSymbols(ThunkSection &IS) {
471
3
  addSymbol(Saver.save("__ARMv5ABSLongThunk_" + Destination.getName()),
472
3
            STT_FUNC, 0, IS);
473
3
  addSymbol("$a", STT_NOTYPE, 0, IS);
474
3
  addSymbol("$d", STT_NOTYPE, 4, IS);
475
3
}
476
477
1
bool ARMV5ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
478
1
  // Thumb branch relocations can't use BLX
479
1
  return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
480
1
}
481
482
1
void ARMV5PILongThunk::writeLong(uint8_t *Buf) {
483
1
  const uint8_t Data[] = {
484
1
      0x04, 0xc0, 0x9f, 0xe5, // P:  ldr ip, [pc,#4] ; L2
485
1
      0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
486
1
      0x1c, 0xff, 0x2f, 0xe1, //     bx ip
487
1
      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
488
1
  };
489
1
  uint64_t S = getARMThunkDestVA(Destination);
490
1
  uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
491
1
  memcpy(Buf, Data, sizeof(Data));
492
1
  Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12);
493
1
}
494
495
1
void ARMV5PILongThunk::addSymbols(ThunkSection &IS) {
496
1
  addSymbol(Saver.save("__ARMV5PILongThunk_" + Destination.getName()), STT_FUNC,
497
1
            0, IS);
498
1
  addSymbol("$a", STT_NOTYPE, 0, IS);
499
1
  addSymbol("$d", STT_NOTYPE, 12, IS);
500
1
}
501
502
0
bool ARMV5PILongThunk::isCompatibleWith(uint32_t RelocType) const {
503
0
  // Thumb branch relocations can't use BLX
504
0
  return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
505
0
}
506
507
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
508
29
void MipsThunk::writeTo(uint8_t *Buf) {
509
29
  uint64_t S = Destination.getVA();
510
29
  write32(Buf, 0x3c190000); // lui   $25, %hi(func)
511
29
  write32(Buf + 4, 0x08000000 | (S >> 2)); // j     func
512
29
  write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
513
29
  write32(Buf + 12, 0x00000000); // nop
514
29
  Target->relocateOne(Buf, R_MIPS_HI16, S);
515
29
  Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
516
29
}
517
518
29
void MipsThunk::addSymbols(ThunkSection &IS) {
519
29
  addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
520
29
            IS);
521
29
}
522
523
69
InputSection *MipsThunk::getTargetInputSection() const {
524
69
  auto &DR = cast<Defined>(Destination);
525
69
  return dyn_cast<InputSection>(DR.Section);
526
69
}
527
528
// Write microMIPS R2-R5 LA25 thunk code
529
// to call PIC function from the non-PIC one.
530
2
void MicroMipsThunk::writeTo(uint8_t *Buf) {
531
2
  uint64_t S = Destination.getVA() | 1;
532
2
  write16(Buf, 0x41b9);       // lui   $25, %hi(func)
533
2
  write16(Buf + 4, 0xd400);   // j     func
534
2
  write16(Buf + 8, 0x3339);   // addiu $25, $25, %lo(func)
535
2
  write16(Buf + 12, 0x0c00);  // nop
536
2
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
537
2
  Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
538
2
  Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
539
2
}
540
541
2
void MicroMipsThunk::addSymbols(ThunkSection &IS) {
542
2
  Defined *D = addSymbol(
543
2
      Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
544
2
  D->StOther |= STO_MIPS_MICROMIPS;
545
2
}
546
547
4
InputSection *MicroMipsThunk::getTargetInputSection() const {
548
4
  auto &DR = cast<Defined>(Destination);
549
4
  return dyn_cast<InputSection>(DR.Section);
550
4
}
551
552
// Write microMIPS R6 LA25 thunk code
553
// to call PIC function from the non-PIC one.
554
2
void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
555
2
  uint64_t S = Destination.getVA() | 1;
556
2
  uint64_t P = getThunkTargetSym()->getVA();
557
2
  write16(Buf, 0x1320);       // lui   $25, %hi(func)
558
2
  write16(Buf + 4, 0x3339);   // addiu $25, $25, %lo(func)
559
2
  write16(Buf + 8, 0x9400);   // bc    func
560
2
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
561
2
  Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
562
2
  Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
563
2
}
564
565
2
void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
566
2
  Defined *D = addSymbol(
567
2
      Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
568
2
  D->StOther |= STO_MIPS_MICROMIPS;
569
2
}
570
571
4
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
572
4
  auto &DR = cast<Defined>(Destination);
573
4
  return dyn_cast<InputSection>(DR.Section);
574
4
}
575
576
29
void PPC64PltCallStub::writeTo(uint8_t *Buf) {
577
29
  int64_t Off = Destination.getGotPltVA() - getPPC64TocBase();
578
29
  // Need to add 0x8000 to offset to account for the low bits being signed.
579
29
  uint16_t OffHa = (Off + 0x8000) >> 16;
580
29
  uint16_t OffLo = Off;
581
29
582
29
  write32(Buf +  0, 0xf8410018);          // std     r2,24(r1)
583
29
  write32(Buf +  4, 0x3d820000 | OffHa);  // addis   r12,r2, X@plt@to@ha
584
29
  write32(Buf +  8, 0xe98c0000 | OffLo);  // ld      r12,X@plt@toc@l(r12)
585
29
  write32(Buf + 12, 0x7d8903a6);          // mtctr   r12
586
29
  write32(Buf + 16, 0x4e800420);          // bctr
587
29
}
588
589
29
void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
590
29
  Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
591
29
                         0, IS);
592
29
  S->NeedsTocRestore = true;
593
29
}
594
595
162
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
596
597
162
Thunk::~Thunk() = default;
598
599
9
static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
600
9
  if (Type != R_AARCH64_CALL26 && 
Type != R_AARCH64_JUMP261
)
601
0
    fatal("unrecognized relocation type");
602
9
  if (Config->Pic)
603
3
    return make<AArch64ADRPThunk>(S);
604
6
  return make<AArch64ABSLongThunk>(S);
605
6
}
606
607
// Creates a thunk for Thumb-ARM interworking.
608
// Arm Architectures v5 and v6 do not support Thumb2 technology. This means
609
// - MOVT and MOVW instructions cannot be used
610
// - Only Thumb relocation that can generate a Thunk is a BL, this can always
611
//   be transformed into a BLX
612
4
static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
613
4
  switch (Reloc) {
614
4
  case R_ARM_PC24:
615
4
  case R_ARM_PLT32:
616
4
  case R_ARM_JUMP24:
617
4
  case R_ARM_CALL:
618
4
  case R_ARM_THM_CALL:
619
4
    if (Config->Pic)
620
1
      return make<ARMV5PILongThunk>(S);
621
3
    return make<ARMV5ABSLongThunk>(S);
622
0
  }
623
0
  fatal("relocation " + toString(Reloc) + " to " + toString(S) +
624
0
        " not supported for Armv5 or Armv6 targets");
625
0
}
626
627
// Creates a thunk for Thumb-ARM interworking or branch range extension.
628
91
static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
629
91
  // Decide which Thunk is needed based on:
630
91
  // Available instruction set
631
91
  // - An Arm Thunk can only be used if Arm state is available.
632
91
  // - A Thumb Thunk can only be used if Thumb state is available.
633
91
  // - Can only use a Thunk if it uses instructions that the Target supports.
634
91
  // Relocation is branch or branch and link
635
91
  // - Branch instructions cannot change state, can only select Thunk that
636
91
  //   starts in the same state as the caller.
637
91
  // - Branch and link relocations can change state, can select Thunks from
638
91
  //   either Arm or Thumb.
639
91
  // Position independent Thunks if we require position independent code.
640
91
641
91
  if (!Config->ARMHasMovtMovw) {
642
4
    if (!Config->ARMJ1J2BranchEncoding)
643
4
      return addThunkPreArmv7(Reloc, S);
644
0
    else
645
0
      // The Armv6-m architecture (Cortex-M0) does not have Arm instructions or
646
0
      // support the MOVT MOVW instructions so it cannot use any of the Thunks
647
0
      // currently implemented.
648
0
      fatal("thunks not supported for architecture Armv6-m");
649
87
  }
650
87
651
87
  switch (Reloc) {
652
87
  case R_ARM_PC24:
653
21
  case R_ARM_PLT32:
654
21
  case R_ARM_JUMP24:
655
21
  case R_ARM_CALL:
656
21
    if (Config->Pic)
657
5
      return make<ARMV7PILongThunk>(S);
658
16
    return make<ARMV7ABSLongThunk>(S);
659
66
  case R_ARM_THM_JUMP19:
660
66
  case R_ARM_THM_JUMP24:
661
66
  case R_ARM_THM_CALL:
662
66
    if (Config->Pic)
663
18
      return make<ThumbV7PILongThunk>(S);
664
48
    return make<ThumbV7ABSLongThunk>(S);
665
0
  }
666
0
  fatal("unrecognized relocation type");
667
0
}
668
669
33
static Thunk *addThunkMips(RelType Type, Symbol &S) {
670
33
  if ((S.StOther & STO_MIPS_MICROMIPS) && 
isMipsR6()4
)
671
2
    return make<MicroMipsR6Thunk>(S);
672
31
  if (S.StOther & STO_MIPS_MICROMIPS)
673
2
    return make<MicroMipsThunk>(S);
674
29
  return make<MipsThunk>(S);
675
29
}
676
677
29
static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
678
29
  if (Type == R_PPC64_REL24)
679
29
    return make<PPC64PltCallStub>(S);
680
0
  fatal("unexpected relocation type");
681
0
}
682
683
162
Thunk *addThunk(RelType Type, Symbol &S) {
684
162
  if (Config->EMachine == EM_AARCH64)
685
9
    return addThunkAArch64(Type, S);
686
153
687
153
  if (Config->EMachine == EM_ARM)
688
91
    return addThunkArm(Type, S);
689
62
690
62
  if (Config->EMachine == EM_MIPS)
691
33
    return addThunkMips(Type, S);
692
29
693
29
  if (Config->EMachine == EM_PPC64)
694
29
    return addThunkPPC64(Type, S);
695
0
696
0
  llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
697
0
}
698
699
} // end namespace elf
700
} // end namespace lld