Coverage Report

Created: 2018-01-17 21:32

/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::support::endian;
44
using namespace llvm::ELF;
45
46
namespace lld {
47
namespace elf {
48
49
namespace {
50
51
// AArch64 long range Thunks
52
class AArch64ABSLongThunk final : public Thunk {
53
public:
54
4
  AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
55
8
  uint32_t size() const override { return 16; }
56
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
57
  void addSymbols(ThunkSection &IS) override;
58
};
59
60
class AArch64ADRPThunk final : public Thunk {
61
public:
62
0
  AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
63
0
  uint32_t size() const override { return 12; }
64
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
65
  void addSymbols(ThunkSection &IS) override;
66
};
67
68
// Specific ARM Thunk implementations. The naming convention is:
69
// Source State, TargetState, Target Requirement, ABS or PI, Range
70
class ARMV7ABSLongThunk final : public Thunk {
71
public:
72
10
  ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
73
74
20
  uint32_t size() const override { return 12; }
75
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
76
  void addSymbols(ThunkSection &IS) override;
77
  bool isCompatibleWith(RelType Type) const override;
78
};
79
80
class ARMV7PILongThunk final : public Thunk {
81
public:
82
4
  ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {}
83
84
8
  uint32_t size() const override { return 16; }
85
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
86
  void addSymbols(ThunkSection &IS) override;
87
  bool isCompatibleWith(RelType Type) const override;
88
};
89
90
class ThumbV7ABSLongThunk final : public Thunk {
91
public:
92
16
  ThumbV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
93
94
32
  uint32_t size() const override { return 10; }
95
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
96
  void addSymbols(ThunkSection &IS) override;
97
  bool isCompatibleWith(RelType Type) const override;
98
};
99
100
class ThumbV7PILongThunk final : public Thunk {
101
public:
102
10
  ThumbV7PILongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
103
104
20
  uint32_t size() const override { return 12; }
105
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
106
  void addSymbols(ThunkSection &IS) override;
107
  bool isCompatibleWith(RelType Type) const override;
108
};
109
110
// MIPS LA25 thunk
111
class MipsThunk final : public Thunk {
112
public:
113
16
  MipsThunk(Symbol &Dest) : Thunk(Dest) {}
114
115
32
  uint32_t size() const override { return 16; }
116
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
117
  void addSymbols(ThunkSection &IS) override;
118
  InputSection *getTargetInputSection() const override;
119
};
120
121
// microMIPS R2-R5 LA25 thunk
122
class MicroMipsThunk final : public Thunk {
123
public:
124
2
  MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
125
126
4
  uint32_t size() const override { return 14; }
127
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
128
  void addSymbols(ThunkSection &IS) override;
129
  InputSection *getTargetInputSection() const override;
130
};
131
132
// microMIPS R6 LA25 thunk
133
class MicroMipsR6Thunk final : public Thunk {
134
public:
135
0
  MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
136
137
0
  uint32_t size() const override { return 12; }
138
  void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
139
  void addSymbols(ThunkSection &IS) override;
140
  InputSection *getTargetInputSection() const override;
141
};
142
143
} // end anonymous namespace
144
145
// AArch64 long range Thunks
146
147
4
static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
148
4
  uint64_t V = S.isInPlt() ? 
S.getPltVA()0
: S.getVA();
149
4
  return V;
150
4
}
151
152
4
void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
153
4
  const uint8_t Data[] = {
154
4
    0x50, 0x00, 0x00, 0x58, //     ldr x16, L0
155
4
    0x00, 0x02, 0x1f, 0xd6, //     br  x16
156
4
    0x00, 0x00, 0x00, 0x00, // L0: .xword S
157
4
    0x00, 0x00, 0x00, 0x00,
158
4
  };
159
4
  uint64_t S = getAArch64ThunkDestVA(Destination);
160
4
  memcpy(Buf, Data, sizeof(Data));
161
4
  Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
162
4
}
163
164
4
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
165
4
  ThunkSym = addSyntheticLocal(
166
4
      Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC,
167
4
      Offset, size(), IS);
168
4
  addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
169
4
  addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, IS);
170
4
}
171
172
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
173
// using the small code model, including pc-relative ones. At time of writing
174
// clang and gcc do not support the large code model for position independent
175
// code so it is safe to use this for position independent thunks without
176
// worrying about the destination being more than 4Gb away.
177
0
void AArch64ADRPThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
178
0
  const uint8_t Data[] = {
179
0
      0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
180
0
      0x10, 0x02, 0x00, 0x91, // add  x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
181
0
      0x00, 0x02, 0x1f, 0xd6, // br   x16
182
0
  };
183
0
  uint64_t S = getAArch64ThunkDestVA(Destination);
184
0
  uint64_t P = ThunkSym->getVA();
185
0
  memcpy(Buf, Data, sizeof(Data));
186
0
  Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
187
0
                      getAArch64Page(S) - getAArch64Page(P));
188
0
  Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
