Coverage Report

Created: 2018-08-19 21:11

/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
21
  ARMThunk(Symbol &Dest) : Thunk(Dest) {}
78
79
  bool mayUseShortThunk();
80
43
  uint32_t size() override { return mayUseShortThunk() ? 
46
:
sizeLong()37
; }
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
// MIPS LA25 thunk
163
class MipsThunk final : public Thunk {
164
public:
165
29
  MipsThunk(Symbol &Dest) : Thunk(Dest) {}
166
167
105
  uint32_t size() override { return 16; }
168
  void writeTo(uint8_t *Buf) override;
169
  void addSymbols(ThunkSection &IS) override;
170
  InputSection *getTargetInputSection() const override;
171
};
172
173
// microMIPS R2-R5 LA25 thunk
174
class MicroMipsThunk final : public Thunk {
175
public:
176
2
  MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
177
178
8
  uint32_t size() override { return 14; }
179
  void writeTo(uint8_t *Buf) override;
180
  void addSymbols(ThunkSection &IS) override;
181
  InputSection *getTargetInputSection() const override;
182
};
183
184
// microMIPS R6 LA25 thunk
185
class MicroMipsR6Thunk final : public Thunk {
186
public:
187
2
  MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
188
189
8
  uint32_t size() override { return 12; }
190
  void writeTo(uint8_t *Buf) override;
191
  void addSymbols(ThunkSection &IS) override;
192
  InputSection *getTargetInputSection() const override;
193
};
194
195
196
// PPC64 Plt call stubs.
197
// Any call site that needs to call through a plt entry needs a call stub in
198
// the .text section. The call stub is responsible for:
199
// 1) Saving the toc-pointer to the stack.
200
// 2) Loading the target functions address from the procedure linkage table into
201
//    r12 for use by the target functions global entry point, and into the count
202
//    register.
203
// 3) Transfering control to the target function through an indirect branch.
204
class PPC64PltCallStub final : public Thunk {
205
public:
206
22
  PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
207
44
  uint32_t size() override { return 20; }
208
  void writeTo(uint8_t *Buf) override;
209
  void addSymbols(ThunkSection &IS) override;
210
};
211
212
} // end anonymous namespace
213
214
Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
215
253
                          InputSectionBase &Section) {
216
253
  Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
217
253
  Syms.push_back(D);
218
253
  return D;
219
253
}
220
221
364
void Thunk::setOffset(uint64_t NewOffset) {
222
364
  for (Defined *D : Syms)
223
575
    D->Value = D->Value - Offset + NewOffset;
224
364
  Offset = NewOffset;
225
364
}
226
227
// AArch64 long range Thunks
228
229
9
static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
230
9
  uint64_t V = S.isInPlt() ? 
S.getPltVA()1
:
S.getVA()8
;
231
9
  return V;
232
9
}
233
234
6
void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
235
6
  const uint8_t Data[] = {
236
6
    0x50, 0x00, 0x00, 0x58, //     ldr x16, L0
237
6
    0x00, 0x02, 0x1f, 0xd6, //     br  x16
238
6
    0x00, 0x00, 0x00, 0x00, // L0: .xword S
239
6
    0x00, 0x00, 0x00, 0x00,
240
6
  };
241
6
  uint64_t S = getAArch64ThunkDestVA(Destination);
242
6
  memcpy(Buf, Data, sizeof(Data));
243
6
  Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
244
6
}
245
246
6
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
247
6
  addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
248
6
            STT_FUNC, 0, IS);
249
6
  addSymbol("$x", STT_NOTYPE, 0, IS);
250
6
  addSymbol("$d", STT_NOTYPE, 8, IS);
251
6
}
252
253
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
254
// using the small code model, including pc-relative ones. At time of writing
255
// clang and gcc do not support the large code model for position independent
256
// code so it is safe to use this for position independent thunks without
257
// worrying about the destination being more than 4Gb away.
258
3
void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
259
3
  const uint8_t Data[] = {
260
3
      0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
261
3
      0x10, 0x02, 0x00, 0x91, // add  x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
262
3
      0x00, 0x02, 0x1f, 0xd6, // br   x16
263
3
  };
264
3
  uint64_t S = getAArch64ThunkDestVA(Destination);
265
3
  uint64_t P = getThunkTargetSym()->getVA();
266
3
  memcpy(Buf, Data, sizeof(Data));
267
3
  Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
268
3
                      getAArch64Page(S) - getAArch64Page(P));
269
3
  Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
270
3
}
271
272
3
void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
273
3
  addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
