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 = addSyntheticLocal(ctx, name, type, value, /*size=*/0, section); |
605 | syms.push_back(Elt: d); |
606 | return d; |
607 | } |
608 | |
609 | void Thunk::setOffset(uint64_t newOffset) { |
610 | for (Defined *d : syms) |
611 | d->value = d->value - offset + newOffset; |
612 | offset = newOffset; |
613 | } |
614 | |
615 | // AArch64 Thunk base class. |
616 | static uint64_t getAArch64ThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) { |
617 | uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, addend: a); |
618 | return v; |
619 | } |
620 | |
621 | bool AArch64Thunk::getMayUseShortThunk() { |
622 | if (!mayUseShortThunk) |
623 | return false; |
624 | uint64_t s = getAArch64ThunkDestVA(ctx, s: destination, a: addend); |
625 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
626 | mayUseShortThunk = llvm::isInt<28>(x: s - p); |
627 | if (!mayUseShortThunk) |
628 | addLongMapSyms(); |
629 | return mayUseShortThunk; |
630 | } |
631 | |
632 | void AArch64Thunk::writeTo(uint8_t *buf) { |
633 | if (!getMayUseShortThunk()) { |
634 | writeLong(buf); |
635 | return; |
636 | } |
637 | uint64_t s = getAArch64ThunkDestVA(ctx, s: destination, a: addend); |
638 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
639 | write32(ctx, p: buf, v: 0x14000000); // b S |
640 | ctx.target->relocateNoSym(loc: buf, type: R_AARCH64_CALL26, val: s - p); |
641 | } |
642 | |
643 | bool AArch64Thunk::needsSyntheticLandingPad() { |
644 | // Short Thunks use a direct branch, no synthetic landing pad |
645 | // required. |
646 | return mayNeedLandingPad && !getMayUseShortThunk(); |
647 | } |
648 | |
649 | // AArch64 long range Thunks. |
650 | void AArch64ABSLongThunk::writeLong(uint8_t *buf) { |
651 | const uint8_t data[] = { |
652 | 0x50, 0x00, 0x00, 0x58, // ldr x16, L0 |
653 | 0x00, 0x02, 0x1f, 0xd6, // br x16 |
654 | 0x00, 0x00, 0x00, 0x00, // L0: .xword S |
655 | 0x00, 0x00, 0x00, 0x00, |
656 | }; |
657 | // If mayNeedLandingPad is true then destination is an |
658 | // AArch64BTILandingPadThunk that defines landingPad. |
659 | assert(!mayNeedLandingPad || landingPad != nullptr); |
660 | uint64_t s = mayNeedLandingPad |
661 | ? landingPad->getVA(ctx, addend: 0) |
662 | : getAArch64ThunkDestVA(ctx, s: destination, a: addend); |
663 | memcpy(dest: buf, src: data, n: sizeof(data)); |
664 | ctx.target->relocateNoSym(loc: buf + 8, type: R_AARCH64_ABS64, val: s); |
665 | } |
666 | |
667 | void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { |
668 | addSymbol(name: ctx.saver.save(S: "__AArch64AbsLongThunk_"+ destination.getName()), |
669 | type: STT_FUNC, value: 0, section&: isec); |
670 | addSymbol(name: "$x", type: STT_NOTYPE, value: 0, section&: isec); |
671 | tsec = &isec; |
672 | (void)getMayUseShortThunk(); |
673 | } |
674 | |
675 | void AArch64ABSLongThunk::addLongMapSyms() { |
676 | addSymbol(name: "$d", type: STT_NOTYPE, value: 8, section&: *tsec); |
677 | // The ldr in the long Thunk requires 8-byte alignment when |
678 | // unaligned accesses are disabled. |
679 | alignment = 8; |
680 | } |
681 | |
682 | void AArch64ABSXOLongThunk::writeLong(uint8_t *buf) { |
683 | const uint8_t data[] = { |
684 | 0x10, 0x00, 0x80, 0xd2, // movz x16, :abs_g0_nc:S, lsl #0 |
685 | 0x10, 0x00, 0xa0, 0xf2, // movk x16, :abs_g1_nc:S, lsl #16 |
686 | 0x10, 0x00, 0xc0, 0xf2, // movk x16, :abs_g2_nc:S, lsl #32 |
687 | 0x10, 0x00, 0xe0, 0xf2, // movk x16, :abs_g3:S, lsl #48 |
688 | 0x00, 0x02, 0x1f, 0xd6, // br x16 |
689 | }; |
690 | // If mayNeedLandingPad is true then destination is an |
691 | // AArch64BTILandingPadThunk that defines landingPad. |
692 | assert(!mayNeedLandingPad || landingPad != nullptr); |
693 | uint64_t s = mayNeedLandingPad |
694 | ? landingPad->getVA(ctx, addend: 0) |
695 | : getAArch64ThunkDestVA(ctx, s: destination, a: addend); |
696 | memcpy(dest: buf, src: data, n: sizeof(data)); |
697 | ctx.target->relocateNoSym(loc: buf + 0, type: R_AARCH64_MOVW_UABS_G0_NC, val: s); |
698 | ctx.target->relocateNoSym(loc: buf + 4, type: R_AARCH64_MOVW_UABS_G1_NC, val: s); |
699 | ctx.target->relocateNoSym(loc: buf + 8, type: R_AARCH64_MOVW_UABS_G2_NC, val: s); |
700 | ctx.target->relocateNoSym(loc: buf + 12, type: R_AARCH64_MOVW_UABS_G3, val: s); |
701 | } |
702 | |
703 | void AArch64ABSXOLongThunk::addSymbols(ThunkSection &sec) { |
704 | addSymbol(name: ctx.saver.save(S: "__AArch64AbsXOLongThunk_"+ destination.getName()), |
705 | type: STT_FUNC, value: 0, section&: sec); |
706 | addSymbol(name: "$x", type: STT_NOTYPE, value: 0, section&: sec); |
707 | } |
708 | |
709 | // This Thunk has a maximum range of 4Gb, this is sufficient for all programs |
710 | // using the small code model, including pc-relative ones. At time of writing |
711 | // clang and gcc do not support the large code model for position independent |
712 | // code so it is safe to use this for position independent thunks without |
713 | // worrying about the destination being more than 4Gb away. |
714 | void AArch64ADRPThunk::writeLong(uint8_t *buf) { |
715 | const uint8_t data[] = { |
716 | 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest) |
717 | 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest) |
718 | 0x00, 0x02, 0x1f, 0xd6, // br x16 |
719 | }; |
720 | // if mayNeedLandingPad is true then destination is an |
721 | // AArch64BTILandingPadThunk that defines landingPad. |
722 | assert(!mayNeedLandingPad || landingPad != nullptr); |
723 | uint64_t s = mayNeedLandingPad |
724 | ? landingPad->getVA(ctx, addend: 0) |
725 | : getAArch64ThunkDestVA(ctx, s: destination, a: addend); |
726 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
727 | memcpy(dest: buf, src: data, n: sizeof(data)); |
728 | ctx.target->relocateNoSym(loc: buf, type: R_AARCH64_ADR_PREL_PG_HI21, |
729 | val: getAArch64Page(expr: s) - getAArch64Page(expr: p)); |
730 | ctx.target->relocateNoSym(loc: buf + 4, type: R_AARCH64_ADD_ABS_LO12_NC, val: s); |
731 | } |
732 | |
733 | void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { |
734 | addSymbol(name: ctx.saver.save(S: "__AArch64ADRPThunk_"+ destination.getName()), |
735 | type: STT_FUNC, value: 0, section&: isec); |
736 | addSymbol(name: "$x", type: STT_NOTYPE, value: 0, section&: isec); |
737 | } |
738 | |
739 | void AArch64BTILandingPadThunk::addSymbols(ThunkSection &isec) { |
740 | addSymbol(name: ctx.saver.save(S: "__AArch64BTIThunk_"+ destination.getName()), |
741 | type: STT_FUNC, value: 0, section&: isec); |
742 | addSymbol(name: "$x", type: STT_NOTYPE, value: 0, section&: isec); |
743 | } |
744 | |
745 | void AArch64BTILandingPadThunk::writeTo(uint8_t *buf) { |
746 | if (!getMayUseShortThunk()) { |
747 | writeLong(buf); |
748 | return; |
749 | } |
750 | write32(ctx, p: buf, v: 0xd503245f); // BTI c |
751 | // Control falls through to target in following section. |
752 | } |
753 | |
754 | bool AArch64BTILandingPadThunk::getMayUseShortThunk() { |
755 | if (!mayUseShortThunk) |
756 | return false; |
757 | // If the target is the following instruction then we can fall |
758 | // through without the indirect branch. |
759 | uint64_t s = destination.getVA(ctx, addend); |
760 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
761 | // This function is called before addresses are stable. We need to |
762 | // work out the range from the thunk to the next section but the |
763 | // address of the start of the next section depends on the size of |
764 | // the thunks in the previous pass. s - p + offset == 0 represents |
765 | // the first pass where the Thunk and following section are assigned |
766 | // the same offset. s - p <= 4 is the last Thunk in the Thunk |
767 | // Section. |
768 | mayUseShortThunk = (s - p + offset == 0 || s - p <= 4); |
769 | return mayUseShortThunk; |
770 | } |
771 | |
772 | void AArch64BTILandingPadThunk::writeLong(uint8_t *buf) { |
773 | uint64_t s = destination.getVA(ctx, addend); |
774 | uint64_t p = getThunkTargetSym()->getVA(ctx) + 4; |
775 | write32(ctx, p: buf, v: 0xd503245f); // BTI c |
776 | write32(ctx, p: buf + 4, v: 0x14000000); // B S |
777 | ctx.target->relocateNoSym(loc: buf + 4, type: R_AARCH64_CALL26, val: s - p); |
778 | } |
779 | |
780 | // ARM Target Thunks |
781 | static uint64_t getARMThunkDestVA(Ctx &ctx, const Symbol &s) { |
782 | uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx); |
783 | return SignExtend64<32>(x: v); |
784 | } |
785 | |
786 | // This function returns true if the target is not Thumb and is within 2^26, and |
787 | // it has not previously returned false (see comment for mayUseShortThunk). |
788 | bool ARMThunk::getMayUseShortThunk() { |
789 | if (!mayUseShortThunk) |
790 | return false; |
791 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
792 | if (s & 1) { |
793 | mayUseShortThunk = false; |
794 | addLongMapSyms(); |
795 | return false; |
796 | } |
797 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
798 | int64_t offset = s - p - 8; |
799 | mayUseShortThunk = llvm::isInt<26>(x: offset); |
800 | if (!mayUseShortThunk) |
801 | addLongMapSyms(); |
802 | return mayUseShortThunk; |
803 | } |
804 | |
805 | void ARMThunk::writeTo(uint8_t *buf) { |
806 | if (!getMayUseShortThunk()) { |
807 | writeLong(buf); |
808 | return; |
809 | } |
810 | |
811 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
812 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
813 | int64_t offset = s - p - 8; |
814 | write32(ctx, p: buf, v: 0xea000000); // b S |
815 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_JUMP24, val: offset); |
816 | } |
817 | |
818 | bool ARMThunk::isCompatibleWith(const InputSection &isec, |
819 | const Relocation &rel) const { |
820 | // v4T does not have BLX, so also deny R_ARM_THM_CALL |
821 | if (!ctx.arg.armHasBlx && rel.type == R_ARM_THM_CALL) |
822 | return false; |
823 | |
824 | // Thumb branch relocations can't use BLX |
825 | return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; |
826 | } |
827 | |
828 | // This function returns true if: |
829 | // the target is Thumb |
830 | // && is within branch range |
831 | // && this function has not previously returned false |
832 | // (see comment for mayUseShortThunk) |
833 | // && the arch supports Thumb branch range extension. |
834 | bool ThumbThunk::getMayUseShortThunk() { |
835 | if (!mayUseShortThunk) |
836 | return false; |
837 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
838 | // To use a short thunk the destination must be Thumb and the target must |
839 | // have the wide branch instruction B.w. This instruction is included when |
840 | // Thumb 2 is present, or in v8-M (and above) baseline architectures. |
841 | // armJ1J2BranchEncoding is available in all architectures with a profile and |
842 | // the one v6 CPU that implements Thumb 2 (Arm1156t2-s). |
843 | // Movt and Movw instructions require Thumb 2 or v8-M baseline. |
844 | if ((s & 1) == 0 || !ctx.arg.armJ1J2BranchEncoding || |
845 | !ctx.arg.armHasMovtMovw) { |
846 | mayUseShortThunk = false; |
847 | addLongMapSyms(); |
848 | return false; |
849 | } |
850 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~1; |
851 | int64_t offset = s - p - 4; |
852 | mayUseShortThunk = llvm::isInt<25>(x: offset); |
853 | if (!mayUseShortThunk) |
854 | addLongMapSyms(); |
855 | return mayUseShortThunk; |
856 | } |
857 | |
858 | void ThumbThunk::writeTo(uint8_t *buf) { |
859 | if (!getMayUseShortThunk()) { |
860 | writeLong(buf); |
861 | return; |
862 | } |
863 | |
864 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
865 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
866 | int64_t offset = s - p - 4; |
867 | write16(ctx, p: buf + 0, v: 0xf000); // b.w S |
868 | write16(ctx, p: buf + 2, v: 0xb000); |
869 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_THM_JUMP24, val: offset); |
870 | } |
871 | |
872 | bool ThumbThunk::isCompatibleWith(const InputSection &isec, |
873 | const Relocation &rel) const { |
874 | // v4T does not have BLX, so also deny R_ARM_CALL |
875 | if (!ctx.arg.armHasBlx && rel.type == R_ARM_CALL) |
876 | return false; |
877 | |
878 | // ARM branch relocations can't use BLX |
879 | return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32; |
880 | } |
881 | |
882 | void ARMV7ABSLongThunk::writeLong(uint8_t *buf) { |
883 | write32(ctx, p: buf + 0, v: 0xe300c000); // movw ip,:lower16:S |
884 | write32(ctx, p: buf + 4, v: 0xe340c000); // movt ip,:upper16:S |
885 | write32(ctx, p: buf + 8, v: 0xe12fff1c); // bx ip |
886 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
887 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_MOVW_ABS_NC, val: s); |
888 | ctx.target->relocateNoSym(loc: buf + 4, type: R_ARM_MOVT_ABS, val: s); |
889 | } |
890 | |
891 | void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { |
892 | addSymbol(name: ctx.saver.save(S: "__ARMv7ABSLongThunk_"+ destination.getName()), |
893 | type: STT_FUNC, value: 0, section&: isec); |
894 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
895 | } |
896 | |
897 | void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) { |
898 | write16(ctx, p: buf + 0, v: 0xf240); // movw ip, :lower16:S |
899 | write16(ctx, p: buf + 2, v: 0x0c00); |
900 | write16(ctx, p: buf + 4, v: 0xf2c0); // movt ip, :upper16:S |
901 | write16(ctx, p: buf + 6, v: 0x0c00); |
902 | write16(ctx, p: buf + 8, v: 0x4760); // bx ip |
903 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
904 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_THM_MOVW_ABS_NC, val: s); |
905 | ctx.target->relocateNoSym(loc: buf + 4, type: R_ARM_THM_MOVT_ABS, val: s); |
906 | } |
907 | |
908 | void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { |
909 | addSymbol(name: ctx.saver.save(S: "__Thumbv7ABSLongThunk_"+ destination.getName()), |
910 | type: STT_FUNC, value: 1, section&: isec); |
911 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
912 | } |
913 | |
914 | void ARMV7PILongThunk::writeLong(uint8_t *buf) { |
915 | write32(ctx, p: buf + 0, |
916 | v: 0xe30fcff0); // P: movw ip,:lower16:S - (P + (L1-P) + 8) |
917 | write32(ctx, p: buf + 4, |
918 | v: 0xe340c000); // movt ip,:upper16:S - (P + (L1-P) + 8) |
919 | write32(ctx, p: buf + 8, v: 0xe08cc00f); // L1: add ip, ip, pc |
920 | write32(ctx, p: buf + 12, v: 0xe12fff1c); // bx ip |
921 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
922 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
923 | int64_t offset = s - p - 16; |
924 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_MOVW_PREL_NC, val: offset); |
925 | ctx.target->relocateNoSym(loc: buf + 4, type: R_ARM_MOVT_PREL, val: offset); |
926 | } |
927 | |
928 | void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { |
929 | addSymbol(name: ctx.saver.save(S: "__ARMV7PILongThunk_"+ destination.getName()), |
930 | type: STT_FUNC, value: 0, section&: isec); |
931 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
932 | } |
933 | |
934 | void ThumbV7PILongThunk::writeLong(uint8_t *buf) { |
935 | write16(ctx, p: buf + 0, v: 0xf64f); // P: movw ip,:lower16:S - (P + (L1-P) + 4) |
936 | write16(ctx, p: buf + 2, v: 0x7cf4); |
937 | write16(ctx, p: buf + 4, v: 0xf2c0); // movt ip,:upper16:S - (P + (L1-P) + 4) |
938 | write16(ctx, p: buf + 6, v: 0x0c00); |
939 | write16(ctx, p: buf + 8, v: 0x44fc); // L1: add ip, pc |
940 | write16(ctx, p: buf + 10, v: 0x4760); // bx ip |
941 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
942 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
943 | int64_t offset = s - p - 12; |
944 | ctx.target->relocateNoSym(loc: buf, type: R_ARM_THM_MOVW_PREL_NC, val: offset); |
945 | ctx.target->relocateNoSym(loc: buf + 4, type: R_ARM_THM_MOVT_PREL, val: offset); |
946 | } |
947 | |
948 | void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { |
949 | addSymbol(name: ctx.saver.save(S: "__ThumbV7PILongThunk_"+ destination.getName()), |
950 | type: STT_FUNC, value: 1, section&: isec); |
951 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
952 | } |
953 | |
954 | void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { |
955 | // Most Thumb instructions cannot access the high registers r8 - r15. As the |
956 | // only register we can corrupt is r12 we must instead spill a low register |
957 | // to the stack to use as a scratch register. We push r1 even though we |
958 | // don't need to get some space to use for the return address. |
959 | write16(ctx, p: buf + 0, v: 0xb403); // push {r0, r1} ; Obtain scratch registers |
960 | write16(ctx, p: buf + 2, v: 0x4801); // ldr r0, [pc, #4] ; L1 |
961 | write16(ctx, p: buf + 4, v: 0x9001); // str r0, [sp, #4] ; SP + 4 = S |
962 | write16(ctx, p: buf + 6, v: 0xbd01); // pop {r0, pc} ; restore r0 and branch to dest |
963 | write32(ctx, p: buf + 8, v: 0x00000000); // L1: .word S |
964 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
965 | ctx.target->relocateNoSym(loc: buf + 8, type: R_ARM_ABS32, val: s); |
966 | } |
967 | |
968 | void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { |
969 | addSymbol(name: ctx.saver.save(S: "__Thumbv6MABSLongThunk_"+ destination.getName()), |
970 | type: STT_FUNC, value: 1, section&: isec); |
971 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
972 | tsec = &isec; |
973 | (void)getMayUseShortThunk(); |
974 | } |
975 | |
976 | void ThumbV6MABSLongThunk::addLongMapSyms() { |
977 | addSymbol(name: "$d", type: STT_NOTYPE, value: 8, section&: *tsec); |
978 | } |
979 | |
980 | void ThumbV6MABSXOLongThunk::writeLong(uint8_t *buf) { |
981 | // Most Thumb instructions cannot access the high registers r8 - r15. As the |
982 | // only register we can corrupt is r12 we must instead spill a low register |
983 | // to the stack to use as a scratch register. We push r1 even though we |
984 | // don't need to get some space to use for the return address. |
985 | write16(ctx, p: buf + 0, v: 0xb403); // push {r0, r1} ; Obtain scratch registers |
986 | write16(ctx, p: buf + 2, v: 0x2000); // movs r0, :upper8_15:S |
987 | write16(ctx, p: buf + 4, v: 0x0200); // lsls r0, r0, #8 |
988 | write16(ctx, p: buf + 6, v: 0x3000); // adds r0, :upper0_7:S |
989 | write16(ctx, p: buf + 8, v: 0x0200); // lsls r0, r0, #8 |
990 | write16(ctx, p: buf + 10, v: 0x3000); // adds r0, :lower8_15:S |
991 | write16(ctx, p: buf + 12, v: 0x0200); // lsls r0, r0, #8 |
992 | write16(ctx, p: buf + 14, v: 0x3000); // adds r0, :lower0_7:S |
993 | write16(ctx, p: buf + 16, v: 0x9001); // str r0, [sp, #4] ; SP + 4 = S |
994 | write16(ctx, p: buf + 18, |
995 | v: 0xbd01); // pop {r0, pc} ; restore r0 and branch to dest |
996 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
997 | ctx.target->relocateNoSym(loc: buf + 2, type: R_ARM_THM_ALU_ABS_G3, val: s); |
998 | ctx.target->relocateNoSym(loc: buf + 6, type: R_ARM_THM_ALU_ABS_G2_NC, val: s); |
999 | ctx.target->relocateNoSym(loc: buf + 10, type: R_ARM_THM_ALU_ABS_G1_NC, val: s); |
1000 | ctx.target->relocateNoSym(loc: buf + 14, type: R_ARM_THM_ALU_ABS_G0_NC, val: s); |
1001 | } |
1002 | |
1003 | void ThumbV6MABSXOLongThunk::addSymbols(ThunkSection &isec) { |
1004 | addSymbol(name: ctx.saver.save(S: "__Thumbv6MABSXOLongThunk_"+ destination.getName()), |
1005 | type: STT_FUNC, value: 1, section&: isec); |
1006 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1007 | } |
1008 | |
1009 | void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { |
1010 | // Most Thumb instructions cannot access the high registers r8 - r15. As the |
1011 | // only register we can corrupt is ip (r12) we must instead spill a low |
1012 | // register to the stack to use as a scratch register. |
1013 | write16(ctx, p: buf + 0, |
1014 | v: 0xb401); // P: push {r0} ; Obtain scratch register |
1015 | write16(ctx, p: buf + 2, v: 0x4802); // ldr r0, [pc, #8] ; L2 |
1016 | write16(ctx, p: buf + 4, v: 0x4684); // mov ip, r0 ; high to low register |
1017 | write16(ctx, p: buf + 6, |
1018 | v: 0xbc01); // pop {r0} ; restore scratch register |
1019 | write16(ctx, p: buf + 8, v: 0x44e7); // L1: add pc, ip ; transfer control |
1020 | write16(ctx, p: buf + 10, |
1021 | v: 0x46c0); // nop ; pad to 4-byte boundary |
1022 | write32(ctx, p: buf + 12, v: 0x00000000); // L2: .word S - (P + (L1 - P) + 4) |
1023 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
1024 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
1025 | ctx.target->relocateNoSym(loc: buf + 12, type: R_ARM_REL32, val: s - p - 12); |
1026 | } |
1027 | |
1028 | void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { |
1029 | addSymbol(name: ctx.saver.save(S: "__Thumbv6MPILongThunk_"+ destination.getName()), |
1030 | type: STT_FUNC, value: 1, section&: isec); |
1031 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1032 | tsec = &isec; |
1033 | (void)getMayUseShortThunk(); |
1034 | } |
1035 | |
1036 | void ThumbV6MPILongThunk::addLongMapSyms() { |
1037 | addSymbol(name: "$d", type: STT_NOTYPE, value: 12, section&: *tsec); |
1038 | } |
1039 | |
1040 | void ARMV5LongLdrPcThunk::writeLong(uint8_t *buf) { |
1041 | write32(ctx, p: buf + 0, v: 0xe51ff004); // ldr pc, [pc,#-4] ; L1 |
1042 | write32(ctx, p: buf + 4, v: 0x00000000); // L1: .word S |
1043 | ctx.target->relocateNoSym(loc: buf + 4, type: R_ARM_ABS32, |
1044 | val: getARMThunkDestVA(ctx, s: destination)); |
1045 | } |
1046 | |
1047 | void ARMV5LongLdrPcThunk::addSymbols(ThunkSection &isec) { |
1048 | addSymbol(name: ctx.saver.save(S: "__ARMv5LongLdrPcThunk_"+ destination.getName()), |
1049 | type: STT_FUNC, value: 0, section&: isec); |
1050 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
1051 | tsec = &isec; |
1052 | (void)getMayUseShortThunk(); |
1053 | } |
1054 | |
1055 | void ARMV5LongLdrPcThunk::addLongMapSyms() { |
1056 | addSymbol(name: "$d", type: STT_NOTYPE, value: 4, section&: *tsec); |
1057 | } |
1058 | |
1059 | void ARMV4ABSLongBXThunk::writeLong(uint8_t *buf) { |
1060 | write32(ctx, p: buf + 0, v: 0xe59fc000); // ldr r12, [pc] ; L1 |
1061 | write32(ctx, p: buf + 4, v: 0xe12fff1c); // bx r12 |
1062 | write32(ctx, p: buf + 8, v: 0x00000000); // L1: .word S |
1063 | ctx.target->relocateNoSym(loc: buf + 8, type: R_ARM_ABS32, |
1064 | val: getARMThunkDestVA(ctx, s: destination)); |
1065 | } |
1066 | |
1067 | void ARMV4ABSLongBXThunk::addSymbols(ThunkSection &isec) { |
1068 | addSymbol(name: ctx.saver.save(S: "__ARMv4ABSLongBXThunk_"+ destination.getName()), |
1069 | type: STT_FUNC, value: 0, section&: isec); |
1070 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
1071 | tsec = &isec; |
1072 | (void)getMayUseShortThunk(); |
1073 | } |
1074 | |
1075 | void ARMV4ABSLongBXThunk::addLongMapSyms() { |
1076 | addSymbol(name: "$d", type: STT_NOTYPE, value: 8, section&: *tsec); |
1077 | } |
1078 | |
1079 | void ThumbV4ABSLongBXThunk::writeLong(uint8_t *buf) { |
1080 | write16(ctx, p: buf + 0, v: 0x4778); // bx pc |
1081 | write16(ctx, p: buf + 2, |
1082 | v: 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc |
1083 | write32(ctx, p: buf + 4, v: 0xe51ff004); // ldr pc, [pc, #-4] ; L1 |
1084 | write32(ctx, p: buf + 8, v: 0x00000000); // L1: .word S |
1085 | ctx.target->relocateNoSym(loc: buf + 8, type: R_ARM_ABS32, |
1086 | val: getARMThunkDestVA(ctx, s: destination)); |
1087 | } |
1088 | |
1089 | void ThumbV4ABSLongBXThunk::addSymbols(ThunkSection &isec) { |
1090 | addSymbol(name: ctx.saver.save(S: "__Thumbv4ABSLongBXThunk_"+ destination.getName()), |
1091 | type: STT_FUNC, value: 1, section&: isec); |
1092 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1093 | tsec = &isec; |
1094 | (void)getMayUseShortThunk(); |
1095 | } |
1096 | |
1097 | void ThumbV4ABSLongBXThunk::addLongMapSyms() { |
1098 | addSymbol(name: "$a", type: STT_NOTYPE, value: 4, section&: *tsec); |
1099 | addSymbol(name: "$d", type: STT_NOTYPE, value: 8, section&: *tsec); |
1100 | } |
1101 | |
1102 | void ThumbV4ABSLongThunk::writeLong(uint8_t *buf) { |
1103 | write16(ctx, p: buf + 0, v: 0x4778); // bx pc |
1104 | write16(ctx, p: buf + 2, |
1105 | v: 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc |
1106 | write32(ctx, p: buf + 4, v: 0xe59fc000); // ldr r12, [pc] ; L1 |
1107 | write32(ctx, p: buf + 8, v: 0xe12fff1c); // bx r12 |
1108 | write32(ctx, p: buf + 12, v: 0x00000000); // L1: .word S |
1109 | ctx.target->relocateNoSym(loc: buf + 12, type: R_ARM_ABS32, |
1110 | val: getARMThunkDestVA(ctx, s: destination)); |
1111 | } |
1112 | |
1113 | void ThumbV4ABSLongThunk::addSymbols(ThunkSection &isec) { |
1114 | addSymbol(name: ctx.saver.save(S: "__Thumbv4ABSLongThunk_"+ destination.getName()), |
1115 | type: STT_FUNC, value: 1, section&: isec); |
1116 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1117 | tsec = &isec; |
1118 | (void)getMayUseShortThunk(); |
1119 | } |
1120 | |
1121 | void ThumbV4ABSLongThunk::addLongMapSyms() { |
1122 | addSymbol(name: "$a", type: STT_NOTYPE, value: 4, section&: *tsec); |
1123 | addSymbol(name: "$d", type: STT_NOTYPE, value: 12, section&: *tsec); |
1124 | } |
1125 | |
1126 | void ARMV4PILongBXThunk::writeLong(uint8_t *buf) { |
1127 | write32(ctx, p: buf + 0, v: 0xe59fc004); // P: ldr ip, [pc,#4] ; L2 |
1128 | write32(ctx, p: buf + 4, v: 0xe08fc00c); // L1: add ip, pc, ip |
1129 | write32(ctx, p: buf + 8, v: 0xe12fff1c); // bx ip |
1130 | write32(ctx, p: buf + 12, v: 0x00000000); // L2: .word S - (P + (L1 - P) + 8) |
1131 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
1132 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
1133 | ctx.target->relocateNoSym(loc: buf + 12, type: R_ARM_REL32, val: s - p - 12); |
1134 | } |
1135 | |
1136 | void ARMV4PILongBXThunk::addSymbols(ThunkSection &isec) { |
1137 | addSymbol(name: ctx.saver.save(S: "__ARMv4PILongBXThunk_"+ destination.getName()), |
1138 | type: STT_FUNC, value: 0, section&: isec); |
1139 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
1140 | tsec = &isec; |
1141 | (void)getMayUseShortThunk(); |
1142 | } |
1143 | |
1144 | void ARMV4PILongBXThunk::addLongMapSyms() { |
1145 | addSymbol(name: "$d", type: STT_NOTYPE, value: 12, section&: *tsec); |
1146 | } |
1147 | |
1148 | void ARMV4PILongThunk::writeLong(uint8_t *buf) { |
1149 | write32(ctx, p: buf + 0, v: 0xe59fc000); // P: ldr ip, [pc] ; L2 |
1150 | write32(ctx, p: buf + 4, v: 0xe08ff00c); // L1: add pc, pc, r12 |
1151 | write32(ctx, p: buf + 8, v: 0x00000000); // L2: .word S - (P + (L1 - P) + 8) |
1152 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
1153 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
1154 | ctx.target->relocateNoSym(loc: buf + 8, type: R_ARM_REL32, val: s - p - 12); |
1155 | } |
1156 | |
1157 | void ARMV4PILongThunk::addSymbols(ThunkSection &isec) { |
1158 | addSymbol(name: ctx.saver.save(S: "__ARMv4PILongThunk_"+ destination.getName()), |
1159 | type: STT_FUNC, value: 0, section&: isec); |
1160 | addSymbol(name: "$a", type: STT_NOTYPE, value: 0, section&: isec); |
1161 | tsec = &isec; |
1162 | (void)getMayUseShortThunk(); |
1163 | } |
1164 | |
1165 | void ARMV4PILongThunk::addLongMapSyms() { |
1166 | addSymbol(name: "$d", type: STT_NOTYPE, value: 8, section&: *tsec); |
1167 | } |
1168 | |
1169 | void ThumbV4PILongBXThunk::writeLong(uint8_t *buf) { |
1170 | write16(ctx, p: buf + 0, v: 0x4778); // P: bx pc |
1171 | write16(ctx, p: buf + 2, |
1172 | v: 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc |
1173 | write32(ctx, p: buf + 4, v: 0xe59fc000); // ldr r12, [pc] ; L2 |
1174 | write32(ctx, p: buf + 8, v: 0xe08cf00f); // L1: add pc, r12, pc |
1175 | write32(ctx, p: buf + 12, v: 0x00000000); // L2: .word S - (P + (L1 - P) + 8) |
1176 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
1177 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
1178 | ctx.target->relocateNoSym(loc: buf + 12, type: R_ARM_REL32, val: s - p - 16); |
1179 | } |
1180 | |
1181 | void ThumbV4PILongBXThunk::addSymbols(ThunkSection &isec) { |
1182 | addSymbol(name: ctx.saver.save(S: "__Thumbv4PILongBXThunk_"+ destination.getName()), |
1183 | type: STT_FUNC, value: 1, section&: isec); |
1184 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1185 | tsec = &isec; |
1186 | (void)getMayUseShortThunk(); |
1187 | } |
1188 | |
1189 | void ThumbV4PILongBXThunk::addLongMapSyms() { |
1190 | addSymbol(name: "$a", type: STT_NOTYPE, value: 4, section&: *tsec); |
1191 | addSymbol(name: "$d", type: STT_NOTYPE, value: 12, section&: *tsec); |
1192 | } |
1193 | |
1194 | void ThumbV4PILongThunk::writeLong(uint8_t *buf) { |
1195 | write16(ctx, p: buf + 0, v: 0x4778); // P: bx pc |
1196 | write16(ctx, p: buf + 2, |
1197 | v: 0xe7fd); // b #-6 ; Arm recommended sequence to follow bx pc |
1198 | write32(ctx, p: buf + 4, v: 0xe59fc004); // ldr ip, [pc,#4] ; L2 |
1199 | write32(ctx, p: buf + 8, v: 0xe08fc00c); // L1: add ip, pc, ip |
1200 | write32(ctx, p: buf + 12, v: 0xe12fff1c); // bx ip |
1201 | write32(ctx, p: buf + 16, v: 0x00000000); // L2: .word S - (P + (L1 - P) + 8) |
1202 | uint64_t s = getARMThunkDestVA(ctx, s: destination); |
1203 | uint64_t p = getThunkTargetSym()->getVA(ctx) & ~0x1; |
1204 | ctx.target->relocateNoSym(loc: buf + 16, type: R_ARM_REL32, val: s - p - 16); |
1205 | } |
1206 | |
1207 | void ThumbV4PILongThunk::addSymbols(ThunkSection &isec) { |
1208 | addSymbol(name: ctx.saver.save(S: "__Thumbv4PILongThunk_"+ destination.getName()), |
1209 | type: STT_FUNC, value: 1, section&: isec); |
1210 | addSymbol(name: "$t", type: STT_NOTYPE, value: 0, section&: isec); |
1211 | tsec = &isec; |
1212 | (void)getMayUseShortThunk(); |
1213 | } |
1214 | |
1215 | void ThumbV4PILongThunk::addLongMapSyms() { |
1216 | addSymbol(name: "$a", type: STT_NOTYPE, value: 4, section&: *tsec); |
1217 | addSymbol(name: "$d", type: STT_NOTYPE, value: 16, section&: *tsec); |
1218 | } |
1219 | |
1220 | // Use the long jump which covers a range up to 8MiB. |
1221 | void AVRThunk::writeTo(uint8_t *buf) { |
1222 | write32(ctx, p: buf, v: 0x940c); // jmp func |
1223 | ctx.target->relocateNoSym(loc: buf, type: R_AVR_CALL, val: destination.getVA(ctx)); |
1224 | } |
1225 | |
1226 | void AVRThunk::addSymbols(ThunkSection &isec) { |
1227 | addSymbol(name: ctx.saver.save(S: "__AVRThunk_"+ destination.getName()), type: STT_FUNC, value: 0, |
1228 | section&: isec); |
1229 | } |
1230 | |
1231 | // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. |
1232 | void MipsThunk::writeTo(uint8_t *buf) { |
1233 | uint64_t s = destination.getVA(ctx); |
1234 | write32(ctx, p: buf, v: 0x3c190000); // lui $25, %hi(func) |
1235 | write32(ctx, p: buf + 4, v: 0x08000000 | (s >> 2)); // j func |
1236 | write32(ctx, p: buf + 8, v: 0x27390000); // addiu $25, $25, %lo(func) |
1237 | write32(ctx, p: buf + 12, v: 0x00000000); // nop |
1238 | ctx.target->relocateNoSym(loc: buf, type: R_MIPS_HI16, val: s); |
1239 | ctx.target->relocateNoSym(loc: buf + 8, type: R_MIPS_LO16, val: s); |
1240 | } |
1241 | |
1242 | void MipsThunk::addSymbols(ThunkSection &isec) { |
1243 | addSymbol(name: ctx.saver.save(S: "__LA25Thunk_"+ destination.getName()), type: STT_FUNC, value: 0, |
1244 | section&: isec); |
1245 | } |
1246 | |
1247 | InputSection *MipsThunk::getTargetInputSection() const { |
1248 | auto &dr = cast<Defined>(Val&: destination); |
1249 | return dyn_cast<InputSection>(Val: dr.section); |
1250 | } |
1251 | |
1252 | // Write microMIPS R2-R5 LA25 thunk code |
1253 | // to call PIC function from the non-PIC one. |
1254 | void MicroMipsThunk::writeTo(uint8_t *buf) { |
1255 | uint64_t s = destination.getVA(ctx); |
1256 | write16(ctx, p: buf, v: 0x41b9); // lui $25, %hi(func) |
1257 | write16(ctx, p: buf + 4, v: 0xd400); // j func |
1258 | write16(ctx, p: buf + 8, v: 0x3339); // addiu $25, $25, %lo(func) |
1259 | write16(ctx, p: buf + 12, v: 0x0c00); // nop |
1260 | ctx.target->relocateNoSym(loc: buf, type: R_MICROMIPS_HI16, val: s); |
1261 | ctx.target->relocateNoSym(loc: buf + 4, type: R_MICROMIPS_26_S1, val: s); |
1262 | ctx.target->relocateNoSym(loc: buf + 8, type: R_MICROMIPS_LO16, val: s); |
1263 | } |
1264 | |
1265 | void MicroMipsThunk::addSymbols(ThunkSection &isec) { |
1266 | Defined *d = |
1267 | addSymbol(name: ctx.saver.save(S: "__microLA25Thunk_"+ destination.getName()), |
1268 | type: STT_FUNC, value: 0, section&: isec); |
1269 | d->stOther |= STO_MIPS_MICROMIPS; |
1270 | } |
1271 | |
1272 | InputSection *MicroMipsThunk::getTargetInputSection() const { |
1273 | auto &dr = cast<Defined>(Val&: destination); |
1274 | return dyn_cast<InputSection>(Val: dr.section); |
1275 | } |
1276 | |
1277 | // Write microMIPS R6 LA25 thunk code |
1278 | // to call PIC function from the non-PIC one. |
1279 | void MicroMipsR6Thunk::writeTo(uint8_t *buf) { |
1280 | uint64_t s = destination.getVA(ctx); |
1281 | uint64_t p = getThunkTargetSym()->getVA(ctx); |
1282 | write16(ctx, p: buf, v: 0x1320); // lui $25, %hi(func) |
1283 | write16(ctx, p: buf + 4, v: 0x3339); // addiu $25, $25, %lo(func) |
1284 | write16(ctx, p: buf + 8, v: 0x9400); // bc func |
1285 | ctx.target->relocateNoSym(loc: buf, type: R_MICROMIPS_HI16, val: s); |
1286 | ctx.target->relocateNoSym(loc: buf + 4, type: R_MICROMIPS_LO16, val: s); |
1287 | ctx.target->relocateNoSym(loc: buf + 8, type: R_MICROMIPS_PC26_S1, val: s - p - 12); |
1288 | } |
1289 | |
1290 | void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { |
1291 | Defined *d = |
1292 | addSymbol(name: ctx.saver.save(S: "__microLA25Thunk_"+ destination.getName()), |
1293 | type: STT_FUNC, value: 0, section&: isec); |
1294 | d->stOther |= STO_MIPS_MICROMIPS; |
1295 | } |
1296 | |
1297 | InputSection *MicroMipsR6Thunk::getTargetInputSection() const { |
1298 | auto &dr = cast<Defined>(Val&: destination); |
1299 | return dyn_cast<InputSection>(Val: dr.section); |
1300 | } |
1301 | |
1302 | void elf::writePPC32PltCallStub(Ctx &ctx, uint8_t *buf, uint64_t gotPltVA, |
1303 | const InputFile *file, int64_t addend) { |
1304 | if (!ctx.arg.isPic) { |
1305 | write32(ctx, p: buf + 0, v: 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha |
1306 | write32(ctx, p: buf + 4, v: 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11) |
1307 | write32(ctx, p: buf + 8, v: 0x7d6903a6); // mtctr r11 |
1308 | write32(ctx, p: buf + 12, v: 0x4e800420); // bctr |
1309 | return; |
1310 | } |
1311 | uint32_t offset; |
1312 | if (addend >= 0x8000) { |
1313 | // The stub loads an address relative to r30 (.got2+Addend). Addend is |
1314 | // almost always 0x8000. The address of .got2 is different in another object |
1315 | // file, so a stub cannot be shared. |
1316 | offset = gotPltVA - |
1317 | (ctx.in.ppc32Got2->getParent()->getVA() + |
1318 | (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend); |
1319 | } else { |
1320 | // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is |
1321 | // currently the address of .got). |
1322 | offset = gotPltVA - ctx.in.got->getVA(); |
1323 | } |
1324 | uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset; |
1325 | if (ha == 0) { |
1326 | write32(ctx, p: buf + 0, v: 0x817e0000 | l); // lwz r11,l(r30) |
1327 | write32(ctx, p: buf + 4, v: 0x7d6903a6); // mtctr r11 |
1328 | write32(ctx, p: buf + 8, v: 0x4e800420); // bctr |
1329 | write32(ctx, p: buf + 12, v: 0x60000000); // nop |
1330 | } else { |
1331 | write32(ctx, p: buf + 0, v: 0x3d7e0000 | ha); // addis r11,r30,ha |
1332 | write32(ctx, p: buf + 4, v: 0x816b0000 | l); // lwz r11,l(r11) |
1333 | write32(ctx, p: buf + 8, v: 0x7d6903a6); // mtctr r11 |
1334 | write32(ctx, p: buf + 12, v: 0x4e800420); // bctr |
1335 | } |
1336 | } |
1337 | |
1338 | void PPC32PltCallStub::writeTo(uint8_t *buf) { |
1339 | writePPC32PltCallStub(ctx, buf, gotPltVA: destination.getGotPltVA(ctx), file, addend); |
1340 | } |
1341 | |
1342 | void PPC32PltCallStub::addSymbols(ThunkSection &isec) { |
1343 | std::string buf; |
1344 | raw_string_ostream os(buf); |
1345 | os << format_hex_no_prefix(N: addend, Width: 8); |
1346 | if (!ctx.arg.isPic) |
1347 | os << ".plt_call32."; |
1348 | else if (addend >= 0x8000) |
1349 | os << ".got2.plt_pic32."; |
1350 | else |
1351 | os << ".plt_pic32."; |
1352 | os << destination.getName(); |
1353 | addSymbol(name: ctx.saver.save(S: buf), type: STT_FUNC, value: 0, section&: isec); |
1354 | } |
1355 | |
1356 | bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, |
1357 | const Relocation &rel) const { |
1358 | return !ctx.arg.isPic || (isec.file == file && rel.addend == addend); |
1359 | } |
1360 | |
1361 | void PPC32LongThunk::addSymbols(ThunkSection &isec) { |
1362 | addSymbol(name: ctx.saver.save(S: "__LongThunk_"+ destination.getName()), type: STT_FUNC, value: 0, |
1363 | section&: isec); |
1364 | } |
1365 | |
1366 | void PPC32LongThunk::writeTo(uint8_t *buf) { |
1367 | auto ha = [](uint32_t v) -> uint16_t { return (v + 0x8000) >> 16; }; |
1368 | auto lo = [](uint32_t v) -> uint16_t { return v; }; |
1369 | uint32_t d = destination.getVA(ctx, addend); |
1370 | if (ctx.arg.isPic) { |
1371 | uint32_t off = d - (getThunkTargetSym()->getVA(ctx) + 8); |
1372 | write32(ctx, p: buf + 0, v: 0x7c0802a6); // mflr r12,0 |
1373 | write32(ctx, p: buf + 4, v: 0x429f0005); // bcl r20,r31,.+4 |
1374 | write32(ctx, p: buf + 8, v: 0x7d8802a6); // mtctr r12 |
1375 | write32(ctx, p: buf + 12, v: 0x3d8c0000 | ha(off)); // addis r12,r12,off@ha |
1376 | write32(ctx, p: buf + 16, v: 0x398c0000 | lo(off)); // addi r12,r12,off@l |
1377 | write32(ctx, p: buf + 20, v: 0x7c0803a6); // mtlr r0 |
1378 | buf += 24; |
1379 | } else { |
1380 | write32(ctx, p: buf + 0, v: 0x3d800000 | ha(d)); // lis r12,d@ha |
1381 | write32(ctx, p: buf + 4, v: 0x398c0000 | lo(d)); // addi r12,r12,d@l |
1382 | buf += 8; |
1383 | } |
1384 | write32(ctx, p: buf + 0, v: 0x7d8903a6); // mtctr r12 |
1385 | write32(ctx, p: buf + 4, v: 0x4e800420); // bctr |
1386 | } |
1387 | |
1388 | void elf::writePPC64LoadAndBranch(Ctx &ctx, uint8_t *buf, int64_t offset) { |
1389 | uint16_t offHa = (offset + 0x8000) >> 16; |
1390 | uint16_t offLo = offset & 0xffff; |
1391 | |
1392 | write32(ctx, p: buf + 0, v: 0x3d820000 | offHa); // addis r12, r2, OffHa |
1393 | write32(ctx, p: buf + 4, v: 0xe98c0000 | offLo); // ld r12, OffLo(r12) |
1394 | write32(ctx, p: buf + 8, v: 0x7d8903a6); // mtctr r12 |
1395 | write32(ctx, p: buf + 12, v: 0x4e800420); // bctr |
1396 | } |
1397 | |
1398 | void PPC64PltCallStub::writeTo(uint8_t *buf) { |
1399 | int64_t offset = destination.getGotPltVA(ctx) - getPPC64TocBase(ctx); |
1400 | // Save the TOC pointer to the save-slot reserved in the call frame. |
1401 | write32(ctx, p: buf + 0, v: 0xf8410018); // std r2,24(r1) |
1402 | writePPC64LoadAndBranch(ctx, buf: buf + 4, offset); |
1403 | } |
1404 | |
1405 | void PPC64PltCallStub::addSymbols(ThunkSection &isec) { |
1406 | Defined *s = addSymbol(name: ctx.saver.save(S: "__plt_"+ destination.getName()), |
1407 | type: STT_FUNC, value: 0, section&: isec); |
1408 | s->setNeedsTocRestore(true); |
1409 | s->file = destination.file; |
1410 | } |
1411 | |
1412 | bool PPC64PltCallStub::isCompatibleWith(const InputSection &isec, |
1413 | const Relocation &rel) const { |
1414 | return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; |
1415 | } |
1416 | |
1417 | void PPC64R2SaveStub::writeTo(uint8_t *buf) { |
1418 | const int64_t offset = computeOffset(); |
1419 | write32(ctx, p: buf + 0, v: 0xf8410018); // std r2,24(r1) |
1420 | // The branch offset needs to fit in 26 bits. |
1421 | if (getMayUseShortThunk()) { |
1422 | write32(ctx, p: buf + 4, v: 0x48000000 | (offset & 0x03fffffc)); // b <offset> |
1423 | } else if (isInt<34>(x: offset)) { |
1424 | int nextInstOffset; |
1425 | uint64_t tocOffset = destination.getVA(ctx) - getPPC64TocBase(ctx); |
1426 | if (tocOffset >> 16 > 0) { |
1427 | const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff); |
1428 | const uint64_t addis = |
1429 | ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff); |
1430 | write32(ctx, p: buf + 4, v: addis); // addis r12, r2 , top of offset |
1431 | write32(ctx, p: buf + 8, v: addi); // addi r12, r12, bottom of offset |
1432 | nextInstOffset = 12; |
1433 | } else { |
1434 | const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff); |
1435 | write32(ctx, p: buf + 4, v: addi); // addi r12, r2, offset |
1436 | nextInstOffset = 8; |
1437 | } |
1438 | write32(ctx, p: buf + nextInstOffset, v: MTCTR_R12); // mtctr r12 |
1439 | write32(ctx, p: buf + nextInstOffset + 4, v: BCTR); // bctr |
1440 | } else { |
1441 | ctx.in.ppc64LongBranchTarget->addEntry(sym: &destination, addend); |
1442 | const int64_t offsetFromTOC = |
1443 | ctx.in.ppc64LongBranchTarget->getEntryVA(sym: &destination, addend) - |
1444 | getPPC64TocBase(ctx); |
1445 | writePPC64LoadAndBranch(ctx, buf: buf + 4, offset: offsetFromTOC); |
1446 | } |
1447 | } |
1448 | |
1449 | void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { |
1450 | Defined *s = addSymbol(name: ctx.saver.save(S: "__toc_save_"+ destination.getName()), |
1451 | type: STT_FUNC, value: 0, section&: isec); |
1452 | s->setNeedsTocRestore(true); |
1453 | } |
1454 | |
1455 | bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec, |
1456 | const Relocation &rel) const { |
1457 | return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; |
1458 | } |
1459 | |
1460 | void PPC64R12SetupStub::writeTo(uint8_t *buf) { |
1461 | int64_t offset = |
1462 | (gotPlt ? destination.getGotPltVA(ctx) : destination.getVA(ctx)) - |
1463 | getThunkTargetSym()->getVA(ctx); |
1464 | if (!isInt<34>(x: offset)) |
1465 | reportRangeError(ctx, loc: buf, v: offset, n: 34, sym: destination, |
1466 | msg: "R12 setup stub offset"); |
1467 | |
1468 | int nextInstOffset; |
1469 | if (ctx.arg.power10Stubs) { |
1470 | const uint64_t imm = (((offset >> 16) & 0x3ffff) << 32) | (offset & 0xffff); |
1471 | // pld 12, func@plt@pcrel or paddi r12, 0, func@pcrel |
1472 | writePrefixedInst(ctx, loc: buf, |
1473 | insn: (gotPlt ? PLD_R12_NO_DISP : PADDI_R12_NO_DISP) | imm); |
1474 | nextInstOffset = 8; |
1475 | } else { |
1476 | uint32_t off = offset - 8; |
1477 | write32(ctx, p: buf + 0, v: 0x7d8802a6); // mflr 12 |
1478 | write32(ctx, p: buf + 4, v: 0x429f0005); // bcl 20,31,.+4 |
1479 | write32(ctx, p: buf + 8, v: 0x7d6802a6); // mflr 11 |
1480 | write32(ctx, p: buf + 12, v: 0x7d8803a6); // mtlr 12 |
1481 | write32(ctx, p: buf + 16, |
1482 | v: 0x3d8b0000 | ((off + 0x8000) >> 16)); // addis 12,11,off@ha |
1483 | if (gotPlt) |
1484 | write32(ctx, p: buf + 20, v: 0xe98c0000 | (off & 0xffff)); // ld 12, off@l(12) |
1485 | else |
1486 | write32(ctx, p: buf + 20, v: 0x398c0000 | (off & 0xffff)); // addi 12,12,off@l |
1487 | nextInstOffset = 24; |
1488 | } |
1489 | write32(ctx, p: buf + nextInstOffset, v: MTCTR_R12); // mtctr r12 |
1490 | write32(ctx, p: buf + nextInstOffset + 4, v: BCTR); // bctr |
1491 | } |
1492 | |
1493 | void PPC64R12SetupStub::addSymbols(ThunkSection &isec) { |
1494 | addSymbol(name: ctx.saver.save(S: (gotPlt ? "__plt_pcrel_": "__gep_setup_") + |
1495 | destination.getName()), |
1496 | type: STT_FUNC, value: 0, section&: isec); |
1497 | } |
1498 | |
1499 | bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec, |
1500 | const Relocation &rel) const { |
1501 | return rel.type == R_PPC64_REL24_NOTOC; |
1502 | } |
1503 | |
1504 | void PPC64LongBranchThunk::writeTo(uint8_t *buf) { |
1505 | int64_t offset = |
1506 | ctx.in.ppc64LongBranchTarget->getEntryVA(sym: &destination, addend) - |
1507 | getPPC64TocBase(ctx); |
1508 | writePPC64LoadAndBranch(ctx, buf, offset); |
1509 | } |
1510 | |
1511 | void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { |
1512 | addSymbol(name: ctx.saver.save(S: "__long_branch_"+ destination.getName()), type: STT_FUNC, |
1513 | value: 0, section&: isec); |
1514 | } |
1515 | |
1516 | bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec, |
1517 | const Relocation &rel) const { |
1518 | return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; |
1519 | } |
1520 | |
1521 | Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a) |
1522 | : ctx(ctx), destination(d), addend(a), offset(0) { |
1523 | destination.thunkAccessed = true; |
1524 | } |
1525 | |
1526 | Thunk::~Thunk() = default; |
1527 | |
1528 | static std::unique_ptr<Thunk> addThunkAArch64(Ctx &ctx, const InputSection &sec, |
1529 | RelType type, Symbol &s, |
1530 | int64_t a) { |
1531 | assert(is_contained({R_AARCH64_CALL26, R_AARCH64_JUMP26, R_AARCH64_PLT32}, |
1532 | type)); |
1533 | bool mayNeedLandingPad = |
1534 | (ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) && |
1535 | !isAArch64BTILandingPad(ctx, s, a); |
1536 | if (ctx.arg.picThunk) |
1537 | return std::make_unique<AArch64ADRPThunk>(args&: ctx, args&: s, args&: a, args&: mayNeedLandingPad); |
1538 | if (sec.getParent()->flags & SHF_AARCH64_PURECODE) |
1539 | return std::make_unique<AArch64ABSXOLongThunk>(args&: ctx, args&: s, args&: a, |
1540 | args&: mayNeedLandingPad); |
1541 | return std::make_unique<AArch64ABSLongThunk>(args&: ctx, args&: s, args&: a, args&: mayNeedLandingPad); |
1542 | } |
1543 | |
1544 | // Creates a thunk for long branches or Thumb-ARM interworking. |
1545 | // Arm Architectures v4t does not support Thumb2 technology, and does not |
1546 | // support BLX or LDR Arm/Thumb state switching. This means that |
1547 | // - MOVT and MOVW instructions cannot be used. |
1548 | // - We can't rewrite BL in place to BLX. We will need thunks. |
1549 | // |
1550 | // TODO: use B for short Thumb->Arm thunks instead of LDR (this doesn't work for |
1551 | // Arm->Thumb, as in Arm state no BX PC trick; it doesn't switch state). |
1552 | static std::unique_ptr<Thunk> addThunkArmv4(Ctx &ctx, RelType reloc, Symbol &s, |
1553 | int64_t a) { |
1554 | bool thumb_target = s.getVA(ctx, addend: a) & 1; |
1555 | |
1556 | switch (reloc) { |
1557 | case R_ARM_PC24: |
1558 | case R_ARM_PLT32: |
1559 | case R_ARM_JUMP24: |
1560 | case R_ARM_CALL: |
1561 | if (ctx.arg.picThunk) { |
1562 | if (thumb_target) |
1563 | return std::make_unique<ARMV4PILongBXThunk>(args&: ctx, args&: s, args&: a); |
1564 | return std::make_unique<ARMV4PILongThunk>(args&: ctx, args&: s, args&: a); |
1565 | } |
1566 | if (thumb_target) |
1567 | return std::make_unique<ARMV4ABSLongBXThunk>(args&: ctx, args&: s, args&: a); |
1568 | return std::make_unique<ARMV5LongLdrPcThunk>(args&: ctx, args&: s, args&: a); |
1569 | case R_ARM_THM_CALL: |
1570 | if (ctx.arg.picThunk) { |
1571 | if (thumb_target) |
1572 | return std::make_unique<ThumbV4PILongThunk>(args&: ctx, args&: s, args&: a); |
1573 | return std::make_unique<ThumbV4PILongBXThunk>(args&: ctx, args&: s, args&: a); |
1574 | } |
1575 | if (thumb_target) |
1576 | return std::make_unique<ThumbV4ABSLongThunk>(args&: ctx, args&: s, args&: a); |
1577 | return std::make_unique<ThumbV4ABSLongBXThunk>(args&: ctx, args&: s, args&: a); |
1578 | } |
1579 | Fatal(ctx) << "relocation "<< reloc << " to "<< &s |
1580 | << " not supported for Armv4 or Armv4T target"; |
1581 | llvm_unreachable(""); |
1582 | } |
1583 | |
1584 | // Creates a thunk for Thumb-ARM interworking compatible with Armv5 and Armv6. |
1585 | // Arm Architectures v5 and v6 do not support Thumb2 technology. This means that |
1586 | // - MOVT and MOVW instructions cannot be used |
1587 | // - Only Thumb relocation that can generate a Thunk is a BL, this can always |
1588 | // be transformed into a BLX |
1589 | static std::unique_ptr<Thunk> addThunkArmv5v6(Ctx &ctx, RelType reloc, |
1590 | Symbol &s, int64_t a) { |
1591 | switch (reloc) { |
1592 | case R_ARM_PC24: |
1593 | case R_ARM_PLT32: |
1594 | case R_ARM_JUMP24: |
1595 | case R_ARM_CALL: |
1596 | case R_ARM_THM_CALL: |
1597 | if (ctx.arg.picThunk) |
1598 | return std::make_unique<ARMV4PILongBXThunk>(args&: ctx, args&: s, args&: a); |
1599 | return std::make_unique<ARMV5LongLdrPcThunk>(args&: ctx, args&: s, args&: a); |
1600 | } |
1601 | Fatal(ctx) << "relocation "<< reloc << " to "<< &s |
1602 | << " not supported for Armv5 or Armv6 targets"; |
1603 | llvm_unreachable(""); |
1604 | } |
1605 | |
1606 | // Create a thunk for Thumb long branch on V6-M. |
1607 | // Arm Architecture v6-M only supports Thumb instructions. This means |
1608 | // - MOVT and MOVW instructions cannot be used. |
1609 | // - Only a limited number of instructions can access registers r8 and above |
1610 | // - No interworking support is needed (all Thumb). |
1611 | static std::unique_ptr<Thunk> addThunkV6M(Ctx &ctx, const InputSection &isec, |
1612 | RelType reloc, Symbol &s, int64_t a) { |
1613 | const bool isPureCode = isec.getParent()->flags & SHF_ARM_PURECODE; |
1614 | switch (reloc) { |
1615 | case R_ARM_THM_JUMP19: |
1616 | case R_ARM_THM_JUMP24: |
1617 | case R_ARM_THM_CALL: |
1618 | if (ctx.arg.isPic) { |
1619 | if (!isPureCode) |
1620 | return std::make_unique<ThumbV6MPILongThunk>(args&: ctx, args&: s, args&: a); |
1621 | |
1622 | Fatal(ctx) |
1623 | << "relocation "<< reloc << " to "<< &s |
1624 | << " not supported for Armv6-M targets for position independent" |
1625 | " and execute only code"; |
1626 | llvm_unreachable(""); |
1627 | } |
1628 | if (isPureCode) |
1629 | return std::make_unique<ThumbV6MABSXOLongThunk>(args&: ctx, args&: s, args&: a); |
1630 | return std::make_unique<ThumbV6MABSLongThunk>(args&: ctx, args&: s, args&: a); |
1631 | } |
1632 | Fatal(ctx) << "relocation "<< reloc << " to "<< &s |
1633 | << " not supported for Armv6-M targets"; |
1634 | llvm_unreachable(""); |
1635 | } |
1636 | |
1637 | // Creates a thunk for Thumb-ARM interworking or branch range extension. |
1638 | static std::unique_ptr<Thunk> addThunkArm(Ctx &ctx, const InputSection &isec, |
1639 | RelType reloc, Symbol &s, int64_t a) { |
1640 | // Decide which Thunk is needed based on: |
1641 | // Available instruction set |
1642 | // - An Arm Thunk can only be used if Arm state is available. |
1643 | // - A Thumb Thunk can only be used if Thumb state is available. |
1644 | // - Can only use a Thunk if it uses instructions that the Target supports. |
1645 | // Relocation is branch or branch and link |
1646 | // - Branch instructions cannot change state, can only select Thunk that |
1647 | // starts in the same state as the caller. |
1648 | // - Branch and link relocations can change state, can select Thunks from |
1649 | // either Arm or Thumb. |
1650 | // Position independent Thunks if we require position independent code. |
1651 | // Execute Only Thunks if the output section is execute only code. |
1652 | |
1653 | // Handle architectures that have restrictions on the instructions that they |
1654 | // can use in Thunks. The flags below are set by reading the BuildAttributes |
1655 | // of the input objects. InputFiles.cpp contains the mapping from ARM |
1656 | // architecture to flag. |
1657 | if (!ctx.arg.armHasMovtMovw) { |
1658 | if (ctx.arg.armJ1J2BranchEncoding) |
1659 | return addThunkV6M(ctx, isec, reloc, s, a); |
1660 | if (ctx.arg.armHasBlx) |
1661 | return addThunkArmv5v6(ctx, reloc, s, a); |
1662 | return addThunkArmv4(ctx, reloc, s, a); |
1663 | } |
1664 | |
1665 | switch (reloc) { |
1666 | case R_ARM_PC24: |
1667 | case R_ARM_PLT32: |
1668 | case R_ARM_JUMP24: |
1669 | case R_ARM_CALL: |
1670 | if (ctx.arg.picThunk) |
1671 | return std::make_unique<ARMV7PILongThunk>(args&: ctx, args&: s, args&: a); |
1672 | return std::make_unique<ARMV7ABSLongThunk>(args&: ctx, args&: s, args&: a); |
1673 | case R_ARM_THM_JUMP19: |
1674 | case R_ARM_THM_JUMP24: |
1675 | case R_ARM_THM_CALL: |
1676 | if (ctx.arg.picThunk) |
1677 | return std::make_unique<ThumbV7PILongThunk>(args&: ctx, args&: s, args&: a); |
1678 | return std::make_unique<ThumbV7ABSLongThunk>(args&: ctx, args&: s, args&: a); |
1679 | } |
1680 | llvm_unreachable(""); |
1681 | } |
1682 | |
1683 | static std::unique_ptr<Thunk> addThunkAVR(Ctx &ctx, RelType type, Symbol &s, |
1684 | int64_t a) { |
1685 | switch (type) { |
1686 | case R_AVR_LO8_LDI_GS: |
1687 | case R_AVR_HI8_LDI_GS: |
1688 | return std::make_unique<AVRThunk>(args&: ctx, args&: s, args&: a); |
1689 | default: |
1690 | llvm_unreachable(""); |
1691 | } |
1692 | } |
1693 | |
1694 | static std::unique_ptr<Thunk> addThunkMips(Ctx &ctx, RelType type, Symbol &s) { |
1695 | if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx)) |
1696 | return std::make_unique<MicroMipsR6Thunk>(args&: ctx, args&: s); |
1697 | if (s.stOther & STO_MIPS_MICROMIPS) |
1698 | return std::make_unique<MicroMipsThunk>(args&: ctx, args&: s); |
1699 | return std::make_unique<MipsThunk>(args&: ctx, args&: s); |
1700 | } |
1701 | |
1702 | static std::unique_ptr<Thunk> addThunkPPC32(Ctx &ctx, const InputSection &isec, |
1703 | const Relocation &rel, Symbol &s) { |
1704 | assert((rel.type == R_PPC_LOCAL24PC || rel.type == R_PPC_REL24 || |
1705 | rel.type == R_PPC_PLTREL24) && |
1706 | "unexpected relocation type for thunk"); |
1707 | if (s.isInPlt(ctx)) |
1708 | return std::make_unique<PPC32PltCallStub>(args&: ctx, args: isec, args: rel, args&: s); |
1709 | return std::make_unique<PPC32LongThunk>(args&: ctx, args&: s, args: rel.addend); |
1710 | } |
1711 | |
1712 | static std::unique_ptr<Thunk> addThunkPPC64(Ctx &ctx, RelType type, Symbol &s, |
1713 | int64_t a) { |
1714 | assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 || |
1715 | type == R_PPC64_REL24_NOTOC) && |
1716 | "unexpected relocation type for thunk"); |
1717 | |
1718 | // If we are emitting stubs for NOTOC relocations, we need to tell |
1719 | // the PLT resolver that there can be multiple TOCs. |
1720 | if (type == R_PPC64_REL24_NOTOC) |
1721 | ctx.target->ppc64DynamicSectionOpt = 0x2; |
1722 | |
1723 | if (s.isInPlt(ctx)) { |
1724 | if (type == R_PPC64_REL24_NOTOC) |
1725 | return std::make_unique<PPC64R12SetupStub>(args&: ctx, args&: s, |
1726 | /*gotPlt=*/args: true); |
1727 | return std::make_unique<PPC64PltCallStub>(args&: ctx, args&: s); |
1728 | } |
1729 | |
1730 | // This check looks at the st_other bits of the callee. If the value is 1 |
1731 | // then the callee clobbers the TOC and we need an R2 save stub when RelType |
1732 | // is R_PPC64_REL14 or R_PPC64_REL24. |
1733 | if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1) |
1734 | return std::make_unique<PPC64R2SaveStub>(args&: ctx, args&: s, args&: a); |
1735 | |
1736 | if (type == R_PPC64_REL24_NOTOC) |
1737 | return std::make_unique<PPC64R12SetupStub>(args&: ctx, args&: s, /*gotPlt=*/args: false); |
1738 | |
1739 | if (ctx.arg.picThunk) |
1740 | return std::make_unique<PPC64PILongBranchThunk>(args&: ctx, args&: s, args&: a); |
1741 | |
1742 | return std::make_unique<PPC64PDLongBranchThunk>(args&: ctx, args&: s, args&: a); |
1743 | } |
1744 | |
1745 | std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec, |
1746 | Relocation &rel) { |
1747 | Symbol &s = *rel.sym; |
1748 | int64_t a = rel.addend; |
1749 | |
1750 | switch (ctx.arg.emachine) { |
1751 | case EM_AARCH64: |
1752 | return addThunkAArch64(ctx, sec: isec, type: rel.type, s, a); |
1753 | case EM_ARM: |
1754 | return addThunkArm(ctx, isec, reloc: rel.type, s, a); |
1755 | case EM_AVR: |
1756 | return addThunkAVR(ctx, type: rel.type, s, a); |
1757 | case EM_MIPS: |
1758 | return addThunkMips(ctx, type: rel.type, s); |
1759 | case EM_PPC: |
1760 | return addThunkPPC32(ctx, isec, rel, s); |
1761 | case EM_PPC64: |
1762 | return addThunkPPC64(ctx, type: rel.type, s, a); |
1763 | default: |
1764 | llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC"); |
1765 | } |
1766 | } |
1767 | |
1768 | std::unique_ptr<Thunk> elf::addLandingPadThunk(Ctx &ctx, Symbol &s, int64_t a) { |
1769 | switch (ctx.arg.emachine) { |
1770 | case EM_AARCH64: |
1771 | return std::make_unique<AArch64BTILandingPadThunk>(args&: ctx, args&: s, args&: a); |
1772 | default: |
1773 | llvm_unreachable("add landing pad only supported for AArch64"); |
1774 | } |
1775 | } |
1776 |
Definitions
- AArch64Thunk
- AArch64Thunk
- addLongMapSyms
- AArch64ABSLongThunk
- AArch64ABSLongThunk
- size
- AArch64ABSXOLongThunk
- AArch64ABSXOLongThunk
- size
- AArch64ADRPThunk
- AArch64ADRPThunk
- size
- AArch64BTILandingPadThunk
- AArch64BTILandingPadThunk
- size
- ARMThunk
- ARMThunk
- size
- addLongMapSyms
- ThumbThunk
- ThumbThunk
- size
- addLongMapSyms
- ARMV7ABSLongThunk
- ARMV7ABSLongThunk
- sizeLong
- ARMV7PILongThunk
- ARMV7PILongThunk
- sizeLong
- ThumbV7ABSLongThunk
- ThumbV7ABSLongThunk
- sizeLong
- ThumbV7PILongThunk
- ThumbV7PILongThunk
- sizeLong
- ThumbV6MABSLongThunk
- ThumbV6MABSLongThunk
- sizeLong
- ThumbV6MABSXOLongThunk
- ThumbV6MABSXOLongThunk
- sizeLong
- ThumbV6MPILongThunk
- ThumbV6MPILongThunk
- sizeLong
- ARMV5LongLdrPcThunk
- ARMV5LongLdrPcThunk
- sizeLong
- ARMV4PILongBXThunk
- ARMV4PILongBXThunk
- sizeLong
- ARMV4PILongThunk
- ARMV4PILongThunk
- sizeLong
- ThumbV4PILongBXThunk
- ThumbV4PILongBXThunk
- sizeLong
- ThumbV4PILongThunk
- ThumbV4PILongThunk
- sizeLong
- ARMV4ABSLongBXThunk
- ARMV4ABSLongBXThunk
- sizeLong
- ThumbV4ABSLongBXThunk
- ThumbV4ABSLongBXThunk
- sizeLong
- ThumbV4ABSLongThunk
- ThumbV4ABSLongThunk
- sizeLong
- AVRThunk
- AVRThunk
- size
- MipsThunk
- MipsThunk
- size
- MicroMipsThunk
- MicroMipsThunk
- size
- MicroMipsR6Thunk
- MicroMipsR6Thunk
- size
- PPC32PltCallStub
- PPC32PltCallStub
- size
- PPC32LongThunk
- PPC32LongThunk
- size
- PPC64PltCallStub
- PPC64PltCallStub
- size
- PPC64R2SaveStub
- PPC64R2SaveStub
- getMayUseShortThunk
- size
- computeOffset
- PPC64R12SetupStub
- PPC64R12SetupStub
- size
- PPC64LongBranchThunk
- size
- PPC64LongBranchThunk
- PPC64PILongBranchThunk
- PPC64PILongBranchThunk
- PPC64PDLongBranchThunk
- PPC64PDLongBranchThunk
- addSymbol
- setOffset
- getAArch64ThunkDestVA
- getMayUseShortThunk
- writeTo
- needsSyntheticLandingPad
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- writeLong
- addSymbols
- addSymbols
- writeTo
- getMayUseShortThunk
- writeLong
- getARMThunkDestVA
- getMayUseShortThunk
- writeTo
- isCompatibleWith
- getMayUseShortThunk
- writeTo
- isCompatibleWith
- writeLong
- addSymbols
- writeLong
- addSymbols
- writeLong
- addSymbols
- writeLong
- addSymbols
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeLong
- addSymbols
- addLongMapSyms
- writeTo
- addSymbols
- writeTo
- addSymbols
- getTargetInputSection
- writeTo
- addSymbols
- getTargetInputSection
- writeTo
- addSymbols
- getTargetInputSection
- writePPC32PltCallStub
- writeTo
- addSymbols
- isCompatibleWith
- addSymbols
- writeTo
- writePPC64LoadAndBranch
- writeTo
- addSymbols
- isCompatibleWith
- writeTo
- addSymbols
- isCompatibleWith
- writeTo
- addSymbols
- isCompatibleWith
- writeTo
- addSymbols
- isCompatibleWith
- Thunk
- ~Thunk
- addThunkAArch64
- addThunkArmv4
- addThunkArmv5v6
- addThunkV6M
- addThunkArm
- addThunkAVR
- addThunkMips
- addThunkPPC32
- addThunkPPC64
- addThunk
Improve your Profiling and Debugging skills
Find out more