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