274
3
            0, IS);
275
3
  addSymbol("$x", STT_NOTYPE, 0, IS);
276
3
}
277
278
// ARM Target Thunks
279
222
static uint64_t getARMThunkDestVA(const Symbol &S) {
280
222
  uint64_t V = S.isInPlt() ? 
S.getPltVA()28
:
S.getVA()194
;
281
222
  return SignExtend64<32>(V);
282
222
}
283
284
// This function returns true if the target is not Thumb and is within 2^26, and
285
// it has not previously returned false (see comment for MayUseShortThunk).
286
64
bool ARMThunk::mayUseShortThunk() {
287
64
  if (!MayUseShortThunk)
288
37
    return false;
289
27
  uint64_t S = getARMThunkDestVA(Destination);
290
27
  if (S & 1) {
291
11
    MayUseShortThunk = false;
292
11
    return false;
293
11
  }
294
16
  uint64_t P = getThunkTargetSym()->getVA();
295
16
  int64_t Offset = S - P - 8;
296
16
  MayUseShortThunk = llvm::isInt<26>(Offset);
297
16
  return MayUseShortThunk;
298
16
}
299
300
21
void ARMThunk::writeTo(uint8_t *Buf) {
301
21
  if (!mayUseShortThunk()) {
302
18
    writeLong(Buf);
303
18
    return;
304
18
  }
305
3
306
3
  uint64_t S = getARMThunkDestVA(Destination);
307
3
  uint64_t P = getThunkTargetSym()->getVA();
308
3
  int64_t Offset = S - P - 8;
309
3
  const uint8_t Data[] = {
310
3
    0x00, 0x00, 0x00, 0xea, // b S
311
3
  };
312
3
  memcpy(Buf, Data, sizeof(Data));
313
3
  Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
314
3
}
315
316
4
bool ARMThunk::isCompatibleWith(RelType Type) const {
317
4
  // Thumb branch relocations can't use BLX
318
4
  return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
319
4
}
320
321
// This function returns true if the target is Thumb and is within 2^25, and
322
// it has not previously returned false (see comment for MayUseShortThunk).
323
204
bool ThumbThunk::mayUseShortThunk() {
324
204
  if (!MayUseShortThunk)
325
96
    return false;
326
108
  uint64_t S = getARMThunkDestVA(Destination);
327
108
  if ((S & 1) == 0) {
328
26
    MayUseShortThunk = false;
329
26
    return false;
330
26
  }
331
82
  uint64_t P = getThunkTargetSym()->getVA() & ~1;
332
82
  int64_t Offset = S - P - 4;
333
82
  MayUseShortThunk = llvm::isInt<25>(Offset);
334
82
  return MayUseShortThunk;
335
82
}
336
337
66
void ThumbThunk::writeTo(uint8_t *Buf) {
338
66
  if (!mayUseShortThunk()) {
339
46
    writeLong(Buf);
340
46
    return;
341
46
  }
342
20
343
20
  uint64_t S = getARMThunkDestVA(Destination);
344
20
  uint64_t P = getThunkTargetSym()->getVA();
345
20
  int64_t Offset = S - P - 4;
346
20
  const uint8_t Data[] = {
347
20
      0x00, 0xf0, 0x00, 0xb0, // b.w S
348
20
  };
349
20
  memcpy(Buf, Data, sizeof(Data));
350
20
  Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
351
20
}
352
353
33
bool ThumbThunk::isCompatibleWith(RelType Type) const {
354
33
  // ARM branch relocations can't use BLX
355
33
  return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
356
33
}
357
358
13
void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
359
13
  const uint8_t Data[] = {
360
13
      0x00, 0xc0, 0x00, 0xe3, // movw         ip,:lower16:S
361
13
      0x00, 0xc0, 0x40, 0xe3, // movt         ip,:upper16:S
362
13
      0x1c, 0xff, 0x2f, 0xe1, // bx   ip
363
13
  };
364
13
  uint64_t S = getARMThunkDestVA(Destination);
365
13
  memcpy(Buf, Data, sizeof(Data));
366
13
  Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
367
13
  Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
368
13
}
369
370
16
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
371
16
  addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
372
16
            STT_FUNC, 0, IS);
373
16
  addSymbol("$a", STT_NOTYPE, 0, IS);
374
16
}
375
376
28
void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
377
28
  const uint8_t Data[] = {
378
28
      0x40, 0xf2, 0x00, 0x0c, // movw         ip, :lower16:S
379
28
      0xc0, 0xf2, 0x00, 0x0c, // movt         ip, :upper16:S
380
28
      0x60, 0x47,             // bx   ip
381
28
  };
