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