189
0
}
190
191
void AArch64ADRPThunk::addSymbols(ThunkSection &IS)
192
0
{
193
0
  ThunkSym = addSyntheticLocal(
194
0
      Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
195
0
      Offset, size(), IS);
196
0
  addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
197
0
}
198
199
// ARM Target Thunks
200
40
static uint64_t getARMThunkDestVA(const Symbol &S) {
201
40
  uint64_t V = S.isInPlt() ? 
S.getPltVA()8
:
S.getVA()32
;
202
40
  return SignExtend64<32>(V);
203
40
}
204
205
10
void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
206
10
  const uint8_t Data[] = {
207
10
      0x00, 0xc0, 0x00, 0xe3, // movw         ip,:lower16:S
208
10
      0x00, 0xc0, 0x40, 0xe3, // movt         ip,:upper16:S
209
10
      0x1c, 0xff, 0x2f, 0xe1, // bx   ip
210
10
  };
211
10
  uint64_t S = getARMThunkDestVA(Destination);
212
10
  memcpy(Buf, Data, sizeof(Data));
213
10
  Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
214
10
  Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
215
10
}
216
217
10
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
218
10
  ThunkSym = addSyntheticLocal(
219
10
      Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
220
10
      Offset, size(), IS);
221
10
  addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
222
10
}
223
224
3
bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
225
3
  // Thumb branch relocations can't use BLX
226
3
  return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
227
3
}
228
229
16
void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
230
16
  const uint8_t Data[] = {
231
16
      0x40, 0xf2, 0x00, 0x0c, // movw         ip, :lower16:S
232
16
      0xc0, 0xf2, 0x00, 0x0c, // movt         ip, :upper16:S
233
16
      0x60, 0x47,             // bx   ip
234
16
  };
235
16
  uint64_t S = getARMThunkDestVA(Destination);
236
16
  memcpy(Buf, Data, sizeof(Data));
237
16
  Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
238
16
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
239
16
}
240
241
16
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
242
16
  ThunkSym = addSyntheticLocal(
243
16
      Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
244
16
      Offset | 0x1, size(), IS);
245
16
  addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
246
16
}
247
248
10
bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
249
10
  // ARM branch relocations can't use BLX
250
10
  return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
251
10
}
252
253
4
void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
254
4
  const uint8_t Data[] = {
255
4
      0xf0, 0xcf, 0x0f, 0xe3, // P:  movw ip,:lower16:S - (P + (L1-P) + 8)
256
4
      0x00, 0xc0, 0x40, 0xe3, //     movt ip,:upper16:S - (P + (L1-P) + 8)
257
4
      0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
258
4
      0x1c, 0xff, 0x2f, 0xe1, //     bx r12
259
4
  };
260
4
  uint64_t S = getARMThunkDestVA(Destination);
261
4
  uint64_t P = ThunkSym->getVA();
262
4
  uint64_t Offset = S - P - 16;
263
4
  memcpy(Buf, Data, sizeof(Data));
264
4
  Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
265
4
  Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
266
4
}
267
268
4
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
269
4
  ThunkSym = addSyntheticLocal(
270
4
      Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
271
4
      Offset, size(), IS);
272
4
  addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
273
4
}
274
275
1
bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
276
1
  // Thumb branch relocations can't use BLX
277
1
  return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
278
1
}
279
280
10
void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
281
10
  const uint8_t Data[] = {
282
10
      0x4f, 0xf6, 0xf4, 0x7c, // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
283
10
      0xc0, 0xf2, 0x00, 0x0c, //     movt ip,:upper16:S - (P + (L1-P) + 4)
284
10
      0xfc, 0x44,             // L1: add  r12, pc
285
10
      0x60, 0x47,             //     bx   r12
286
10
  };
287
10
  uint64_t S = getARMThunkDestVA(Destination);
288
10
  uint64_t P = ThunkSym->getVA() & ~0x1;
289
10
  uint64_t Offset = S - P - 12;
290
10
  memcpy(Buf, Data, sizeof(Data));
291
10
  Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
292
10
  Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
293
10
}
294
295
10
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
296
10
  ThunkSym = addSyntheticLocal(
297
10
      Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC,
298
10
      Offset | 0x1, size(), IS);
299
10
  addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
300
10
}
301
302
5
bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
303
5
  // ARM branch relocations can't use BLX
304
5
  return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
305
5
}
306
307
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
308
16
void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
309
16
  uint64_t S = Destination.getVA();
310
16
  write32(Buf, 0x3c190000, Config->Endianness); // lui   $25, %hi(func)
311
16
  write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j     func
312
16
  write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
313
16
  write32(Buf + 12, 0x00000000, Config->Endianness); // nop
314
16
  Target->relocateOne(Buf, R_MIPS_HI16, S);
315
16
  Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
316
16
}
317
318
16
void MipsThunk::addSymbols(ThunkSection &IS) {
319
16
  ThunkSym =
320
16
      addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()),
321
16
                        STT_FUNC, Offset, size(), IS);
