Skip to main content

linker_utils/
aarch64.rs

1use crate::bit_misc::BitExtraction;
2use crate::elf::AArch64Instruction;
3use crate::elf::AllowedRange;
4use crate::elf::PAGE_MASK_4KB;
5use crate::elf::PageMask;
6use crate::elf::RelocationKind;
7use crate::elf::RelocationKindInfo;
8use crate::elf::RelocationSize;
9use crate::elf::Sign;
10use crate::relaxation::RelocationModifier;
11use crate::utils::and_from_slice;
12use crate::utils::or_from_slice;
13use crate::utils::u32_from_slice;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum RelaxationKind {
17    /// Leave the instruction alone. Used when we only want to change the kind of relocation used.
18    NoOp,
19
20    /// Replace with nop
21    ReplaceWithNop,
22
23    /// Replace with movz x0 lsl #16
24    MovzX0Lsl16,
25
26    /// Replace with movk x0
27    MovkX0,
28
29    /// Replace with movz xn lsl #16
30    MovzXnLsl16,
31
32    /// Replace with movk xn
33    MovkXn,
34
35    /// Replace adrp with adr
36    AdrpToAdr,
37
38    /// Replace with adrp x0 with adr
39    AdrpX0,
40
41    /// Replace with ldr x0
42    LdrX0,
43
44    /// Replace add with adr. We don't apply this, but lld does, so this is used by linker-diff.
45    AddToAdr,
46    LdrToAdr,
47}
48
49impl RelaxationKind {
50    pub fn apply(self, section_bytes: &mut [u8], offset_in_section: &mut u64, _addend: &mut i64) {
51        let offset = *offset_in_section as usize;
52        match self {
53            RelaxationKind::NoOp => {}
54            RelaxationKind::ReplaceWithNop => {
55                section_bytes[offset..offset + 4].copy_from_slice(&[
56                    0x1f, 0x20, 0x03, 0xd5, // nop
57                ]);
58            }
59            RelaxationKind::MovzX0Lsl16 => {
60                section_bytes[offset..offset + 4].copy_from_slice(&[
61                    0x0, 0x0, 0xa0, 0xd2, // movz x0, ${offset}, lsl #16
62                ]);
63            }
64            RelaxationKind::MovkX0 => {
65                section_bytes[offset..offset + 4].copy_from_slice(&[
66                    0x0, 0x0, 0x80, 0xf2, // movk x0, ${offset}
67                ]);
68            }
69            RelaxationKind::MovzXnLsl16 => {
70                let reg = u64::from(u32_from_slice(&section_bytes[offset..offset + 4]))
71                    .extract_bit_range(0..5) as u8;
72                section_bytes[offset..offset + 4].copy_from_slice(&[
73                    reg, 0x0, 0xa0, 0xd2, // movz x{reg}, ${offset}, lsl #16
74                ]);
75            }
76            RelaxationKind::MovkXn => {
77                let raw = u64::from(u32_from_slice(&section_bytes[offset..offset + 4]));
78                let dst_reg = raw.extract_bit_range(0..5) as u8;
79                let src_reg = raw.extract_bit_range(5..10) as u8;
80                debug_assert_eq!(
81                    src_reg, dst_reg,
82                    "Source and destination registers must be equal"
83                );
84                section_bytes[offset..offset + 4].copy_from_slice(&[
85                    dst_reg, 0x0, 0x80, 0xf2, // movk x{dst}, ${offset}
86                ]);
87            }
88            RelaxationKind::AdrpToAdr => {
89                // Clear the op bit of the instruction. See C6.2.12 and C6.2.13.
90                section_bytes[offset + 3] &= !0x80;
91            }
92            RelaxationKind::AddToAdr => {
93                section_bytes[offset + 3] &= !0x89;
94            }
95            RelaxationKind::LdrToAdr => {
96                section_bytes[offset + 3] &= !0x89;
97            }
98            RelaxationKind::AdrpX0 => {
99                section_bytes[offset..offset + 4].copy_from_slice(&[
100                    0x0, 0x0, 0x0, 0x90, // adrp x0 {addr}
101                ]);
102            }
103            RelaxationKind::LdrX0 => {
104                section_bytes[offset..offset + 4].copy_from_slice(&[
105                    0x0, 0x0, 0x40, 0xf9, // x0, [x0, {addr}]
106                ]);
107            }
108        }
109    }
110
111    #[must_use]
112    pub fn next_modifier(&self) -> RelocationModifier {
113        RelocationModifier::Normal
114    }
115}
116
117#[must_use]
118pub const fn relocation_type_from_raw(r_type: u32) -> Option<RelocationKindInfo> {
119    let (kind, size, mask, range, alignment) = match r_type {
120        // 5.7.4   Static miscellaneous relocations
121        object::elf::R_AARCH64_NONE => (
122            RelocationKind::None,
123            RelocationSize::ByteSize(0),
124            None,
125            AllowedRange::no_check(),
126            1,
127        ),
128
129        // 5.7.5   Static Data relocations
130        // Data relocations
131        object::elf::R_AARCH64_ABS64 => (
132            RelocationKind::Absolute,
133            RelocationSize::ByteSize(8),
134            None,
135            AllowedRange::no_check(),
136            1,
137        ),
138        object::elf::R_AARCH64_ABS32 => (
139            RelocationKind::Absolute,
140            RelocationSize::ByteSize(4),
141            None,
142            // Can represent signed or unsigned value.
143            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
144            1,
145        ),
146        object::elf::R_AARCH64_ABS16 => (
147            RelocationKind::Absolute,
148            RelocationSize::ByteSize(2),
149            None,
150            // Can represent signed or unsigned value.
151            AllowedRange::new(-(2i64.pow(15)), 2i64.pow(16)),
152            1,
153        ),
154        object::elf::R_AARCH64_PREL64 => (
155            RelocationKind::Relative,
156            RelocationSize::ByteSize(8),
157            None,
158            AllowedRange::no_check(),
159            1,
160        ),
161        object::elf::R_AARCH64_PREL32 => (
162            RelocationKind::Relative,
163            RelocationSize::ByteSize(4),
164            None,
165            // Can represent signed or unsigned value.
166            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
167            1,
168        ),
169        object::elf::R_AARCH64_PREL16 => (
170            RelocationKind::Relative,
171            RelocationSize::ByteSize(2),
172            None,
173            // Can represent signed or unsigned value.
174            AllowedRange::new(-(2i64.pow(15)), 2i64.pow(16)),
175            1,
176        ),
177        object::elf::R_AARCH64_PLT32 => (
178            RelocationKind::PltRelative,
179            RelocationSize::ByteSize(4),
180            None,
181            // Can represent signed or unsigned value.
182            AllowedRange::from_bit_size(32, Sign::Signed),
183            1,
184        ),
185
186        // 5.7.6   Static AArch64 relocations
187        // Group relocations to create a 16-, 32-, 48-, or 64-bit unsigned data value or address
188        // inline
189        object::elf::R_AARCH64_MOVW_UABS_G0 => (
190            RelocationKind::Absolute,
191            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
192            None,
193            AllowedRange::from_bit_size(16, Sign::Unsigned),
194            1,
195        ),
196        object::elf::R_AARCH64_MOVW_UABS_G0_NC => (
197            RelocationKind::Absolute,
198            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
199            None,
200            AllowedRange::no_check(),
201            1,
202        ),
203        object::elf::R_AARCH64_MOVW_UABS_G1 => (
204            RelocationKind::Absolute,
205            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
206            None,
207            AllowedRange::from_bit_size(32, Sign::Unsigned),
208            1,
209        ),
210        object::elf::R_AARCH64_MOVW_UABS_G1_NC => (
211            RelocationKind::Absolute,
212            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
213            None,
214            AllowedRange::no_check(),
215            1,
216        ),
217        object::elf::R_AARCH64_MOVW_UABS_G2 => (
218            RelocationKind::Absolute,
219            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movkz),
220            None,
221            AllowedRange::from_bit_size(48, Sign::Unsigned),
222            1,
223        ),
224        object::elf::R_AARCH64_MOVW_UABS_G2_NC => (
225            RelocationKind::Absolute,
226            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movkz),
227            None,
228            AllowedRange::no_check(),
229            1,
230        ),
231        object::elf::R_AARCH64_MOVW_UABS_G3 => (
232            RelocationKind::Absolute,
233            RelocationSize::bit_mask_aarch64(48, 64, AArch64Instruction::Movkz),
234            None,
235            AllowedRange::no_check(),
236            1,
237        ),
238        // Group relocations to create a 16, 32, 48, or 64 bit signed data or offset value inline
239        object::elf::R_AARCH64_MOVW_SABS_G0 => (
240            RelocationKind::Absolute,
241            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movnz),
242            None,
243            AllowedRange::from_bit_size(17, Sign::Signed),
244            1,
245        ),
246        object::elf::R_AARCH64_MOVW_SABS_G1 => (
247            RelocationKind::Absolute,
248            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
249            None,
250            AllowedRange::from_bit_size(33, Sign::Signed),
251            1,
252        ),
253        object::elf::R_AARCH64_MOVW_SABS_G2 => (
254            RelocationKind::Absolute,
255            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movnz),
256            None,
257            AllowedRange::from_bit_size(49, Sign::Signed),
258            1,
259        ),
260        // Relocations to generate 19, 21 and 33 bit PC-relative addresses
261        object::elf::R_AARCH64_LD_PREL_LO19 => (
262            RelocationKind::Relative,
263            RelocationSize::bit_mask_aarch64(2, 21, AArch64Instruction::Ldr),
264            None,
265            AllowedRange::from_bit_size(21, Sign::Signed),
266            4,
267        ),
268        object::elf::R_AARCH64_ADR_PREL_LO21 => (
269            RelocationKind::Relative,
270            RelocationSize::bit_mask_aarch64(0, 21, AArch64Instruction::Adr),
271            None,
272            AllowedRange::from_bit_size(21, Sign::Signed),
273            1,
274        ),
275        object::elf::R_AARCH64_ADR_PREL_PG_HI21 => (
276            RelocationKind::Relative,
277            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
278            Some(PageMask::SymbolPlusAddendAndPosition(PAGE_MASK_4KB)),
279            AllowedRange::from_bit_size(33, Sign::Signed),
280            1,
281        ),
282        object::elf::R_AARCH64_ADR_PREL_PG_HI21_NC => (
283            RelocationKind::Relative,
284            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
285            Some(PageMask::SymbolPlusAddendAndPosition(PAGE_MASK_4KB)),
286            AllowedRange::no_check(),
287            1,
288        ),
289        object::elf::R_AARCH64_ADD_ABS_LO12_NC => (
290            RelocationKind::AbsoluteLowPart,
291            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
292            None,
293            AllowedRange::no_check(),
294            1,
295        ),
296        object::elf::R_AARCH64_LDST8_ABS_LO12_NC => (
297            RelocationKind::AbsoluteLowPart,
298            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::LdSt),
299            None,
300            AllowedRange::no_check(),
301            1,
302        ),
303        object::elf::R_AARCH64_LDST16_ABS_LO12_NC => (
304            RelocationKind::AbsoluteLowPart,
305            RelocationSize::bit_mask_aarch64(1, 12, AArch64Instruction::LdSt),
306            None,
307            AllowedRange::no_check(),
308            2,
309        ),
310        object::elf::R_AARCH64_LDST32_ABS_LO12_NC => (
311            RelocationKind::AbsoluteLowPart,
312            RelocationSize::bit_mask_aarch64(2, 12, AArch64Instruction::LdSt),
313            None,
314            AllowedRange::no_check(),
315            4,
316        ),
317        object::elf::R_AARCH64_LDST64_ABS_LO12_NC => (
318            RelocationKind::AbsoluteLowPart,
319            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
320            None,
321            AllowedRange::no_check(),
322            8,
323        ),
324        object::elf::R_AARCH64_LDST128_ABS_LO12_NC => (
325            RelocationKind::AbsoluteLowPart,
326            RelocationSize::bit_mask_aarch64(4, 12, AArch64Instruction::LdSt),
327            None,
328            AllowedRange::no_check(),
329            16,
330        ),
331
332        // Relocations for control-flow instructions - all offsets are a multiple of 4
333        object::elf::R_AARCH64_TSTBR14 => (
334            RelocationKind::Relative,
335            RelocationSize::bit_mask_aarch64(2, 16, AArch64Instruction::TstBr),
336            None,
337            AllowedRange::from_bit_size(16, Sign::Signed),
338            4,
339        ),
340        object::elf::R_AARCH64_CONDBR19 => (
341            RelocationKind::Relative,
342            RelocationSize::bit_mask_aarch64(2, 21, AArch64Instruction::Bcond),
343            None,
344            AllowedRange::from_bit_size(21, Sign::Signed),
345            4,
346        ),
347        object::elf::R_AARCH64_JUMP26 => (
348            RelocationKind::PltRelative,
349            RelocationSize::bit_mask_aarch64(2, 28, AArch64Instruction::JumpCall),
350            None,
351            AllowedRange::from_bit_size(28, Sign::Signed),
352            4,
353        ),
354        object::elf::R_AARCH64_CALL26 => (
355            RelocationKind::PltRelative,
356            RelocationSize::bit_mask_aarch64(2, 28, AArch64Instruction::JumpCall),
357            None,
358            AllowedRange::from_bit_size(28, Sign::Signed),
359            4,
360        ),
361
362        // Group relocations to create a 16, 32, 48, or 64 bit PC-relative offset inline
363        object::elf::R_AARCH64_MOVW_PREL_G0 => (
364            RelocationKind::Relative,
365            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movnz),
366            None,
367            AllowedRange::no_check(),
368            1,
369        ),
370        object::elf::R_AARCH64_MOVW_PREL_G0_NC => (
371            RelocationKind::Relative,
372            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
373            None,
374            AllowedRange::no_check(),
375            1,
376        ),
377        object::elf::R_AARCH64_MOVW_PREL_G1 => (
378            RelocationKind::Relative,
379            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
380            None,
381            AllowedRange::no_check(),
382            1,
383        ),
384        object::elf::R_AARCH64_MOVW_PREL_G1_NC => (
385            RelocationKind::Relative,
386            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
387            None,
388            AllowedRange::no_check(),
389            1,
390        ),
391        object::elf::R_AARCH64_MOVW_PREL_G2 => (
392            RelocationKind::Relative,
393            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movnz),
394            None,
395            AllowedRange::no_check(),
396            1,
397        ),
398        object::elf::R_AARCH64_MOVW_PREL_G2_NC => (
399            RelocationKind::Relative,
400            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movkz),
401            None,
402            AllowedRange::no_check(),
403            1,
404        ),
405        object::elf::R_AARCH64_MOVW_PREL_G3 => (
406            RelocationKind::Relative,
407            RelocationSize::bit_mask_aarch64(48, 64, AArch64Instruction::Movnz),
408            None,
409            AllowedRange::no_check(),
410            1,
411        ),
412
413        // Group relocations to create a 16, 32, 48, or 64 bit GOT-relative offsets inline
414        object::elf::R_AARCH64_MOVW_GOTOFF_G0 => (
415            RelocationKind::GotRelGotBase,
416            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movnz),
417            None,
418            AllowedRange::no_check(),
419            1,
420        ),
421        object::elf::R_AARCH64_MOVW_GOTOFF_G0_NC => (
422            RelocationKind::GotRelGotBase,
423            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
424            None,
425            AllowedRange::no_check(),
426            1,
427        ),
428        object::elf::R_AARCH64_MOVW_GOTOFF_G1 => (
429            RelocationKind::GotRelGotBase,
430            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
431            None,
432            AllowedRange::no_check(),
433            1,
434        ),
435        object::elf::R_AARCH64_MOVW_GOTOFF_G1_NC => (
436            RelocationKind::GotRelGotBase,
437            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
438            None,
439            AllowedRange::no_check(),
440            1,
441        ),
442        object::elf::R_AARCH64_MOVW_GOTOFF_G2 => (
443            RelocationKind::GotRelGotBase,
444            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movnz),
445            None,
446            AllowedRange::no_check(),
447            1,
448        ),
449        object::elf::R_AARCH64_MOVW_GOTOFF_G2_NC => (
450            RelocationKind::GotRelGotBase,
451            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movkz),
452            None,
453            AllowedRange::no_check(),
454            1,
455        ),
456        object::elf::R_AARCH64_MOVW_GOTOFF_G3 => (
457            RelocationKind::GotRelGotBase,
458            RelocationSize::bit_mask_aarch64(48, 64, AArch64Instruction::Movnz),
459            None,
460            AllowedRange::no_check(),
461            1,
462        ),
463
464        // GOT-relative data relocations
465        object::elf::R_AARCH64_GOTREL64 => (
466            RelocationKind::SymRelGotBase,
467            RelocationSize::ByteSize(4),
468            None,
469            AllowedRange::no_check(),
470            1,
471        ),
472        object::elf::R_AARCH64_GOTREL32 => (
473            RelocationKind::SymRelGotBase,
474            RelocationSize::ByteSize(8),
475            None,
476            AllowedRange::from_bit_size(32, Sign::Signed),
477            1,
478        ),
479        object::elf::R_AARCH64_GOTPCREL32 => (
480            RelocationKind::GotRelative,
481            RelocationSize::ByteSize(4),
482            None,
483            AllowedRange::from_bit_size(32, Sign::Signed),
484            1,
485        ),
486        object::elf::R_AARCH64_GOT_LD_PREL19 => (
487            RelocationKind::GotRelative,
488            RelocationSize::bit_mask_aarch64(2, 21, AArch64Instruction::LdSt),
489            None,
490            AllowedRange::from_bit_size(21, Sign::Signed),
491            4,
492        ),
493        object::elf::R_AARCH64_LD64_GOTOFF_LO15 => (
494            RelocationKind::GotRelGotBase,
495            RelocationSize::bit_mask_aarch64(3, 15, AArch64Instruction::LdSt),
496            None,
497            AllowedRange::from_bit_size(15, Sign::Unsigned),
498            8,
499        ),
500        object::elf::R_AARCH64_ADR_GOT_PAGE => (
501            RelocationKind::GotRelative,
502            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
503            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
504            AllowedRange::from_bit_size(33, Sign::Signed),
505            1,
506        ),
507        object::elf::R_AARCH64_LD64_GOT_LO12_NC => (
508            RelocationKind::Got,
509            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
510            None,
511            AllowedRange::no_check(),
512            8,
513        ),
514        object::elf::R_AARCH64_LD64_GOTPAGE_LO15 => (
515            RelocationKind::GotRelGotBase,
516            RelocationSize::bit_mask_aarch64(3, 15, AArch64Instruction::LdSt),
517            Some(PageMask::GotBase(PAGE_MASK_4KB)),
518            AllowedRange::from_bit_size(15, Sign::Unsigned),
519            8,
520        ),
521
522        // 5.7.11.1   General Dynamic thread-local storage model
523        object::elf::R_AARCH64_TLSGD_ADR_PREL21 => (
524            RelocationKind::TlsGd,
525            RelocationSize::bit_mask_aarch64(0, 21, AArch64Instruction::Adr),
526            None,
527            AllowedRange::from_bit_size(21, Sign::Signed),
528            1,
529        ),
530        object::elf::R_AARCH64_TLSGD_ADR_PAGE21 => (
531            RelocationKind::TlsGd,
532            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
533            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
534            AllowedRange::from_bit_size(33, Sign::Signed),
535            1,
536        ),
537        object::elf::R_AARCH64_TLSGD_ADD_LO12_NC => (
538            RelocationKind::TlsGdGot,
539            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
540            None,
541            AllowedRange::no_check(),
542            1,
543        ),
544        object::elf::R_AARCH64_TLSGD_MOVW_G1 => (
545            RelocationKind::TlsGdGotBase,
546            RelocationSize::bit_mask_aarch64(16, 33, AArch64Instruction::Movnz),
547            None,
548            AllowedRange::no_check(),
549            1,
550        ),
551        object::elf::R_AARCH64_TLSGD_MOVW_G0_NC => (
552            RelocationKind::TlsGdGotBase,
553            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
554            None,
555            AllowedRange::no_check(),
556            1,
557        ),
558
559        // 5.7.11.2   Local Dynamic thread-local storage model
560        object::elf::R_AARCH64_TLSLD_ADR_PREL21 => (
561            RelocationKind::TlsLd,
562            RelocationSize::bit_mask_aarch64(0, 21, AArch64Instruction::Adr),
563            None,
564            AllowedRange::from_bit_size(21, Sign::Signed),
565            1,
566        ),
567        object::elf::R_AARCH64_TLSLD_ADR_PAGE21 => (
568            RelocationKind::TlsLd,
569            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
570            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
571            AllowedRange::from_bit_size(33, Sign::Signed),
572            1,
573        ),
574        object::elf::R_AARCH64_TLSLD_ADD_LO12_NC => (
575            RelocationKind::TlsLdGot,
576            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
577            None,
578            AllowedRange::no_check(),
579            1,
580        ),
581        object::elf::R_AARCH64_TLSLD_MOVW_G1 => (
582            RelocationKind::TlsLdGotBase,
583            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
584            None,
585            AllowedRange::no_check(),
586            1,
587        ),
588        object::elf::R_AARCH64_TLSLD_MOVW_G0_NC => (
589            RelocationKind::TlsLdGotBase,
590            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
591            None,
592            AllowedRange::no_check(),
593            1,
594        ),
595        object::elf::R_AARCH64_TLSLD_LD_PREL19 => (
596            RelocationKind::TlsLd,
597            RelocationSize::bit_mask_aarch64(0, 21, AArch64Instruction::Ldr),
598            None,
599            AllowedRange::from_bit_size(21, Sign::Signed),
600            1,
601        ),
602        object::elf::R_AARCH64_TLSLD_MOVW_DTPREL_G2 => (
603            RelocationKind::DtpOff,
604            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movnz),
605            None,
606            AllowedRange::no_check(),
607            1,
608        ),
609        object::elf::R_AARCH64_TLSLD_MOVW_DTPREL_G1 => (
610            RelocationKind::DtpOff,
611            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
612            None,
613            AllowedRange::no_check(),
614            1,
615        ),
616        object::elf::R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC => (
617            RelocationKind::DtpOff,
618            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
619            None,
620            AllowedRange::no_check(),
621            1,
622        ),
623        object::elf::R_AARCH64_TLSLD_MOVW_DTPREL_G0 => (
624            RelocationKind::DtpOff,
625            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movnz),
626            None,
627            AllowedRange::no_check(),
628            1,
629        ),
630        object::elf::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC => (
631            RelocationKind::DtpOff,
632            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
633            None,
634            AllowedRange::no_check(),
635            1,
636        ),
637        object::elf::R_AARCH64_TLSLD_ADD_DTPREL_HI12 => (
638            RelocationKind::DtpOff,
639            RelocationSize::bit_mask_aarch64(12, 24, AArch64Instruction::Add),
640            None,
641            AllowedRange::from_bit_size(24, Sign::Unsigned),
642            1,
643        ),
644        object::elf::R_AARCH64_TLSLD_ADD_DTPREL_LO12 => (
645            RelocationKind::DtpOff,
646            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
647            None,
648            AllowedRange::from_bit_size(12, Sign::Unsigned),
649            1,
650        ),
651        object::elf::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC => (
652            RelocationKind::DtpOff,
653            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
654            None,
655            AllowedRange::no_check(),
656            1,
657        ),
658        object::elf::R_AARCH64_TLSLD_LDST8_DTPREL_LO12 => (
659            RelocationKind::DtpOff,
660            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::LdSt),
661            None,
662            AllowedRange::from_bit_size(12, Sign::Unsigned),
663            1,
664        ),
665        object::elf::R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC => (
666            RelocationKind::DtpOff,
667            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::LdSt),
668            None,
669            AllowedRange::no_check(),
670            1,
671        ),
672        object::elf::R_AARCH64_TLSLD_LDST16_DTPREL_LO12 => (
673            RelocationKind::DtpOff,
674            RelocationSize::bit_mask_aarch64(1, 12, AArch64Instruction::LdSt),
675            None,
676            AllowedRange::from_bit_size(12, Sign::Unsigned),
677            2,
678        ),
679        object::elf::R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC => (
680            RelocationKind::DtpOff,
681            RelocationSize::bit_mask_aarch64(1, 12, AArch64Instruction::LdSt),
682            None,
683            AllowedRange::no_check(),
684            2,
685        ),
686        object::elf::R_AARCH64_TLSLD_LDST32_DTPREL_LO12 => (
687            RelocationKind::DtpOff,
688            RelocationSize::bit_mask_aarch64(2, 12, AArch64Instruction::LdSt),
689            None,
690            AllowedRange::from_bit_size(12, Sign::Unsigned),
691            4,
692        ),
693        object::elf::R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC => (
694            RelocationKind::DtpOff,
695            RelocationSize::bit_mask_aarch64(2, 12, AArch64Instruction::LdSt),
696            None,
697            AllowedRange::no_check(),
698            4,
699        ),
700        object::elf::R_AARCH64_TLSLD_LDST64_DTPREL_LO12 => (
701            RelocationKind::DtpOff,
702            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
703            None,
704            AllowedRange::from_bit_size(12, Sign::Unsigned),
705            8,
706        ),
707        object::elf::R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC => (
708            RelocationKind::DtpOff,
709            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
710            None,
711            AllowedRange::no_check(),
712            8,
713        ),
714        object::elf::R_AARCH64_TLSLD_LDST128_DTPREL_LO12 => (
715            RelocationKind::DtpOff,
716            RelocationSize::bit_mask_aarch64(4, 12, AArch64Instruction::LdSt),
717            None,
718            AllowedRange::from_bit_size(12, Sign::Unsigned),
719            16,
720        ),
721
722        object::elf::R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC => (
723            RelocationKind::DtpOff,
724            RelocationSize::bit_mask_aarch64(4, 12, AArch64Instruction::LdSt),
725            None,
726            AllowedRange::no_check(),
727            16,
728        ),
729
730        // 5.7.11.3   Initial Exec thread-local storage model
731        object::elf::R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 => (
732            RelocationKind::GotTpOffGotBase,
733            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
734            None,
735            AllowedRange::no_check(),
736            1,
737        ),
738        object::elf::R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC => (
739            RelocationKind::GotTpOffGotBase,
740            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
741            None,
742            AllowedRange::no_check(),
743            1,
744        ),
745        object::elf::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 => (
746            RelocationKind::GotTpOff,
747            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
748            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
749            AllowedRange::from_bit_size(33, Sign::Signed),
750            1,
751        ),
752        object::elf::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC => (
753            RelocationKind::GotTpOffGot,
754            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdrRegister),
755            None,
756            AllowedRange::no_check(),
757            8,
758        ),
759        object::elf::R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 => (
760            RelocationKind::GotTpOff,
761            RelocationSize::bit_mask_aarch64(2, 21, AArch64Instruction::Ldr),
762            None,
763            AllowedRange::from_bit_size(21, Sign::Signed),
764            4,
765        ),
766
767        // 5.7.11.4   Local Exec thread-local storage model
768        object::elf::R_AARCH64_TLSLE_MOVW_TPREL_G2 => (
769            RelocationKind::TpOff,
770            RelocationSize::bit_mask_aarch64(32, 48, AArch64Instruction::Movnz),
771            None,
772            AllowedRange::no_check(),
773            1,
774        ),
775        object::elf::R_AARCH64_TLSLE_MOVW_TPREL_G1 => (
776            RelocationKind::TpOff,
777            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
778            None,
779            AllowedRange::no_check(),
780            1,
781        ),
782        object::elf::R_AARCH64_TLSLE_MOVW_TPREL_G1_NC => (
783            RelocationKind::TpOff,
784            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movkz),
785            None,
786            AllowedRange::no_check(),
787            1,
788        ),
789        object::elf::R_AARCH64_TLSLE_MOVW_TPREL_G0 => (
790            RelocationKind::TpOff,
791            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movnz),
792            None,
793            AllowedRange::no_check(),
794            1,
795        ),
796        object::elf::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC => (
797            RelocationKind::TpOff,
798            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
799            None,
800            AllowedRange::no_check(),
801            1,
802        ),
803        object::elf::R_AARCH64_TLSLE_ADD_TPREL_HI12 => (
804            RelocationKind::TpOff,
805            RelocationSize::bit_mask_aarch64(12, 24, AArch64Instruction::Add),
806            None,
807            AllowedRange::from_bit_size(24, Sign::Unsigned),
808            1,
809        ),
810        object::elf::R_AARCH64_TLSLE_ADD_TPREL_LO12 => (
811            RelocationKind::TpOff,
812            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
813            None,
814            AllowedRange::from_bit_size(12, Sign::Unsigned),
815            1,
816        ),
817        object::elf::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC => (
818            RelocationKind::TpOff,
819            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
820            None,
821            AllowedRange::no_check(),
822            1,
823        ),
824        object::elf::R_AARCH64_TLSLE_LDST8_TPREL_LO12 => (
825            RelocationKind::TpOff,
826            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::LdSt),
827            None,
828            AllowedRange::from_bit_size(12, Sign::Unsigned),
829            1,
830        ),
831        object::elf::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC => (
832            RelocationKind::TpOff,
833            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::LdSt),
834            None,
835            AllowedRange::no_check(),
836            1,
837        ),
838        object::elf::R_AARCH64_TLSLE_LDST16_TPREL_LO12 => (
839            RelocationKind::TpOff,
840            RelocationSize::bit_mask_aarch64(1, 12, AArch64Instruction::LdSt),
841            None,
842            AllowedRange::from_bit_size(12, Sign::Unsigned),
843            2,
844        ),
845
846        object::elf::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC => (
847            RelocationKind::TpOff,
848            RelocationSize::bit_mask_aarch64(1, 12, AArch64Instruction::LdSt),
849            None,
850            AllowedRange::no_check(),
851            2,
852        ),
853        object::elf::R_AARCH64_TLSLE_LDST32_TPREL_LO12 => (
854            RelocationKind::TpOff,
855            RelocationSize::bit_mask_aarch64(2, 12, AArch64Instruction::LdSt),
856            None,
857            AllowedRange::from_bit_size(12, Sign::Unsigned),
858            4,
859        ),
860
861        object::elf::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC => (
862            RelocationKind::TpOff,
863            RelocationSize::bit_mask_aarch64(2, 12, AArch64Instruction::LdSt),
864            None,
865            AllowedRange::no_check(),
866            4,
867        ),
868        object::elf::R_AARCH64_TLSLE_LDST64_TPREL_LO12 => (
869            RelocationKind::TpOff,
870            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
871            None,
872            AllowedRange::from_bit_size(12, Sign::Unsigned),
873            8,
874        ),
875        object::elf::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC => (
876            RelocationKind::TpOff,
877            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdSt),
878            None,
879            AllowedRange::no_check(),
880            8,
881        ),
882        object::elf::R_AARCH64_TLSLE_LDST128_TPREL_LO12 => (
883            RelocationKind::TpOff,
884            RelocationSize::bit_mask_aarch64(4, 12, AArch64Instruction::LdSt),
885            None,
886            AllowedRange::from_bit_size(12, Sign::Unsigned),
887            16,
888        ),
889
890        object::elf::R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC => (
891            RelocationKind::TpOff,
892            RelocationSize::bit_mask_aarch64(4, 12, AArch64Instruction::LdSt),
893            None,
894            AllowedRange::no_check(),
895            16,
896        ),
897
898        // 5.7.11.5 Thread-local storage descriptors
899        object::elf::R_AARCH64_TLSDESC_LD_PREL19 => (
900            RelocationKind::TlsDesc,
901            RelocationSize::bit_mask_aarch64(2, 21, AArch64Instruction::Ldr),
902            None,
903            AllowedRange::from_bit_size(21, Sign::Signed),
904            4,
905        ),
906        object::elf::R_AARCH64_TLSDESC_ADR_PREL21 => (
907            RelocationKind::TlsDesc,
908            RelocationSize::bit_mask_aarch64(0, 21, AArch64Instruction::Adr),
909            None,
910            AllowedRange::from_bit_size(21, Sign::Signed),
911            1,
912        ),
913        object::elf::R_AARCH64_TLSDESC_ADR_PAGE21 => (
914            RelocationKind::TlsDesc,
915            RelocationSize::bit_mask_aarch64(12, 33, AArch64Instruction::Adr),
916            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
917            AllowedRange::from_bit_size(33, Sign::Signed),
918            1,
919        ),
920
921        object::elf::R_AARCH64_TLSDESC_LD64_LO12 => (
922            RelocationKind::TlsDescGot,
923            RelocationSize::bit_mask_aarch64(3, 12, AArch64Instruction::LdrRegister),
924            None,
925            AllowedRange::no_check(),
926            8,
927        ),
928        object::elf::R_AARCH64_TLSDESC_ADD_LO12 => (
929            RelocationKind::TlsDescGot,
930            RelocationSize::bit_mask_aarch64(0, 12, AArch64Instruction::Add),
931            None,
932            AllowedRange::no_check(),
933            1,
934        ),
935        object::elf::R_AARCH64_TLSDESC_OFF_G1 => (
936            RelocationKind::TlsDescGotBase,
937            RelocationSize::bit_mask_aarch64(16, 32, AArch64Instruction::Movnz),
938            None,
939            AllowedRange::from_bit_size(33, Sign::Signed),
940            1,
941        ),
942        object::elf::R_AARCH64_TLSDESC_OFF_G0_NC => (
943            RelocationKind::TlsDescGotBase,
944            RelocationSize::bit_mask_aarch64(0, 16, AArch64Instruction::Movkz),
945            None,
946            AllowedRange::no_check(),
947            1,
948        ),
949
950        // Misc relocations
951        object::elf::R_AARCH64_TLSDESC_CALL => (
952            RelocationKind::TlsDescCall,
953            RelocationSize::ByteSize(0),
954            None,
955            AllowedRange::no_check(),
956            1,
957        ),
958
959        _ => return None,
960    };
961
962    Some(RelocationKindInfo {
963        kind,
964        size,
965        mask,
966        range,
967        alignment,
968        bias: 0,
969        thunkable: matches!(
970            r_type,
971            object::elf::R_AARCH64_CALL26 | object::elf::R_AARCH64_JUMP26
972        ),
973    })
974}
975
976impl AArch64Instruction {
977    // Encode computed relocation value and store it based on the encoding of an instruction.
978    // Each instruction links to a chapter in the Arm Architecture Reference Manual for A-profile
979    // architecture manual: https://developer.arm.com/documentation/ddi0487/latest/
980    pub fn write_to_value(self, extracted_value: u64, negative: bool, dest: &mut [u8]) {
981        let mut mask;
982        match self {
983            // C6.2.13
984            AArch64Instruction::Adr => {
985                mask = ((extracted_value.extract_bit_range(0..2) as u32) << 29)
986                    | ((extracted_value.extract_bit_range(2..32) as u32) << 5);
987            }
988            // C6.2.252, C6.2.254
989            AArch64Instruction::Movkz => {
990                mask = (extracted_value as u32) << 5;
991            }
992            // C6.2.253, C6.2.254
993            AArch64Instruction::Movnz => {
994                // Clear all bits except rd[4:0] and hw[22:21]
995                and_from_slice(dest, &0x0060_001F_u32.to_le_bytes());
996                let mut value = extracted_value as i64;
997                mask = 0u32;
998                if negative {
999                    value = !value;
1000                    // MOVN opcode: sf=1, opc=00, fixed=100101
1001                    mask |= 0x9280_0000;
1002                } else {
1003                    // MOVZ opcode: sf=1, opc=10, fixed=100101
1004                    mask |= 0xd280_0000;
1005                }
1006                mask |= ((value as u64).extract_bit_range(0..16) as u32) << 5;
1007            }
1008            // C6.2.192
1009            AArch64Instruction::Ldr => {
1010                mask = (extracted_value as u32) << 5;
1011            }
1012            AArch64Instruction::LdrRegister => {
1013                mask = (extracted_value as u32) << 10;
1014            }
1015            // C6.2.5
1016            AArch64Instruction::Add => {
1017                mask = (extracted_value as u32) << 10;
1018            }
1019            // C7.2.208, C6.2.383
1020            AArch64Instruction::LdSt => {
1021                mask = (extracted_value as u32) << 10;
1022            }
1023            // C6.2.438
1024            AArch64Instruction::TstBr => {
1025                mask = (extracted_value as u32) << 5;
1026            }
1027            // C6.2.34
1028            AArch64Instruction::Bcond => {
1029                mask = (extracted_value as u32) << 5;
1030            }
1031            // C6.2.33
1032            AArch64Instruction::JumpCall => {
1033                mask = extracted_value as u32;
1034            }
1035            AArch64Instruction::MachOLow12 => {
1036                // The relocation value is scaled by the access size for ADD, LDR and STR
1037                // instructions. The following logic is taken from LLVM
1038                // (encodePageOff12).
1039                let insn = u32_from_slice(&dest[0..4]);
1040                let mut scale = 0;
1041                if (insn & 0x3b00_0000) == 0x3900_0000 {
1042                    // load/store
1043                    scale = insn >> 30;
1044                    if scale == 0 && (insn & 0x0480_0000) == 0x0480_0000 {
1045                        // 128-bit variant
1046                        scale = 4;
1047                    }
1048                }
1049                mask = (extracted_value as u32) >> scale;
1050            }
1051        }
1052        // Read the original value and combine it with the prepared mask.
1053        or_from_slice(dest, &mask.to_le_bytes());
1054    }
1055
1056    /// The inverse of `write_to_value`. Returns `(extracted_value, negative)`. Supplied `bytes`
1057    /// must be at least 4 bytes, otherwise we panic.
1058    #[must_use]
1059    pub fn read_value(self, bytes: &[u8]) -> (u64, bool) {
1060        let mut negative = false;
1061        let value = u64::from(u32_from_slice(bytes));
1062        let extracted_value = match self {
1063            // C6.2.13
1064            AArch64Instruction::Adr => {
1065                (value >> 29).low_bits(2) | (((value >> 5).low_bits_signed(19)) << 2)
1066            }
1067            // C6.2.252, C6.2.254
1068            AArch64Instruction::Movkz => (value >> 5).low_bits_signed(16),
1069            // C6.2.253, C6.2.254
1070            AArch64Instruction::Movnz => {
1071                negative = (value & (1 << 30)) == 0;
1072                let v = (value >> 5).low_bits(16);
1073                if negative { !v } else { v }
1074            }
1075            // C6.2.192
1076            AArch64Instruction::Ldr => (value >> 5).low_bits_signed(19),
1077            // C6.2.193
1078            AArch64Instruction::LdrRegister => (value >> 10).low_bits(12),
1079            // C6.2.5
1080            AArch64Instruction::Add => (value >> 10).low_bits(12),
1081            // C7.2.208, C6.2.383
1082            AArch64Instruction::LdSt => (value >> 10).low_bits_signed(12),
1083            // C6.2.438
1084            AArch64Instruction::TstBr => (value >> 5).low_bits_signed(14),
1085            // C6.2.34
1086            AArch64Instruction::Bcond => (value >> 5).low_bits_signed(19),
1087            // C6.2.33
1088            AArch64Instruction::JumpCall => value.low_bits_signed(26),
1089            AArch64Instruction::MachOLow12 => todo!(),
1090        };
1091
1092        (extracted_value, negative)
1093    }
1094}