382
28
  uint64_t S = getARMThunkDestVA(Destination);
383
28
  memcpy(Buf, Data, sizeof(Data));
384
28
  Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
385
28
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
386
28
}
387
388
48
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
389
48
  addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
390
48
            STT_FUNC, 1, IS);
391
48
  addSymbol("$t", STT_NOTYPE, 0, IS);
392
48
}
393
394
5
void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
395
5
  const uint8_t Data[] = {
396
5
      0xf0, 0xcf, 0x0f, 0xe3, // P:  movw ip,:lower16:S - (P + (L1-P) + 8)
397
5
      0x00, 0xc0, 0x40, 0xe3, //     movt ip,:upper16:S - (P + (L1-P) + 8)
398
5
      0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
399
5
      0x1c, 0xff, 0x2f, 0xe1, //     bx r12
400
5
  };
401
5
  uint64_t S = getARMThunkDestVA(Destination);
402
5
  uint64_t P = getThunkTargetSym()->getVA();
403
5
  uint64_t Offset = S - P - 16;
404
5
  memcpy(Buf, Data, sizeof(Data));
405
5
  Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
406
5
  Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
407
5
}
408
409
5
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
410
5
  addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
411
5
            0, IS);
412
5
  addSymbol("$a", STT_NOTYPE, 0, IS);
413
5
}
414
415
18
void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
416
18
  const uint8_t Data[] = {
417
18
      0x4f, 0xf6, 0xf4, 0x7c, // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
418
18
      0xc0, 0xf2, 0x00, 0x0c, //     movt ip,:upper16:S - (P + (L1-P) + 4)
419
18
      0xfc, 0x44,             // L1: add  r12, pc
420
18
      0x60, 0x47,             //     bx   r12
421
18
  };
422
18
  uint64_t S = getARMThunkDestVA(Destination);
423
18
  uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
424
18
  uint64_t Offset = S - P - 12;
425
18
  memcpy(Buf, Data, sizeof(Data));
426
18
  Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
427
18
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
428
18
}
429
430
18
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
431
18
  addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
432
18
            STT_FUNC, 1, IS);
433
18
  addSymbol("$t", STT_NOTYPE, 0, IS);
434
18
}
435
436
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
437
29
void MipsThunk::writeTo(uint8_t *Buf) {
438
29
  uint64_t S = Destination.getVA();
439
29
  write32(Buf, 0x3c190000); // lui   $25, %hi(func)
440
29
  write32(Buf + 4, 0x08000000 | (S >> 2)); // j     func
441
29
  write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
442
29
  write32(Buf + 12, 0x00000000); // nop
443
29
  Target->relocateOne(Buf, R_MIPS_HI16, S);
444
29
  Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
445
29
}
446
447
29
void MipsThunk::addSymbols(ThunkSection &IS) {
448
29
  addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
449
29
            IS);
450
29
}
451
452
69
InputSection *MipsThunk::getTargetInputSection() const {
453
69
  auto &DR = cast<Defined>(Destination);
454
69
  return dyn_cast<InputSection>(DR.Section);
455
69
}
456
457
// Write microMIPS R2-R5 LA25 thunk code
458
// to call PIC function from the non-PIC one.
459
2
void MicroMipsThunk::writeTo(uint8_t *Buf) {
460
2
  uint64_t S = Destination.getVA() | 1;
461
2
  write16(Buf, 0x41b9);       // lui   $25, %hi(func)
462
2
  write16(Buf + 4, 0xd400);   // j     func
463
2
  write16(Buf + 8, 0x3339);   // addiu $25, $25, %lo(func)
464
2
  write16(Buf + 12, 0x0c00);  // nop
465
2
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
466
2
  Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
467
2
  Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
468
2
}
469
470
2
void MicroMipsThunk::addSymbols(ThunkSection &IS) {
471
2
  Defined *D = addSymbol(
472
2
      Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
473
2
  D->StOther |= STO_MIPS_MICROMIPS;
474
2
}
475
476
4
InputSection *MicroMipsThunk::getTargetInputSection() const {
477
4
  auto &DR = cast<Defined>(Destination);
478
4
  return dyn_cast<InputSection>(DR.Section);
479
4
}
480
481
// Write microMIPS R6 LA25 thunk code
482
// to call PIC function from the non-PIC one.
483
2
void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
484
2
  uint64_t S = Destination.getVA() | 1;
485
2
  uint64_t P = getThunkTargetSym()->getVA();
486
2
  write16(Buf, 0x1320);       // lui   $25, %hi(func)
487
2
  write16(Buf + 4, 0x3339);   // addiu $25, $25, %lo(func)
488
2
  write16(Buf + 8, 0x9400);   // bc    func
489
2
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
490
2
  Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
491
2
  Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
492
2
}
493
494
2
void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
495
2
  Defined *D = addSymbol(
496
2
      Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
497
2
  D->StOther |= STO_MIPS_MICROMIPS;
498
2
}
499
500
4
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
501
4
  auto &DR = cast<Defined>(Destination);