322
16
}
323
324
40
InputSection *MipsThunk::getTargetInputSection() const {
325
40
  auto &DR = cast<Defined>(Destination);
326
40
  return dyn_cast<InputSection>(DR.Section);
327
40
}
328
329
// Write microMIPS R2-R5 LA25 thunk code
330
// to call PIC function from the non-PIC one.
331
2
void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
332
2
  uint64_t S = Destination.getVA() | 1;
333
2
  write16(Buf, 0x41b9, Config->Endianness);       // lui   $25, %hi(func)
334
2
  write16(Buf + 4, 0xd400, Config->Endianness);   // j     func
335
2
  write16(Buf + 8, 0x3339, Config->Endianness);   // addiu $25, $25, %lo(func)
336
2
  write16(Buf + 12, 0x0c00, Config->Endianness);  // nop
337
2
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
338
2
  Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
339
2
  Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
340
2
}
341
342
2
void MicroMipsThunk::addSymbols(ThunkSection &IS) {
343
2
  ThunkSym =
344
2
      addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
345
2
                        STT_FUNC, Offset, size(), IS);
346
2
  ThunkSym->StOther |= STO_MIPS_MICROMIPS;
347
2
}
348
349
4
InputSection *MicroMipsThunk::getTargetInputSection() const {
350
4
  auto &DR = cast<Defined>(Destination);
351
4
  return dyn_cast<InputSection>(DR.Section);
352
4
}
353
354
// Write microMIPS R6 LA25 thunk code
355
// to call PIC function from the non-PIC one.
356
0
void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const {
357
0
  uint64_t S = Destination.getVA() | 1;
358
0
  uint64_t P = ThunkSym->getVA();
359
0
  write16(Buf, 0x1320, Config->Endianness);       // lui   $25, %hi(func)
360
0
  write16(Buf + 4, 0x3339, Config->Endianness);   // addiu $25, $25, %lo(func)
361
0
  write16(Buf + 8, 0x9400, Config->Endianness);   // bc    func
362
0
  Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
363
0
  Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
364
0
  Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
365
0
}
366
367
0
void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
368
0
  ThunkSym =
369
0
      addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
370
0
                        STT_FUNC, Offset, size(), IS);
371
0
  ThunkSym->StOther |= STO_MIPS_MICROMIPS;
372
0
}
373
374
0
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
375
0
  auto &DR = cast<Defined>(Destination);
376
0
  return dyn_cast<InputSection>(DR.Section);
377
0
}
378
379
62
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
380
381
62
Thunk::~Thunk() = default;
382
383
4
static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
384
4
  if (Type != R_AARCH64_CALL26 && 
Type != R_AARCH64_JUMP261
)
385
0
    fatal("unrecognized relocation type");
386
4
  if (Config->Pic)
387
0
    return make<AArch64ADRPThunk>(S);
388
4
  return make<AArch64ABSLongThunk>(S);
389
4
}
390
391
// Creates a thunk for Thumb-ARM interworking.
392
40
static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
393
40
  // ARM relocations need ARM to Thumb interworking Thunks.
394
40
  // Thumb relocations need Thumb to ARM relocations.
395
40
  // Use position independent Thunks if we require position independent code.
396
40
  switch (Reloc) {
397
40
  case R_ARM_PC24:
398
14
  case R_ARM_PLT32:
399
14
  case R_ARM_JUMP24:
400
14
  case R_ARM_CALL:
401
14
    if (Config->Pic)
402
4
      return make<ARMV7PILongThunk>(S);
403
10
    return make<ARMV7ABSLongThunk>(S);
404
26
  case R_ARM_THM_JUMP19:
405
26
  case R_ARM_THM_JUMP24:
406
26
  case R_ARM_THM_CALL:
407
26
    if (Config->Pic)
408
10
      return make<ThumbV7PILongThunk>(S);
409
16
    return make<ThumbV7ABSLongThunk>(S);
410
0
  }
411
0
  fatal("unrecognized relocation type");
412
0
}
413
414
18
static Thunk *addThunkMips(RelType Type, Symbol &S) {
415
18
  if ((S.StOther & STO_MIPS_MICROMIPS) && 
isMipsR6()2
)
416
0
    return make<MicroMipsR6Thunk>(S);
417
18
  if (S.StOther & STO_MIPS_MICROMIPS)
418
2
    return make<MicroMipsThunk>(S);
419
16
  return make<MipsThunk>(S);
420
16
}
421
422
62
Thunk *addThunk(RelType Type, Symbol &S) {
423
62
  if (Config->EMachine == EM_AARCH64)
424
4
    return addThunkAArch64(Type, S);
425
58
  else if (Config->EMachine == EM_ARM)
426
40
    return addThunkArm(Type, S);
427
18
  else if (Config->EMachine == EM_MIPS)
428
18
    return addThunkMips(Type, S);
429
0
  llvm_unreachable("add Thunk only supported for ARM and Mips");
430
0
  return nullptr;
431
0
}
432
433
} // end namespace elf
434
} // end namespace lld