150
|
1 //===- Thunks.cpp --------------------------------------------------------===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===---------------------------------------------------------------------===//
|
|
8 //
|
|
9 // This file contains Thunk subclasses.
|
|
10 //
|
|
11 // A thunk is a small piece of code written after an input section
|
|
12 // which is used to jump between "incompatible" functions
|
|
13 // such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions.
|
|
14 //
|
|
15 // If a jump target is too far and its address doesn't fit to a
|
|
16 // short jump instruction, we need to create a thunk too, but we
|
|
17 // haven't supported it yet.
|
|
18 //
|
|
19 // i386 and x86-64 don't need thunks.
|
|
20 //
|
|
21 //===---------------------------------------------------------------------===//
|
|
22
|
|
23 #include "Thunks.h"
|
|
24 #include "Config.h"
|
|
25 #include "InputSection.h"
|
|
26 #include "OutputSections.h"
|
|
27 #include "Symbols.h"
|
|
28 #include "SyntheticSections.h"
|
|
29 #include "Target.h"
|
|
30 #include "lld/Common/ErrorHandler.h"
|
|
31 #include "lld/Common/Memory.h"
|
|
32 #include "llvm/BinaryFormat/ELF.h"
|
|
33 #include "llvm/Support/Casting.h"
|
|
34 #include "llvm/Support/Endian.h"
|
|
35 #include "llvm/Support/ErrorHandling.h"
|
|
36 #include "llvm/Support/MathExtras.h"
|
|
37 #include <cstdint>
|
|
38 #include <cstring>
|
|
39
|
|
40 using namespace llvm;
|
|
41 using namespace llvm::object;
|
|
42 using namespace llvm::ELF;
|
173
|
43 using namespace lld;
|
|
44 using namespace lld::elf;
|
150
|
45
|
|
46 namespace {
|
|
47
|
|
48 // AArch64 long range Thunks
|
|
49 class AArch64ABSLongThunk final : public Thunk {
|
|
50 public:
|
|
51 AArch64ABSLongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
|
|
52 uint32_t size() override { return 16; }
|
|
53 void writeTo(uint8_t *buf) override;
|
|
54 void addSymbols(ThunkSection &isec) override;
|
|
55 };
|
|
56
|
|
57 class AArch64ADRPThunk final : public Thunk {
|
|
58 public:
|
|
59 AArch64ADRPThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
|
|
60 uint32_t size() override { return 12; }
|
|
61 void writeTo(uint8_t *buf) override;
|
|
62 void addSymbols(ThunkSection &isec) override;
|
|
63 };
|
|
64
|
|
65 // Base class for ARM thunks.
|
|
66 //
|
|
67 // An ARM thunk may be either short or long. A short thunk is simply a branch
|
|
68 // (B) instruction, and it may be used to call ARM functions when the distance
|
|
69 // from the thunk to the target is less than 32MB. Long thunks can branch to any
|
|
70 // virtual address and can switch between ARM and Thumb, and they are
|
|
71 // implemented in the derived classes. This class tries to create a short thunk
|
|
72 // if the target is in range, otherwise it creates a long thunk.
|
|
73 class ARMThunk : public Thunk {
|
|
74 public:
|
207
|
75 ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
|
150
|
76
|
|
77 bool getMayUseShortThunk();
|
|
78 uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
|
|
79 void writeTo(uint8_t *buf) override;
|
|
80 bool isCompatibleWith(const InputSection &isec,
|
|
81 const Relocation &rel) const override;
|
|
82
|
|
83 // Returns the size of a long thunk.
|
|
84 virtual uint32_t sizeLong() = 0;
|
|
85
|
|
86 // Writes a long thunk to Buf.
|
|
87 virtual void writeLong(uint8_t *buf) = 0;
|
|
88
|
|
89 private:
|
|
90 // This field tracks whether all previously considered layouts would allow
|
|
91 // this thunk to be short. If we have ever needed a long thunk, we always
|
|
92 // create a long thunk, even if the thunk may be short given the current
|
|
93 // distance to the target. We do this because transitioning from long to short
|
|
94 // can create layout oscillations in certain corner cases which would prevent
|
|
95 // the layout from converging.
|
|
96 bool mayUseShortThunk = true;
|
|
97 };
|
|
98
|
|
99 // Base class for Thumb-2 thunks.
|
|
100 //
|
|
101 // This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
|
|
102 // which has a range of 16MB.
|
|
103 class ThumbThunk : public Thunk {
|
|
104 public:
|
207
|
105 ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
|
|
106 alignment = 2;
|
|
107 }
|
150
|
108
|
|
109 bool getMayUseShortThunk();
|
|
110 uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
|
|
111 void writeTo(uint8_t *buf) override;
|
|
112 bool isCompatibleWith(const InputSection &isec,
|
|
113 const Relocation &rel) const override;
|
|
114
|
|
115 // Returns the size of a long thunk.
|
|
116 virtual uint32_t sizeLong() = 0;
|
|
117
|
|
118 // Writes a long thunk to Buf.
|
|
119 virtual void writeLong(uint8_t *buf) = 0;
|
|
120
|
|
121 private:
|
|
122 // See comment in ARMThunk above.
|
|
123 bool mayUseShortThunk = true;
|
|
124 };
|
|
125
|
|
126 // Specific ARM Thunk implementations. The naming convention is:
|
|
127 // Source State, TargetState, Target Requirement, ABS or PI, Range
|
|
128 class ARMV7ABSLongThunk final : public ARMThunk {
|
|
129 public:
|
207
|
130 ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
|
150
|
131
|
|
132 uint32_t sizeLong() override { return 12; }
|
|
133 void writeLong(uint8_t *buf) override;
|
|
134 void addSymbols(ThunkSection &isec) override;
|
|
135 };
|
|
136
|
|
137 class ARMV7PILongThunk final : public ARMThunk {
|
|
138 public:
|
207
|
139 ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
|
150
|
140
|
|
141 uint32_t sizeLong() override { return 16; }
|
|
142 void writeLong(uint8_t *buf) override;
|
|
143 void addSymbols(ThunkSection &isec) override;
|
|
144 };
|
|
145
|
|
146 class ThumbV7ABSLongThunk final : public ThumbThunk {
|
|
147 public:
|
207
|
148 ThumbV7ABSLongThunk(Symbol &dest, int64_t addend)
|
|
149 : ThumbThunk(dest, addend) {}
|
150
|
150
|
|
151 uint32_t sizeLong() override { return 10; }
|
|
152 void writeLong(uint8_t *buf) override;
|
|
153 void addSymbols(ThunkSection &isec) override;
|
|
154 };
|
|
155
|
|
156 class ThumbV7PILongThunk final : public ThumbThunk {
|
|
157 public:
|
207
|
158 ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {}
|
150
|
159
|
|
160 uint32_t sizeLong() override { return 12; }
|
|
161 void writeLong(uint8_t *buf) override;
|
|
162 void addSymbols(ThunkSection &isec) override;
|
|
163 };
|
|
164
|
|
165 // Implementations of Thunks for older Arm architectures that do not support
|
|
166 // the movt/movw instructions. These thunks require at least Architecture v5
|
|
167 // as used on processors such as the Arm926ej-s. There are no Thumb entry
|
|
168 // points as there is no Thumb branch instruction on these architecture that
|
|
169 // can result in a thunk
|
|
170 class ARMV5ABSLongThunk final : public ARMThunk {
|
|
171 public:
|
207
|
172 ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
|
150
|
173
|
|
174 uint32_t sizeLong() override { return 8; }
|
|
175 void writeLong(uint8_t *buf) override;
|
|
176 void addSymbols(ThunkSection &isec) override;
|
|
177 bool isCompatibleWith(const InputSection &isec,
|
|
178 const Relocation &rel) const override;
|
|
179 };
|
|
180
|
|
181 class ARMV5PILongThunk final : public ARMThunk {
|
|
182 public:
|
207
|
183 ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
|
150
|
184
|
|
185 uint32_t sizeLong() override { return 16; }
|
|
186 void writeLong(uint8_t *buf) override;
|
|
187 void addSymbols(ThunkSection &isec) override;
|
|
188 bool isCompatibleWith(const InputSection &isec,
|
|
189 const Relocation &rel) const override;
|
|
190 };
|
|
191
|
|
192 // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
|
|
193 class ThumbV6MABSLongThunk final : public ThumbThunk {
|
|
194 public:
|
207
|
195 ThumbV6MABSLongThunk(Symbol &dest, int64_t addend)
|
|
196 : ThumbThunk(dest, addend) {}
|
150
|
197
|
|
198 uint32_t sizeLong() override { return 12; }
|
|
199 void writeLong(uint8_t *buf) override;
|
|
200 void addSymbols(ThunkSection &isec) override;
|
|
201 };
|
|
202
|
|
203 class ThumbV6MPILongThunk final : public ThumbThunk {
|
|
204 public:
|
207
|
205 ThumbV6MPILongThunk(Symbol &dest, int64_t addend)
|
|
206 : ThumbThunk(dest, addend) {}
|
150
|
207
|
|
208 uint32_t sizeLong() override { return 16; }
|
|
209 void writeLong(uint8_t *buf) override;
|
|
210 void addSymbols(ThunkSection &isec) override;
|
|
211 };
|
|
212
|
|
213 // MIPS LA25 thunk
|
|
214 class MipsThunk final : public Thunk {
|
|
215 public:
|
|
216 MipsThunk(Symbol &dest) : Thunk(dest, 0) {}
|
|
217
|
|
218 uint32_t size() override { return 16; }
|
|
219 void writeTo(uint8_t *buf) override;
|
|
220 void addSymbols(ThunkSection &isec) override;
|
|
221 InputSection *getTargetInputSection() const override;
|
|
222 };
|
|
223
|
|
224 // microMIPS R2-R5 LA25 thunk
|
|
225 class MicroMipsThunk final : public Thunk {
|
|
226 public:
|
|
227 MicroMipsThunk(Symbol &dest) : Thunk(dest, 0) {}
|
|
228
|
|
229 uint32_t size() override { return 14; }
|
|
230 void writeTo(uint8_t *buf) override;
|
|
231 void addSymbols(ThunkSection &isec) override;
|
|
232 InputSection *getTargetInputSection() const override;
|
|
233 };
|
|
234
|
|
235 // microMIPS R6 LA25 thunk
|
|
236 class MicroMipsR6Thunk final : public Thunk {
|
|
237 public:
|
|
238 MicroMipsR6Thunk(Symbol &dest) : Thunk(dest, 0) {}
|
|
239
|
|
240 uint32_t size() override { return 12; }
|
|
241 void writeTo(uint8_t *buf) override;
|
|
242 void addSymbols(ThunkSection &isec) override;
|
|
243 InputSection *getTargetInputSection() const override;
|
|
244 };
|
|
245
|
|
246 class PPC32PltCallStub final : public Thunk {
|
|
247 public:
|
|
248 // For R_PPC_PLTREL24, Thunk::addend records the addend which will be used to
|
|
249 // decide the offsets in the call stub.
|
|
250 PPC32PltCallStub(const InputSection &isec, const Relocation &rel,
|
|
251 Symbol &dest)
|
|
252 : Thunk(dest, rel.addend), file(isec.file) {}
|
|
253 uint32_t size() override { return 16; }
|
|
254 void writeTo(uint8_t *buf) override;
|
|
255 void addSymbols(ThunkSection &isec) override;
|
|
256 bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override;
|
|
257
|
|
258 private:
|
|
259 // Records the call site of the call stub.
|
|
260 const InputFile *file;
|
|
261 };
|
|
262
|
|
263 class PPC32LongThunk final : public Thunk {
|
|
264 public:
|
|
265 PPC32LongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
|
|
266 uint32_t size() override { return config->isPic ? 32 : 16; }
|
|
267 void writeTo(uint8_t *buf) override;
|
|
268 void addSymbols(ThunkSection &isec) override;
|
|
269 };
|
|
270
|
|
271 // PPC64 Plt call stubs.
|
|
272 // Any call site that needs to call through a plt entry needs a call stub in
|
|
273 // the .text section. The call stub is responsible for:
|
|
274 // 1) Saving the toc-pointer to the stack.
|
|
275 // 2) Loading the target functions address from the procedure linkage table into
|
|
276 // r12 for use by the target functions global entry point, and into the count
|
|
277 // register.
|
|
278 // 3) Transferring control to the target function through an indirect branch.
|
|
279 class PPC64PltCallStub final : public Thunk {
|
|
280 public:
|
|
281 PPC64PltCallStub(Symbol &dest) : Thunk(dest, 0) {}
|
|
282 uint32_t size() override { return 20; }
|
|
283 void writeTo(uint8_t *buf) override;
|
|
284 void addSymbols(ThunkSection &isec) override;
|
207
|
285 bool isCompatibleWith(const InputSection &isec,
|
|
286 const Relocation &rel) const override;
|
|
287 };
|
|
288
|
|
289 // PPC64 R2 Save Stub
|
|
290 // When the caller requires a valid R2 TOC pointer but the callee does not
|
|
291 // require a TOC pointer and the callee cannot guarantee that it doesn't
|
|
292 // clobber R2 then we need to save R2. This stub:
|
|
293 // 1) Saves the TOC pointer to the stack.
|
|
294 // 2) Tail calls the callee.
|
|
295 class PPC64R2SaveStub final : public Thunk {
|
|
296 public:
|
|
297 PPC64R2SaveStub(Symbol &dest, int64_t addend) : Thunk(dest, addend) {
|
|
298 alignment = 16;
|
|
299 }
|
|
300
|
|
301 // To prevent oscillations in layout when moving from short to long thunks
|
|
302 // we make sure that once a thunk has been set to long it cannot go back.
|
|
303 bool getMayUseShortThunk() {
|
|
304 if (!mayUseShortThunk)
|
|
305 return false;
|
|
306 if (!isInt<26>(computeOffset())) {
|
|
307 mayUseShortThunk = false;
|
|
308 return false;
|
|
309 }
|
|
310 return true;
|
|
311 }
|
|
312 uint32_t size() override { return getMayUseShortThunk() ? 8 : 32; }
|
|
313 void writeTo(uint8_t *buf) override;
|
|
314 void addSymbols(ThunkSection &isec) override;
|
|
315 bool isCompatibleWith(const InputSection &isec,
|
|
316 const Relocation &rel) const override;
|
|
317
|
|
318 private:
|
|
319 // Transitioning from long to short can create layout oscillations in
|
|
320 // certain corner cases which would prevent the layout from converging.
|
|
321 // This is similar to the handling for ARMThunk.
|
|
322 bool mayUseShortThunk = true;
|
|
323 int64_t computeOffset() const {
|
|
324 return destination.getVA() - (getThunkTargetSym()->getVA() + 4);
|
|
325 }
|
|
326 };
|
|
327
|
|
328 // PPC64 R12 Setup Stub
|
|
329 // When a caller that does not maintain a toc-pointer performs a local call to
|
|
330 // a callee which requires a toc-pointer then we need this stub to place the
|
|
331 // callee's global entry point into r12 without a save of R2.
|
|
332 class PPC64R12SetupStub final : public Thunk {
|
|
333 public:
|
|
334 PPC64R12SetupStub(Symbol &dest) : Thunk(dest, 0) { alignment = 16; }
|
|
335 uint32_t size() override { return 32; }
|
|
336 void writeTo(uint8_t *buf) override;
|
|
337 void addSymbols(ThunkSection &isec) override;
|
|
338 bool isCompatibleWith(const InputSection &isec,
|
|
339 const Relocation &rel) const override;
|
|
340 };
|
|
341
|
|
342 // PPC64 PC-relative PLT Stub
|
|
343 // When a caller that does not maintain a toc-pointer performs an extern call
|
|
344 // then this stub is needed for:
|
|
345 // 1) Loading the target functions address from the procedure linkage table into
|
|
346 // r12 for use by the target functions global entry point, and into the count
|
|
347 // register with pc-relative instructions.
|
|
348 // 2) Transferring control to the target function through an indirect branch.
|
|
349 class PPC64PCRelPLTStub final : public Thunk {
|
|
350 public:
|
|
351 PPC64PCRelPLTStub(Symbol &dest) : Thunk(dest, 0) { alignment = 16; }
|
|
352 uint32_t size() override { return 32; }
|
|
353 void writeTo(uint8_t *buf) override;
|
|
354 void addSymbols(ThunkSection &isec) override;
|
|
355 bool isCompatibleWith(const InputSection &isec,
|
|
356 const Relocation &rel) const override;
|
150
|
357 };
|
|
358
|
|
359 // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
|
|
360 // alignment. This gives a possible 26 bits of 'reach'. If the call offset is
|
207
|
361 // larger than that we need to emit a long-branch thunk. The target address
|
150
|
362 // of the callee is stored in a table to be accessed TOC-relative. Since the
|
|
363 // call must be local (a non-local call will have a PltCallStub instead) the
|
|
364 // table stores the address of the callee's local entry point. For
|
|
365 // position-independent code a corresponding relative dynamic relocation is
|
|
366 // used.
|
|
367 class PPC64LongBranchThunk : public Thunk {
|
|
368 public:
|
207
|
369 uint32_t size() override { return 32; }
|
150
|
370 void writeTo(uint8_t *buf) override;
|
|
371 void addSymbols(ThunkSection &isec) override;
|
207
|
372 bool isCompatibleWith(const InputSection &isec,
|
|
373 const Relocation &rel) const override;
|
150
|
374
|
|
375 protected:
|
|
376 PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {}
|
|
377 };
|
|
378
|
|
379 class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
|
|
380 public:
|
|
381 PPC64PILongBranchThunk(Symbol &dest, int64_t addend)
|
|
382 : PPC64LongBranchThunk(dest, addend) {
|
|
383 assert(!dest.isPreemptible);
|
|
384 if (Optional<uint32_t> index =
|
|
385 in.ppc64LongBranchTarget->addEntry(&dest, addend)) {
|
|
386 mainPart->relaDyn->addReloc(
|
|
387 {target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8),
|
|
388 true, &dest,
|
|
389 addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)});
|
|
390 }
|
|
391 }
|
|
392 };
|
|
393
|
|
394 class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
|
|
395 public:
|
|
396 PPC64PDLongBranchThunk(Symbol &dest, int64_t addend)
|
|
397 : PPC64LongBranchThunk(dest, addend) {
|
|
398 in.ppc64LongBranchTarget->addEntry(&dest, addend);
|
|
399 }
|
|
400 };
|
|
401
|
207
|
402 // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
|
|
403 // alignment. This gives a possible 26 bits of 'reach'. If the caller and
|
|
404 // callee do not use toc and the call offset is larger than 26 bits,
|
|
405 // we need to emit a pc-rel based long-branch thunk. The target address of
|
|
406 // the callee is computed with a PC-relative offset.
|
|
407 class PPC64PCRelLongBranchThunk final : public Thunk {
|
|
408 public:
|
|
409 PPC64PCRelLongBranchThunk(Symbol &dest, int64_t addend)
|
|
410 : Thunk(dest, addend) {
|
|
411 alignment = 16;
|
|
412 }
|
|
413 uint32_t size() override { return 32; }
|
|
414 void writeTo(uint8_t *buf) override;
|
|
415 void addSymbols(ThunkSection &isec) override;
|
|
416 bool isCompatibleWith(const InputSection &isec,
|
|
417 const Relocation &rel) const override;
|
|
418 };
|
|
419
|
150
|
420 } // end anonymous namespace
|
|
421
|
|
422 Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
|
|
423 InputSectionBase §ion) {
|
|
424 Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section);
|
|
425 syms.push_back(d);
|
|
426 return d;
|
|
427 }
|
|
428
|
|
429 void Thunk::setOffset(uint64_t newOffset) {
|
|
430 for (Defined *d : syms)
|
|
431 d->value = d->value - offset + newOffset;
|
|
432 offset = newOffset;
|
|
433 }
|
|
434
|
|
435 // AArch64 long range Thunks
|
|
436
|
|
437 static uint64_t getAArch64ThunkDestVA(const Symbol &s, int64_t a) {
|
|
438 uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(a);
|
|
439 return v;
|
|
440 }
|
|
441
|
|
442 void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
|
|
443 const uint8_t data[] = {
|
|
444 0x50, 0x00, 0x00, 0x58, // ldr x16, L0
|
|
445 0x00, 0x02, 0x1f, 0xd6, // br x16
|
|
446 0x00, 0x00, 0x00, 0x00, // L0: .xword S
|
|
447 0x00, 0x00, 0x00, 0x00,
|
|
448 };
|
|
449 uint64_t s = getAArch64ThunkDestVA(destination, addend);
|
|
450 memcpy(buf, data, sizeof(data));
|
|
451 target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s);
|
|
452 }
|
|
453
|
|
454 void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
|
|
455 addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()),
|
|
456 STT_FUNC, 0, isec);
|
|
457 addSymbol("$x", STT_NOTYPE, 0, isec);
|
|
458 addSymbol("$d", STT_NOTYPE, 8, isec);
|
|
459 }
|
|
460
|
|
461 // This Thunk has a maximum range of 4Gb, this is sufficient for all programs
|
|
462 // using the small code model, including pc-relative ones. At time of writing
|
|
463 // clang and gcc do not support the large code model for position independent
|
|
464 // code so it is safe to use this for position independent thunks without
|
|
465 // worrying about the destination being more than 4Gb away.
|
|
466 void AArch64ADRPThunk::writeTo(uint8_t *buf) {
|
|
467 const uint8_t data[] = {
|
|
468 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
|
|
469 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
|
|
470 0x00, 0x02, 0x1f, 0xd6, // br x16
|
|
471 };
|
|
472 uint64_t s = getAArch64ThunkDestVA(destination, addend);
|
|
473 uint64_t p = getThunkTargetSym()->getVA();
|
|
474 memcpy(buf, data, sizeof(data));
|
|
475 target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
|
|
476 getAArch64Page(s) - getAArch64Page(p));
|
|
477 target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
|
|
478 }
|
|
479
|
|
480 void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
|
|
481 addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC,
|
|
482 0, isec);
|
|
483 addSymbol("$x", STT_NOTYPE, 0, isec);
|
|
484 }
|
|
485
|
|
486 // ARM Target Thunks
|
|
487 static uint64_t getARMThunkDestVA(const Symbol &s) {
|
|
488 uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
|
|
489 return SignExtend64<32>(v);
|
|
490 }
|
|
491
|
|
492 // This function returns true if the target is not Thumb and is within 2^26, and
|
|
493 // it has not previously returned false (see comment for mayUseShortThunk).
|
|
494 bool ARMThunk::getMayUseShortThunk() {
|
|
495 if (!mayUseShortThunk)
|
|
496 return false;
|
|
497 uint64_t s = getARMThunkDestVA(destination);
|
|
498 if (s & 1) {
|
|
499 mayUseShortThunk = false;
|
|
500 return false;
|
|
501 }
|
|
502 uint64_t p = getThunkTargetSym()->getVA();
|
|
503 int64_t offset = s - p - 8;
|
|
504 mayUseShortThunk = llvm::isInt<26>(offset);
|
|
505 return mayUseShortThunk;
|
|
506 }
|
|
507
|
|
508 void ARMThunk::writeTo(uint8_t *buf) {
|
|
509 if (!getMayUseShortThunk()) {
|
|
510 writeLong(buf);
|
|
511 return;
|
|
512 }
|
|
513
|
|
514 uint64_t s = getARMThunkDestVA(destination);
|
|
515 uint64_t p = getThunkTargetSym()->getVA();
|
|
516 int64_t offset = s - p - 8;
|
|
517 const uint8_t data[] = {
|
|
518 0x00, 0x00, 0x00, 0xea, // b S
|
|
519 };
|
|
520 memcpy(buf, data, sizeof(data));
|
|
521 target->relocateNoSym(buf, R_ARM_JUMP24, offset);
|
|
522 }
|
|
523
|
|
524 bool ARMThunk::isCompatibleWith(const InputSection &isec,
|
|
525 const Relocation &rel) const {
|
|
526 // Thumb branch relocations can't use BLX
|
|
527 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
|
|
528 }
|
|
529
|
|
530 // This function returns true if the target is Thumb and is within 2^25, and
|
|
531 // it has not previously returned false (see comment for mayUseShortThunk).
|
|
532 bool ThumbThunk::getMayUseShortThunk() {
|
|
533 if (!mayUseShortThunk)
|
|
534 return false;
|
|
535 uint64_t s = getARMThunkDestVA(destination);
|
|
536 if ((s & 1) == 0) {
|
|
537 mayUseShortThunk = false;
|
|
538 return false;
|
|
539 }
|
|
540 uint64_t p = getThunkTargetSym()->getVA() & ~1;
|
|
541 int64_t offset = s - p - 4;
|
|
542 mayUseShortThunk = llvm::isInt<25>(offset);
|
|
543 return mayUseShortThunk;
|
|
544 }
|
|
545
|
|
546 void ThumbThunk::writeTo(uint8_t *buf) {
|
|
547 if (!getMayUseShortThunk()) {
|
|
548 writeLong(buf);
|
|
549 return;
|
|
550 }
|
|
551
|
|
552 uint64_t s = getARMThunkDestVA(destination);
|
|
553 uint64_t p = getThunkTargetSym()->getVA();
|
|
554 int64_t offset = s - p - 4;
|
|
555 const uint8_t data[] = {
|
|
556 0x00, 0xf0, 0x00, 0xb0, // b.w S
|
|
557 };
|
|
558 memcpy(buf, data, sizeof(data));
|
|
559 target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset);
|
|
560 }
|
|
561
|
|
562 bool ThumbThunk::isCompatibleWith(const InputSection &isec,
|
|
563 const Relocation &rel) const {
|
|
564 // ARM branch relocations can't use BLX
|
|
565 return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
|
|
566 }
|
|
567
|
|
568 void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
|
|
569 const uint8_t data[] = {
|
|
570 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
|
|
571 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
|
|
572 0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
|
573 };
|
|
574 uint64_t s = getARMThunkDestVA(destination);
|
|
575 memcpy(buf, data, sizeof(data));
|
|
576 target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s);
|
|
577 target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s);
|
|
578 }
|
|
579
|
|
580 void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
|
|
581 addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()),
|
|
582 STT_FUNC, 0, isec);
|
|
583 addSymbol("$a", STT_NOTYPE, 0, isec);
|
|
584 }
|
|
585
|
|
586 void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
|
|
587 const uint8_t data[] = {
|
|
588 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
|
|
589 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
|
|
590 0x60, 0x47, // bx ip
|
|
591 };
|
|
592 uint64_t s = getARMThunkDestVA(destination);
|
|
593 memcpy(buf, data, sizeof(data));
|
|
594 target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s);
|
|
595 target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s);
|
|
596 }
|
|
597
|
|
598 void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
|
|
599 addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()),
|
|
600 STT_FUNC, 1, isec);
|
|
601 addSymbol("$t", STT_NOTYPE, 0, isec);
|
|
602 }
|
|
603
|
|
604 void ARMV7PILongThunk::writeLong(uint8_t *buf) {
|
|
605 const uint8_t data[] = {
|
|
606 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
|
|
607 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
|
|
608 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
|
609 0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
|
610 };
|
|
611 uint64_t s = getARMThunkDestVA(destination);
|
|
612 uint64_t p = getThunkTargetSym()->getVA();
|
|
613 int64_t offset = s - p - 16;
|
|
614 memcpy(buf, data, sizeof(data));
|
|
615 target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset);
|
|
616 target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset);
|
|
617 }
|
|
618
|
|
619 void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
|
|
620 addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC,
|
|
621 0, isec);
|
|
622 addSymbol("$a", STT_NOTYPE, 0, isec);
|
|
623 }
|
|
624
|
|
625 void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
|
|
626 const uint8_t data[] = {
|
|
627 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
|
|
628 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
|
|
629 0xfc, 0x44, // L1: add ip, pc
|
|
630 0x60, 0x47, // bx ip
|
|
631 };
|
|
632 uint64_t s = getARMThunkDestVA(destination);
|
|
633 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
|
|
634 int64_t offset = s - p - 12;
|
|
635 memcpy(buf, data, sizeof(data));
|
|
636 target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset);
|
|
637 target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset);
|
|
638 }
|
|
639
|
|
640 void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
|
|
641 addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()),
|
|
642 STT_FUNC, 1, isec);
|
|
643 addSymbol("$t", STT_NOTYPE, 0, isec);
|
|
644 }
|
|
645
|
|
646 void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
|
|
647 const uint8_t data[] = {
|
|
648 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
|
|
649 0x00, 0x00, 0x00, 0x00, // L1: .word S
|
|
650 };
|
|
651 memcpy(buf, data, sizeof(data));
|
|
652 target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
|
|
653 }
|
|
654
|
|
655 void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
|
|
656 addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()),
|
|
657 STT_FUNC, 0, isec);
|
|
658 addSymbol("$a", STT_NOTYPE, 0, isec);
|
|
659 addSymbol("$d", STT_NOTYPE, 4, isec);
|
|
660 }
|
|
661
|
|
662 bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec,
|
|
663 const Relocation &rel) const {
|
|
664 // Thumb branch relocations can't use BLX
|
|
665 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
|
|
666 }
|
|
667
|
|
668 void ARMV5PILongThunk::writeLong(uint8_t *buf) {
|
|
669 const uint8_t data[] = {
|
|
670 0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
|
|
671 0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
|
|
672 0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
|
673 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
|
|
674 };
|
|
675 uint64_t s = getARMThunkDestVA(destination);
|
|
676 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
|
|
677 memcpy(buf, data, sizeof(data));
|
|
678 target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
|
|
679 }
|
|
680
|
|
681 void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
|
|
682 addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC,
|
|
683 0, isec);
|
|
684 addSymbol("$a", STT_NOTYPE, 0, isec);
|
|
685 addSymbol("$d", STT_NOTYPE, 12, isec);
|
|
686 }
|
|
687
|
|
688 bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec,
|
|
689 const Relocation &rel) const {
|
|
690 // Thumb branch relocations can't use BLX
|
|
691 return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
|
|
692 }
|
|
693
|
|
694 void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
|
|
695 // Most Thumb instructions cannot access the high registers r8 - r15. As the
|
|
696 // only register we can corrupt is r12 we must instead spill a low register
|
|
697 // to the stack to use as a scratch register. We push r1 even though we
|
|
698 // don't need to get some space to use for the return address.
|
|
699 const uint8_t data[] = {
|
|
700 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers
|
|
701 0x01, 0x48, // ldr r0, [pc, #4] ; L1
|
|
702 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S
|
|
703 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest
|
|
704 0x00, 0x00, 0x00, 0x00 // L1: .word S
|
|
705 };
|
|
706 uint64_t s = getARMThunkDestVA(destination);
|
|
707 memcpy(buf, data, sizeof(data));
|
|
708 target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
|
|
709 }
|
|
710
|
|
711 void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
|
|
712 addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()),
|
|
713 STT_FUNC, 1, isec);
|
|
714 addSymbol("$t", STT_NOTYPE, 0, isec);
|
|
715 addSymbol("$d", STT_NOTYPE, 8, isec);
|
|
716 }
|
|
717
|
|
718 void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
|
|
719 // Most Thumb instructions cannot access the high registers r8 - r15. As the
|
|
720 // only register we can corrupt is ip (r12) we must instead spill a low
|
|
721 // register to the stack to use as a scratch register.
|
|
722 const uint8_t data[] = {
|
|
723 0x01, 0xb4, // P: push {r0} ; Obtain scratch register
|
|
724 0x02, 0x48, // ldr r0, [pc, #8] ; L2
|
|
725 0x84, 0x46, // mov ip, r0 ; high to low register
|
|
726 0x01, 0xbc, // pop {r0} ; restore scratch register
|
|
727 0xe7, 0x44, // L1: add pc, ip ; transfer control
|
|
728 0xc0, 0x46, // nop ; pad to 4-byte boundary
|
|
729 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
|
|
730 };
|
|
731 uint64_t s = getARMThunkDestVA(destination);
|
|
732 uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
|
|
733 memcpy(buf, data, sizeof(data));
|
|
734 target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
|
|
735 }
|
|
736
|
|
737 void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
|
|
738 addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()),
|
|
739 STT_FUNC, 1, isec);
|
|
740 addSymbol("$t", STT_NOTYPE, 0, isec);
|
|
741 addSymbol("$d", STT_NOTYPE, 12, isec);
|
|
742 }
|
|
743
|
|
744 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
|
|
745 void MipsThunk::writeTo(uint8_t *buf) {
|
|
746 uint64_t s = destination.getVA();
|
|
747 write32(buf, 0x3c190000); // lui $25, %hi(func)
|
|
748 write32(buf + 4, 0x08000000 | (s >> 2)); // j func
|
|
749 write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
|
|
750 write32(buf + 12, 0x00000000); // nop
|
|
751 target->relocateNoSym(buf, R_MIPS_HI16, s);
|
|
752 target->relocateNoSym(buf + 8, R_MIPS_LO16, s);
|
|
753 }
|
|
754
|
|
755 void MipsThunk::addSymbols(ThunkSection &isec) {
|
|
756 addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
|
|
757 isec);
|
|
758 }
|
|
759
|
|
760 InputSection *MipsThunk::getTargetInputSection() const {
|
|
761 auto &dr = cast<Defined>(destination);
|
|
762 return dyn_cast<InputSection>(dr.section);
|
|
763 }
|
|
764
|
|
765 // Write microMIPS R2-R5 LA25 thunk code
|
|
766 // to call PIC function from the non-PIC one.
|
|
767 void MicroMipsThunk::writeTo(uint8_t *buf) {
|
|
768 uint64_t s = destination.getVA();
|
|
769 write16(buf, 0x41b9); // lui $25, %hi(func)
|
|
770 write16(buf + 4, 0xd400); // j func
|
|
771 write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func)
|
|
772 write16(buf + 12, 0x0c00); // nop
|
|
773 target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
|
|
774 target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s);
|
|
775 target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s);
|
|
776 }
|
|
777
|
|
778 void MicroMipsThunk::addSymbols(ThunkSection &isec) {
|
|
779 Defined *d = addSymbol(
|
|
780 saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
|
|
781 d->stOther |= STO_MIPS_MICROMIPS;
|
|
782 }
|
|
783
|
|
784 InputSection *MicroMipsThunk::getTargetInputSection() const {
|
|
785 auto &dr = cast<Defined>(destination);
|
|
786 return dyn_cast<InputSection>(dr.section);
|
|
787 }
|
|
788
|
|
789 // Write microMIPS R6 LA25 thunk code
|
|
790 // to call PIC function from the non-PIC one.
|
|
791 void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
|
|
792 uint64_t s = destination.getVA();
|
|
793 uint64_t p = getThunkTargetSym()->getVA();
|
|
794 write16(buf, 0x1320); // lui $25, %hi(func)
|
|
795 write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func)
|
|
796 write16(buf + 8, 0x9400); // bc func
|
|
797 target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
|
|
798 target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s);
|
|
799 target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
|
|
800 }
|
|
801
|
|
802 void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
|
|
803 Defined *d = addSymbol(
|
|
804 saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
|
|
805 d->stOther |= STO_MIPS_MICROMIPS;
|
|
806 }
|
|
807
|
|
808 InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
|
|
809 auto &dr = cast<Defined>(destination);
|
|
810 return dyn_cast<InputSection>(dr.section);
|
|
811 }
|
|
812
|
173
|
813 void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
|
|
814 const InputFile *file, int64_t addend) {
|
150
|
815 if (!config->isPic) {
|
|
816 write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
|
|
817 write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
|
|
818 write32(buf + 8, 0x7d6903a6); // mtctr r11
|
|
819 write32(buf + 12, 0x4e800420); // bctr
|
|
820 return;
|
|
821 }
|
|
822 uint32_t offset;
|
|
823 if (addend >= 0x8000) {
|
|
824 // The stub loads an address relative to r30 (.got2+Addend). Addend is
|
|
825 // almost always 0x8000. The address of .got2 is different in another object
|
|
826 // file, so a stub cannot be shared.
|
|
827 offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() +
|
|
828 file->ppc32Got2OutSecOff + addend);
|
|
829 } else {
|
|
830 // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
|
|
831 // currently the address of .got).
|
|
832 offset = gotPltVA - in.got->getVA();
|
|
833 }
|
|
834 uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
|
|
835 if (ha == 0) {
|
|
836 write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30)
|
|
837 write32(buf + 4, 0x7d6903a6); // mtctr r11
|
|
838 write32(buf + 8, 0x4e800420); // bctr
|
|
839 write32(buf + 12, 0x60000000); // nop
|
|
840 } else {
|
|
841 write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha
|
|
842 write32(buf + 4, 0x816b0000 | l); // lwz r11,l(r11)
|
|
843 write32(buf + 8, 0x7d6903a6); // mtctr r11
|
|
844 write32(buf + 12, 0x4e800420); // bctr
|
|
845 }
|
|
846 }
|
|
847
|
|
848 void PPC32PltCallStub::writeTo(uint8_t *buf) {
|
|
849 writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend);
|
|
850 }
|
|
851
|
|
852 void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
|
|
853 std::string buf;
|
|
854 raw_string_ostream os(buf);
|
|
855 os << format_hex_no_prefix(addend, 8);
|
|
856 if (!config->isPic)
|
|
857 os << ".plt_call32.";
|
|
858 else if (addend >= 0x8000)
|
|
859 os << ".got2.plt_pic32.";
|
|
860 else
|
|
861 os << ".plt_pic32.";
|
|
862 os << destination.getName();
|
|
863 addSymbol(saver.save(os.str()), STT_FUNC, 0, isec);
|
|
864 }
|
|
865
|
|
866 bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
|
|
867 const Relocation &rel) const {
|
|
868 return !config->isPic || (isec.file == file && rel.addend == addend);
|
|
869 }
|
|
870
|
|
871 void PPC32LongThunk::addSymbols(ThunkSection &isec) {
|
|
872 addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0,
|
|
873 isec);
|
|
874 }
|
|
875
|
|
876 void PPC32LongThunk::writeTo(uint8_t *buf) {
|
|
877 auto ha = [](uint32_t v) -> uint16_t { return (v + 0x8000) >> 16; };
|
|
878 auto lo = [](uint32_t v) -> uint16_t { return v; };
|
|
879 uint32_t d = destination.getVA(addend);
|
|
880 if (config->isPic) {
|
|
881 uint32_t off = d - (getThunkTargetSym()->getVA() + 8);
|
|
882 write32(buf + 0, 0x7c0802a6); // mflr r12,0
|
|
883 write32(buf + 4, 0x429f0005); // bcl r20,r31,.+4
|
|
884 write32(buf + 8, 0x7d8802a6); // mtctr r12
|
|
885 write32(buf + 12, 0x3d8c0000 | ha(off)); // addis r12,r12,off@ha
|
|
886 write32(buf + 16, 0x398c0000 | lo(off)); // addi r12,r12,off@l
|
|
887 write32(buf + 20, 0x7c0803a6); // mtlr r0
|
|
888 buf += 24;
|
|
889 } else {
|
|
890 write32(buf + 0, 0x3d800000 | ha(d)); // lis r12,d@ha
|
|
891 write32(buf + 4, 0x398c0000 | lo(d)); // addi r12,r12,d@l
|
|
892 buf += 8;
|
|
893 }
|
|
894 write32(buf + 0, 0x7d8903a6); // mtctr r12
|
|
895 write32(buf + 4, 0x4e800420); // bctr
|
|
896 }
|
|
897
|
173
|
898 void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
|
150
|
899 uint16_t offHa = (offset + 0x8000) >> 16;
|
|
900 uint16_t offLo = offset & 0xffff;
|
|
901
|
|
902 write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
|
|
903 write32(buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12)
|
|
904 write32(buf + 8, 0x7d8903a6); // mtctr r12
|
|
905 write32(buf + 12, 0x4e800420); // bctr
|
|
906 }
|
|
907
|
|
908 void PPC64PltCallStub::writeTo(uint8_t *buf) {
|
|
909 int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
|
|
910 // Save the TOC pointer to the save-slot reserved in the call frame.
|
|
911 write32(buf + 0, 0xf8410018); // std r2,24(r1)
|
|
912 writePPC64LoadAndBranch(buf + 4, offset);
|
|
913 }
|
|
914
|
|
915 void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
|
|
916 Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC,
|
|
917 0, isec);
|
|
918 s->needsTocRestore = true;
|
|
919 s->file = destination.file;
|
|
920 }
|
|
921
|
207
|
922 bool PPC64PltCallStub::isCompatibleWith(const InputSection &isec,
|
|
923 const Relocation &rel) const {
|
|
924 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
|
|
925 }
|
|
926
|
|
927 void PPC64R2SaveStub::writeTo(uint8_t *buf) {
|
|
928 const int64_t offset = computeOffset();
|
|
929 write32(buf + 0, 0xf8410018); // std r2,24(r1)
|
|
930 // The branch offset needs to fit in 26 bits.
|
|
931 if (getMayUseShortThunk()) {
|
|
932 write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset>
|
|
933 } else if (isInt<34>(offset)) {
|
|
934 int nextInstOffset;
|
|
935 if (!config->Power10Stub) {
|
|
936 uint64_t tocOffset = destination.getVA() - getPPC64TocBase();
|
|
937 if (tocOffset >> 16 > 0) {
|
|
938 const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff);
|
|
939 const uint64_t addis = ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff);
|
|
940 write32(buf + 4, addis); // addis r12, r2 , top of offset
|
|
941 write32(buf + 8, addi); // addi r12, r12, bottom of offset
|
|
942 nextInstOffset = 12;
|
|
943 } else {
|
|
944 const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff);
|
|
945 write32(buf + 4, addi); // addi r12, r2, offset
|
|
946 nextInstOffset = 8;
|
|
947 }
|
|
948 } else {
|
|
949 const uint64_t paddi = PADDI_R12_NO_DISP |
|
|
950 (((offset >> 16) & 0x3ffff) << 32) |
|
|
951 (offset & 0xffff);
|
|
952 writePrefixedInstruction(buf + 4, paddi); // paddi r12, 0, func@pcrel, 1
|
|
953 nextInstOffset = 12;
|
|
954 }
|
|
955 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
|
|
956 write32(buf + nextInstOffset + 4, BCTR); // bctr
|
|
957 } else {
|
|
958 in.ppc64LongBranchTarget->addEntry(&destination, addend);
|
|
959 const int64_t offsetFromTOC =
|
|
960 in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
|
|
961 getPPC64TocBase();
|
|
962 writePPC64LoadAndBranch(buf + 4, offsetFromTOC);
|
|
963 }
|
|
964 }
|
|
965
|
|
966 void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
|
|
967 Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()),
|
|
968 STT_FUNC, 0, isec);
|
|
969 s->needsTocRestore = true;
|
|
970 }
|
|
971
|
|
972 bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec,
|
|
973 const Relocation &rel) const {
|
|
974 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
|
|
975 }
|
|
976
|
|
977 void PPC64R12SetupStub::writeTo(uint8_t *buf) {
|
|
978 int64_t offset = destination.getVA() - getThunkTargetSym()->getVA();
|
|
979 if (!isInt<34>(offset))
|
|
980 reportRangeError(buf, offset, 34, destination, "R12 setup stub offset");
|
|
981
|
|
982 int nextInstOffset;
|
|
983 if (!config->Power10Stub) {
|
|
984 uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
|
|
985 write32(buf + 0, 0x7c0802a6); // mflr r12
|
|
986 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4
|
|
987 write32(buf + 8, 0x7d6802a6); // mflr r11
|
|
988 write32(buf + 12, 0x7d8803a6); // mtlr r12
|
|
989 write32(buf + 16, 0x3d8b0000 | computeHiBits(off));// addis r12,r11,off@ha
|
|
990 write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l
|
|
991 nextInstOffset = 24;
|
|
992 } else {
|
|
993 uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
|
|
994 (offset & 0xffff);
|
|
995 writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1
|
|
996 nextInstOffset = 8;
|
|
997 }
|
|
998 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
|
|
999 write32(buf + nextInstOffset + 4, BCTR); // bctr
|
|
1000 }
|
|
1001
|
|
1002 void PPC64R12SetupStub::addSymbols(ThunkSection &isec) {
|
|
1003 addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0,
|
|
1004 isec);
|
|
1005 }
|
|
1006
|
|
1007 bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec,
|
|
1008 const Relocation &rel) const {
|
|
1009 return rel.type == R_PPC64_REL24_NOTOC;
|
|
1010 }
|
|
1011
|
|
1012 void PPC64PCRelPLTStub::writeTo(uint8_t *buf) {
|
|
1013 int nextInstOffset = 0;
|
|
1014 int64_t offset = destination.getGotPltVA() - getThunkTargetSym()->getVA();
|
|
1015
|
|
1016 if (config->Power10Stub) {
|
|
1017 if (!isInt<34>(offset))
|
|
1018 reportRangeError(buf, offset, 34, destination,
|
|
1019 "PC-relative PLT stub offset");
|
|
1020 const uint64_t pld = PLD_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
|
|
1021 (offset & 0xffff);
|
|
1022 writePrefixedInstruction(buf + 0, pld); // pld r12, func@plt@pcrel
|
|
1023 nextInstOffset = 8;
|
|
1024 } else {
|
|
1025 uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
|
|
1026 write32(buf + 0, 0x7c0802a6); // mflr r12
|
|
1027 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4
|
|
1028 write32(buf + 8, 0x7d6802a6); // mflr r11
|
|
1029 write32(buf + 12, 0x7d8803a6); // mtlr r12
|
|
1030 write32(buf + 16, 0x3d8b0000 | computeHiBits(off)); // addis r12,r11,off@ha
|
|
1031 write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l
|
|
1032 nextInstOffset = 24;
|
|
1033 }
|
|
1034 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
|
|
1035 write32(buf + nextInstOffset + 4, BCTR); // bctr
|
|
1036 }
|
|
1037
|
|
1038 void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) {
|
|
1039 addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0,
|
|
1040 isec);
|
|
1041 }
|
|
1042
|
|
1043 bool PPC64PCRelPLTStub::isCompatibleWith(const InputSection &isec,
|
|
1044 const Relocation &rel) const {
|
|
1045 return rel.type == R_PPC64_REL24_NOTOC;
|
|
1046 }
|
|
1047
|
150
|
1048 void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
|
|
1049 int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
|
|
1050 getPPC64TocBase();
|
|
1051 writePPC64LoadAndBranch(buf, offset);
|
|
1052 }
|
|
1053
|
|
1054 void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
|
|
1055 addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0,
|
|
1056 isec);
|
|
1057 }
|
|
1058
|
207
|
1059 bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
|
|
1060 const Relocation &rel) const {
|
|
1061 return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
|
|
1062 }
|
|
1063
|
|
1064 void PPC64PCRelLongBranchThunk::writeTo(uint8_t *buf) {
|
|
1065 int64_t offset = destination.getVA() - getThunkTargetSym()->getVA();
|
|
1066 if (!isInt<34>(offset))
|
|
1067 reportRangeError(buf, offset, 34, destination,
|
|
1068 "PC-relative long branch stub offset");
|
|
1069
|
|
1070 int nextInstOffset;
|
|
1071 if (!config->Power10Stub) {
|
|
1072 uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
|
|
1073 write32(buf + 0, 0x7c0802a6); // mflr r12
|
|
1074 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4
|
|
1075 write32(buf + 8, 0x7d6802a6); // mflr r11
|
|
1076 write32(buf + 12, 0x7d8803a6); // mtlr r12
|
|
1077 write32(buf + 16, 0x3d8b0000 | computeHiBits(off)); // addis r12,r11,off@ha
|
|
1078 write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l
|
|
1079 nextInstOffset = 24;
|
|
1080 } else {
|
|
1081 uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
|
|
1082 (offset & 0xffff);
|
|
1083 writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1
|
|
1084 nextInstOffset = 8;
|
|
1085 }
|
|
1086 write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
|
|
1087 write32(buf + nextInstOffset + 4, BCTR); // bctr
|
|
1088 }
|
|
1089
|
|
1090 void PPC64PCRelLongBranchThunk::addSymbols(ThunkSection &isec) {
|
|
1091 addSymbol(saver.save("__long_branch_pcrel_" + destination.getName()),
|
|
1092 STT_FUNC, 0, isec);
|
|
1093 }
|
|
1094
|
|
1095 bool PPC64PCRelLongBranchThunk::isCompatibleWith(const InputSection &isec,
|
|
1096 const Relocation &rel) const {
|
|
1097 return rel.type == R_PPC64_REL24_NOTOC;
|
|
1098 }
|
|
1099
|
150
|
1100 Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {}
|
|
1101
|
|
1102 Thunk::~Thunk() = default;
|
|
1103
|
|
1104 static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
|
207
|
1105 if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
|
|
1106 type != R_AARCH64_PLT32)
|
150
|
1107 fatal("unrecognized relocation type");
|
|
1108 if (config->picThunk)
|
|
1109 return make<AArch64ADRPThunk>(s, a);
|
|
1110 return make<AArch64ABSLongThunk>(s, a);
|
|
1111 }
|
|
1112
|
|
1113 // Creates a thunk for Thumb-ARM interworking.
|
|
1114 // Arm Architectures v5 and v6 do not support Thumb2 technology. This means
|
|
1115 // - MOVT and MOVW instructions cannot be used
|
|
1116 // - Only Thumb relocation that can generate a Thunk is a BL, this can always
|
|
1117 // be transformed into a BLX
|
207
|
1118 static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) {
|
150
|
1119 switch (reloc) {
|
|
1120 case R_ARM_PC24:
|
|
1121 case R_ARM_PLT32:
|
|
1122 case R_ARM_JUMP24:
|
|
1123 case R_ARM_CALL:
|
|
1124 case R_ARM_THM_CALL:
|
|
1125 if (config->picThunk)
|
207
|
1126 return make<ARMV5PILongThunk>(s, a);
|
|
1127 return make<ARMV5ABSLongThunk>(s, a);
|
150
|
1128 }
|
|
1129 fatal("relocation " + toString(reloc) + " to " + toString(s) +
|
|
1130 " not supported for Armv5 or Armv6 targets");
|
|
1131 }
|
|
1132
|
|
1133 // Create a thunk for Thumb long branch on V6-M.
|
|
1134 // Arm Architecture v6-M only supports Thumb instructions. This means
|
|
1135 // - MOVT and MOVW instructions cannot be used.
|
|
1136 // - Only a limited number of instructions can access registers r8 and above
|
|
1137 // - No interworking support is needed (all Thumb).
|
207
|
1138 static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) {
|
150
|
1139 switch (reloc) {
|
|
1140 case R_ARM_THM_JUMP19:
|
|
1141 case R_ARM_THM_JUMP24:
|
|
1142 case R_ARM_THM_CALL:
|
|
1143 if (config->isPic)
|
207
|
1144 return make<ThumbV6MPILongThunk>(s, a);
|
|
1145 return make<ThumbV6MABSLongThunk>(s, a);
|
150
|
1146 }
|
|
1147 fatal("relocation " + toString(reloc) + " to " + toString(s) +
|
|
1148 " not supported for Armv6-M targets");
|
|
1149 }
|
|
1150
|
|
1151 // Creates a thunk for Thumb-ARM interworking or branch range extension.
|
207
|
1152 static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) {
|
150
|
1153 // Decide which Thunk is needed based on:
|
|
1154 // Available instruction set
|
|
1155 // - An Arm Thunk can only be used if Arm state is available.
|
|
1156 // - A Thumb Thunk can only be used if Thumb state is available.
|
|
1157 // - Can only use a Thunk if it uses instructions that the Target supports.
|
|
1158 // Relocation is branch or branch and link
|
|
1159 // - Branch instructions cannot change state, can only select Thunk that
|
|
1160 // starts in the same state as the caller.
|
|
1161 // - Branch and link relocations can change state, can select Thunks from
|
|
1162 // either Arm or Thumb.
|
|
1163 // Position independent Thunks if we require position independent code.
|
|
1164
|
|
1165 // Handle architectures that have restrictions on the instructions that they
|
|
1166 // can use in Thunks. The flags below are set by reading the BuildAttributes
|
|
1167 // of the input objects. InputFiles.cpp contains the mapping from ARM
|
|
1168 // architecture to flag.
|
|
1169 if (!config->armHasMovtMovw) {
|
|
1170 if (!config->armJ1J2BranchEncoding)
|
207
|
1171 return addThunkPreArmv7(reloc, s, a);
|
|
1172 return addThunkV6M(reloc, s, a);
|
150
|
1173 }
|
|
1174
|
|
1175 switch (reloc) {
|
|
1176 case R_ARM_PC24:
|
|
1177 case R_ARM_PLT32:
|
|
1178 case R_ARM_JUMP24:
|
|
1179 case R_ARM_CALL:
|
|
1180 if (config->picThunk)
|
207
|
1181 return make<ARMV7PILongThunk>(s, a);
|
|
1182 return make<ARMV7ABSLongThunk>(s, a);
|
150
|
1183 case R_ARM_THM_JUMP19:
|
|
1184 case R_ARM_THM_JUMP24:
|
|
1185 case R_ARM_THM_CALL:
|
|
1186 if (config->picThunk)
|
207
|
1187 return make<ThumbV7PILongThunk>(s, a);
|
|
1188 return make<ThumbV7ABSLongThunk>(s, a);
|
150
|
1189 }
|
|
1190 fatal("unrecognized relocation type");
|
|
1191 }
|
|
1192
|
|
1193 static Thunk *addThunkMips(RelType type, Symbol &s) {
|
|
1194 if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
|
|
1195 return make<MicroMipsR6Thunk>(s);
|
|
1196 if (s.stOther & STO_MIPS_MICROMIPS)
|
|
1197 return make<MicroMipsThunk>(s);
|
|
1198 return make<MipsThunk>(s);
|
|
1199 }
|
|
1200
|
|
1201 static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel,
|
|
1202 Symbol &s) {
|
|
1203 assert((rel.type == R_PPC_LOCAL24PC || rel.type == R_PPC_REL24 ||
|
|
1204 rel.type == R_PPC_PLTREL24) &&
|
|
1205 "unexpected relocation type for thunk");
|
|
1206 if (s.isInPlt())
|
|
1207 return make<PPC32PltCallStub>(isec, rel, s);
|
|
1208 return make<PPC32LongThunk>(s, rel.addend);
|
|
1209 }
|
|
1210
|
|
1211 static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
|
207
|
1212 assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 ||
|
|
1213 type == R_PPC64_REL24_NOTOC) &&
|
173
|
1214 "unexpected relocation type for thunk");
|
150
|
1215 if (s.isInPlt())
|
207
|
1216 return type == R_PPC64_REL24_NOTOC ? (Thunk *)make<PPC64PCRelPLTStub>(s)
|
|
1217 : (Thunk *)make<PPC64PltCallStub>(s);
|
|
1218
|
|
1219 // This check looks at the st_other bits of the callee. If the value is 1
|
|
1220 // then the callee clobbers the TOC and we need an R2 save stub when RelType
|
|
1221 // is R_PPC64_REL14 or R_PPC64_REL24.
|
|
1222 if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1)
|
|
1223 return make<PPC64R2SaveStub>(s, a);
|
|
1224
|
|
1225 if (type == R_PPC64_REL24_NOTOC)
|
|
1226 return (s.stOther >> 5) > 1
|
|
1227 ? (Thunk *)make<PPC64R12SetupStub>(s)
|
|
1228 : (Thunk *)make<PPC64PCRelLongBranchThunk>(s, a);
|
150
|
1229
|
|
1230 if (config->picThunk)
|
|
1231 return make<PPC64PILongBranchThunk>(s, a);
|
|
1232
|
|
1233 return make<PPC64PDLongBranchThunk>(s, a);
|
|
1234 }
|
|
1235
|
173
|
1236 Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
|
150
|
1237 Symbol &s = *rel.sym;
|
|
1238 int64_t a = rel.addend;
|
|
1239
|
|
1240 if (config->emachine == EM_AARCH64)
|
|
1241 return addThunkAArch64(rel.type, s, a);
|
|
1242
|
|
1243 if (config->emachine == EM_ARM)
|
207
|
1244 return addThunkArm(rel.type, s, a);
|
150
|
1245
|
|
1246 if (config->emachine == EM_MIPS)
|
|
1247 return addThunkMips(rel.type, s);
|
|
1248
|
|
1249 if (config->emachine == EM_PPC)
|
|
1250 return addThunkPPC32(isec, rel, s);
|
|
1251
|
|
1252 if (config->emachine == EM_PPC64)
|
|
1253 return addThunkPPC64(rel.type, s, a);
|
|
1254
|
|
1255 llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
|
|
1256 }
|