502
4
  return dyn_cast<InputSection>(DR.Section);
503
4
}
504
505
22
void PPC64PltCallStub::writeTo(uint8_t *Buf) {
506
22
  int64_t Off = Destination.getGotPltVA() - getPPC64TocBase();
507
22
  // Need to add 0x8000 to offset to account for the low bits being signed.
508
22
  uint16_t OffHa = (Off + 0x8000) >> 16;
509
22
  uint16_t OffLo = Off;
510
22
511
22
  write32(Buf +  0, 0xf8410018);          // std     r2,24(r1)
512
22
  write32(Buf +  4, 0x3d820000 | OffHa);  // addis   r12,r2, X@plt@to@ha
513
22
  write32(Buf +  8, 0xe98c0000 | OffLo);  // ld      r12,X@plt@toc@l(r12)
514
22
  write32(Buf + 12, 0x7d8903a6);          // mtctr   r12
515
22
  write32(Buf + 16, 0x4e800420);          // bctr
516
22
}
517
518
22
void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
519
22
  Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
520
22
                         0, IS);
521
22
  S->NeedsTocRestore = true;
522
22
}
523
524
151
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
525
526
151
Thunk::~Thunk() = default;
527
528
9
static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
529
9
  if (Type != R_AARCH64_CALL26 && 
Type != R_AARCH64_JUMP261
)
530
0
    fatal("unrecognized relocation type");
531
9
  if (Config->Pic)
532
3
    return make<AArch64ADRPThunk>(S);
533
6
  return make<AArch64ABSLongThunk>(S);
534
6
}
535
536
// Creates a thunk for Thumb-ARM interworking.
537
87
static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
538
87
  // ARM relocations need ARM to Thumb interworking Thunks.
539
87
  // Thumb relocations need Thumb to ARM relocations.
540
87
  // Use position independent Thunks if we require position independent code.
541
87
  switch (Reloc) {
542
87
  case R_ARM_PC24:
543
21
  case R_ARM_PLT32:
544
21
  case R_ARM_JUMP24:
545
21
  case R_ARM_CALL:
546
21
    if (Config->Pic)
547
5
      return make<ARMV7PILongThunk>(S);
548
16
    return make<ARMV7ABSLongThunk>(S);
549
66
  case R_ARM_THM_JUMP19:
550
66
  case R_ARM_THM_JUMP24:
551
66
  case R_ARM_THM_CALL:
552
66
    if (Config->Pic)
553
18
      return make<ThumbV7PILongThunk>(S);
554
48
    return make<ThumbV7ABSLongThunk>(S);
555
0
  }
556
0
  fatal("unrecognized relocation type");
557
0
}
558
559
33
static Thunk *addThunkMips(RelType Type, Symbol &S) {
560
33
  if ((S.StOther & STO_MIPS_MICROMIPS) && 
isMipsR6()4
)
561
2
    return make<MicroMipsR6Thunk>(S);
562
31
  if (S.StOther & STO_MIPS_MICROMIPS)
563
2
    return make<MicroMipsThunk>(S);
564
29
  return make<MipsThunk>(S);
565
29
}
566
567
22
static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
568
22
  if (Type == R_PPC64_REL24)
569
22
    return make<PPC64PltCallStub>(S);
570
0
  fatal("unexpected relocation type");
571
0
}
572
573
151
Thunk *addThunk(RelType Type, Symbol &S) {
574
151
  if (Config->EMachine == EM_AARCH64)
575
9
    return addThunkAArch64(Type, S);
576
142
577
142
  if (Config->EMachine == EM_ARM)
578
87
    return addThunkArm(Type, S);
579
55
580
55
  if (Config->EMachine == EM_MIPS)
581
33
    return addThunkMips(Type, S);
582
22
583
22
  if (Config->EMachine == EM_PPC64)
584
22
    return addThunkPPC64(Type, S);
585
0
586
0
  llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
587
0
}
588
589
} // end namespace elf
590
} // end namespace lld