Skip to main content

linker_utils/
loongarch64.rs

1use crate::elf::AllowedRange;
2use crate::elf::LoongArch64Instruction;
3use crate::elf::PAGE_MASK_4KB;
4use crate::elf::PageMask;
5use crate::elf::RelocationKind;
6use crate::elf::RelocationKindInfo;
7use crate::elf::RelocationSize;
8use crate::elf::SIZE_2GB;
9use crate::elf::SIZE_2KB;
10use crate::elf::SIZE_4GB;
11use crate::elf::SIZE_4KB;
12use crate::elf::Sign;
13use crate::relaxation::RelocationModifier;
14use crate::utils::and_from_slice;
15use crate::utils::or_from_slice;
16use crate::utils::u32_from_slice;
17use crate::utils::u64_from_slice;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum RelaxationKind {
21    /// Leave the instruction alone. Used when we only want to change the kind of relocation used.
22    NoOp,
23
24    /// Replace with nop
25    ReplaceWithNop,
26}
27
28impl RelaxationKind {
29    pub fn apply(self, section_bytes: &mut [u8], offset_in_section: &mut u64, _addend: &mut i64) {
30        let offset = *offset_in_section as usize;
31        match self {
32            RelaxationKind::NoOp => {}
33            RelaxationKind::ReplaceWithNop => {
34                section_bytes[offset..offset + 4].copy_from_slice(&[
35                    0x03, 0x40, 0x0, 0x0, // nop
36                ]);
37            }
38        }
39    }
40
41    #[must_use]
42    pub fn next_modifier(&self) -> RelocationModifier {
43        RelocationModifier::Normal
44    }
45}
46
47#[must_use]
48pub const fn relocation_type_from_raw(r_type: u32) -> Option<RelocationKindInfo> {
49    // The relocation listing following the order defined in the standard:
50    // https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#relocation-types
51    let (kind, size, mask, range, alignment, bias) = match r_type {
52        object::elf::R_LARCH_NONE => (
53            RelocationKind::None,
54            RelocationSize::ByteSize(0),
55            None,
56            AllowedRange::no_check(),
57            1,
58            0,
59        ),
60        // Addition and subtraction relocations.
61        object::elf::R_LARCH_32 => (
62            RelocationKind::Absolute,
63            RelocationSize::ByteSize(4),
64            None,
65            // Can represent signed or unsigned value.
66            AllowedRange::new(-(2i64.pow(31)), 2i64.pow(32)),
67            1,
68            0,
69        ),
70        object::elf::R_LARCH_64 => (
71            RelocationKind::Absolute,
72            RelocationSize::ByteSize(8),
73            None,
74            AllowedRange::no_check(),
75            1,
76            0,
77        ),
78        object::elf::R_LARCH_ADD6 => (
79            RelocationKind::AbsoluteAdditionWord6,
80            RelocationSize::ByteSize(1),
81            None,
82            AllowedRange::no_check(),
83            1,
84            0,
85        ),
86        object::elf::R_LARCH_ADD8 => (
87            RelocationKind::AbsoluteAddition,
88            RelocationSize::ByteSize(1),
89            None,
90            AllowedRange::no_check(),
91            1,
92            0,
93        ),
94        object::elf::R_LARCH_ADD16 => (
95            RelocationKind::AbsoluteAddition,
96            RelocationSize::ByteSize(2),
97            None,
98            AllowedRange::no_check(),
99            1,
100            0,
101        ),
102        object::elf::R_LARCH_ADD24 => (
103            RelocationKind::AbsoluteAddition,
104            RelocationSize::ByteSize(3),
105            None,
106            AllowedRange::no_check(),
107            1,
108            0,
109        ),
110        object::elf::R_LARCH_ADD32 => (
111            RelocationKind::AbsoluteAddition,
112            RelocationSize::ByteSize(4),
113            None,
114            AllowedRange::no_check(),
115            1,
116            0,
117        ),
118        object::elf::R_LARCH_ADD64 => (
119            RelocationKind::AbsoluteAddition,
120            RelocationSize::ByteSize(8),
121            None,
122            AllowedRange::no_check(),
123            1,
124            0,
125        ),
126        object::elf::R_LARCH_SUB6 => (
127            RelocationKind::AbsoluteSubtractionWord6,
128            RelocationSize::ByteSize(1),
129            None,
130            AllowedRange::no_check(),
131            1,
132            0,
133        ),
134        object::elf::R_LARCH_SUB8 => (
135            RelocationKind::AbsoluteSubtraction,
136            RelocationSize::ByteSize(1),
137            None,
138            AllowedRange::no_check(),
139            1,
140            0,
141        ),
142        object::elf::R_LARCH_SUB16 => (
143            RelocationKind::AbsoluteSubtraction,
144            RelocationSize::ByteSize(2),
145            None,
146            AllowedRange::no_check(),
147            1,
148            0,
149        ),
150        object::elf::R_LARCH_SUB24 => (
151            RelocationKind::AbsoluteSubtraction,
152            RelocationSize::ByteSize(3),
153            None,
154            AllowedRange::no_check(),
155            1,
156            0,
157        ),
158        object::elf::R_LARCH_SUB32 => (
159            RelocationKind::AbsoluteSubtraction,
160            RelocationSize::ByteSize(4),
161            None,
162            AllowedRange::no_check(),
163            1,
164            0,
165        ),
166        object::elf::R_LARCH_SUB64 => (
167            RelocationKind::AbsoluteSubtraction,
168            RelocationSize::ByteSize(8),
169            None,
170            AllowedRange::no_check(),
171            1,
172            0,
173        ),
174        // We process the subtraction in the SUB_ULEB128 relocation,
175        // thus we skip the first relocation in the pair.
176        object::elf::R_LARCH_ADD_ULEB128 => (
177            RelocationKind::Relative,
178            RelocationSize::ByteSize(0),
179            None,
180            AllowedRange::no_check(),
181            1,
182            0,
183        ),
184        object::elf::R_LARCH_SUB_ULEB128 => (
185            RelocationKind::PairSubtractionULEB128(object::elf::R_LARCH_ADD_ULEB128),
186            RelocationSize::ByteSize(8),
187            None,
188            AllowedRange::no_check(),
189            1,
190            0,
191        ),
192        // General relocations
193        object::elf::R_LARCH_ABS_HI20 => (
194            RelocationKind::Absolute,
195            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
196            None,
197            AllowedRange::no_check(),
198            1,
199            0,
200        ),
201        object::elf::R_LARCH_ABS_LO12 => (
202            RelocationKind::Absolute,
203            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
204            None,
205            AllowedRange::no_check(),
206            1,
207            0,
208        ),
209        object::elf::R_LARCH_ABS64_HI12 => (
210            RelocationKind::Absolute,
211            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
212            None,
213            AllowedRange::no_check(),
214            1,
215            0,
216        ),
217        object::elf::R_LARCH_ABS64_LO20 => (
218            RelocationKind::Absolute,
219            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
220            None,
221            AllowedRange::no_check(),
222            1,
223            0,
224        ),
225        object::elf::R_LARCH_PCALA_HI20 => (
226            RelocationKind::Relative,
227            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
228            Some(PageMask::SymbolPlusAddendAndPosition(PAGE_MASK_4KB)),
229            AllowedRange::no_check(),
230            1,
231            SIZE_2KB,
232        ),
233        object::elf::R_LARCH_PCALA_LO12 => (
234            RelocationKind::AbsoluteLowPart,
235            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
236            None,
237            AllowedRange::no_check(),
238            1,
239            0,
240        ),
241        object::elf::R_LARCH_PCALA64_HI12 => (
242            RelocationKind::RelativeLoongArchHigh,
243            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
244            // Mark is applied directly in the relocation!
245            None,
246            AllowedRange::no_check(),
247            1,
248            0,
249        ),
250        object::elf::R_LARCH_PCALA64_LO20 => (
251            RelocationKind::RelativeLoongArchHigh,
252            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
253            // Mark is applied directly in the relocation!
254            None,
255            AllowedRange::no_check(),
256            1,
257            0,
258        ),
259        object::elf::R_LARCH_32_PCREL => (
260            RelocationKind::Relative,
261            RelocationSize::ByteSize(4),
262            None,
263            // with check 32-bit signed overflow
264            AllowedRange::from_bit_size(32, Sign::Signed),
265            1,
266            0,
267        ),
268        object::elf::R_LARCH_PCREL20_S2 => (
269            RelocationKind::Relative,
270            RelocationSize::bit_mask_loongarch64(2, 22, LoongArch64Instruction::Shift5),
271            None,
272            // with check 22-bit signed overflow and a multiple of 4
273            AllowedRange::from_bit_size(22, Sign::Signed),
274            4,
275            0,
276        ),
277        object::elf::R_LARCH_64_PCREL => (
278            RelocationKind::Relative,
279            RelocationSize::ByteSize(8),
280            None,
281            AllowedRange::no_check(),
282            1,
283            0,
284        ),
285        object::elf::R_LARCH_PCADD_HI20 => (
286            RelocationKind::Relative,
287            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
288            None,
289            AllowedRange::no_check(),
290            1,
291            SIZE_2KB,
292        ),
293        // GOT-relative relocations
294        object::elf::R_LARCH_GOT_PC_HI20 => (
295            RelocationKind::GotRelative,
296            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
297            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
298            AllowedRange::no_check(),
299            1,
300            SIZE_2KB,
301        ),
302        object::elf::R_LARCH_GOT_PC_LO12 => (
303            RelocationKind::Got,
304            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
305            None,
306            AllowedRange::no_check(),
307            1,
308            0,
309        ),
310        object::elf::R_LARCH_GOT64_PC_HI12 => (
311            RelocationKind::GotRelativeLoongArch64,
312            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
313            // Mark is applied directly in the relocation!
314            None,
315            AllowedRange::no_check(),
316            1,
317            0,
318        ),
319        object::elf::R_LARCH_GOT64_PC_LO20 => (
320            RelocationKind::GotRelativeLoongArch64,
321            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
322            // Mark is applied directly in the relocation!
323            None,
324            AllowedRange::no_check(),
325            1,
326            0,
327        ),
328        // CFG-related relocations.
329        object::elf::R_LARCH_B16 => (
330            RelocationKind::Relative,
331            RelocationSize::bit_mask_loongarch64(2, 18, LoongArch64Instruction::Shift10),
332            None,
333            // with check 18-bit signed overflow and a multiple of 4
334            AllowedRange::from_bit_size(18, Sign::Signed),
335            4,
336            0,
337        ),
338        object::elf::R_LARCH_B21 => (
339            RelocationKind::Relative,
340            RelocationSize::bit_mask_loongarch64(2, 23, LoongArch64Instruction::Branch21),
341            None,
342            // with check 23-bit signed overflow and a multiple of 4
343            AllowedRange::from_bit_size(23, Sign::Signed),
344            4,
345            0,
346        ),
347        object::elf::R_LARCH_B26 => (
348            RelocationKind::PltRelative,
349            RelocationSize::bit_mask_loongarch64(2, 28, LoongArch64Instruction::Branch26),
350            None,
351            // with check 28-bit signed overflow and a multiple of 4
352            AllowedRange::from_bit_size(28, Sign::Signed),
353            4,
354            0,
355        ),
356        object::elf::R_LARCH_CALL36 => (
357            RelocationKind::Relative,
358            RelocationSize::bit_mask_loongarch64(2, 38, LoongArch64Instruction::Call36),
359            None,
360            // with check 38-bit signed overflow and a multiple of 4
361            AllowedRange::from_bit_size(38, Sign::Signed),
362            4,
363            0,
364        ),
365        object::elf::R_LARCH_CALL30 => (
366            RelocationKind::Relative,
367            RelocationSize::bit_mask_loongarch64(2, 19, LoongArch64Instruction::Call30),
368            None,
369            AllowedRange::no_check(),
370            1,
371            0,
372        ),
373        // TLS-related relocations (traditional).
374        object::elf::R_LARCH_TLS_LE_HI20 => (
375            RelocationKind::TpOff,
376            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
377            None,
378            AllowedRange::no_check(),
379            1,
380            0,
381        ),
382        object::elf::R_LARCH_TLS_LE_LO12 => (
383            RelocationKind::TpOff,
384            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
385            None,
386            AllowedRange::no_check(),
387            1,
388            0,
389        ),
390        object::elf::R_LARCH_TLS_LE64_LO20 => (
391            RelocationKind::TpOff,
392            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
393            None,
394            AllowedRange::no_check(),
395            1,
396            0,
397        ),
398        object::elf::R_LARCH_TLS_LE64_HI12 => (
399            RelocationKind::TpOff,
400            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
401            None,
402            AllowedRange::no_check(),
403            1,
404            0,
405        ),
406        object::elf::R_LARCH_TLS_LE_HI20_R => (
407            RelocationKind::TpOff,
408            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
409            None,
410            AllowedRange::no_check(),
411            1,
412            SIZE_2KB,
413        ),
414        object::elf::R_LARCH_TLS_LE_LO12_R => (
415            RelocationKind::TpOff,
416            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
417            None,
418            AllowedRange::no_check(),
419            1,
420            0,
421        ),
422        object::elf::R_LARCH_TLS_IE_PC_HI20 => (
423            RelocationKind::GotTpOff,
424            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
425            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
426            AllowedRange::no_check(),
427            1,
428            SIZE_2KB,
429        ),
430        object::elf::R_LARCH_TLS_IE_PC_LO12 => (
431            RelocationKind::GotTpOffGot,
432            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
433            None,
434            AllowedRange::no_check(),
435            1,
436            0,
437        ),
438        object::elf::R_LARCH_TLS_IE64_PC_LO20 => (
439            RelocationKind::GotTpOffLoongArch64,
440            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
441            // Mark is applied directly in the relocation!
442            None,
443            AllowedRange::no_check(),
444            1,
445            0,
446        ),
447        object::elf::R_LARCH_TLS_IE64_PC_HI12 => (
448            RelocationKind::GotTpOffLoongArch64,
449            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
450            // Mark is applied directly in the relocation!
451            None,
452            AllowedRange::no_check(),
453            1,
454            0,
455        ),
456        object::elf::R_LARCH_TLS_IE_HI20 => (
457            RelocationKind::GotTpOffGot,
458            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
459            None,
460            AllowedRange::no_check(),
461            1,
462            0,
463        ),
464        object::elf::R_LARCH_TLS_IE_LO12 => (
465            RelocationKind::GotTpOffGot,
466            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
467            None,
468            AllowedRange::no_check(),
469            1,
470            0,
471        ),
472        object::elf::R_LARCH_TLS_IE64_HI12 => (
473            RelocationKind::GotTpOffGot,
474            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
475            None,
476            AllowedRange::no_check(),
477            1,
478            0,
479        ),
480        object::elf::R_LARCH_TLS_IE64_LO20 => (
481            RelocationKind::GotTpOffGot,
482            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
483            None,
484            AllowedRange::no_check(),
485            1,
486            0,
487        ),
488        // It's a known limitation the ABI does not properly support TLS LD:
489        // https://github.com/loongson/la-abi-specs/issues/19
490        object::elf::R_LARCH_TLS_LD_PC_HI20 => (
491            RelocationKind::TlsGd,
492            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
493            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
494            AllowedRange::no_check(),
495            1,
496            SIZE_2KB,
497        ),
498        object::elf::R_LARCH_TLS_LD_HI20 => (
499            RelocationKind::TlsGdGot,
500            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
501            None,
502            AllowedRange::no_check(),
503            1,
504            0,
505        ),
506        object::elf::R_LARCH_TLS_GD_PC_HI20 => (
507            RelocationKind::TlsGd,
508            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
509            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
510            AllowedRange::no_check(),
511            1,
512            SIZE_2KB,
513        ),
514        object::elf::R_LARCH_TLS_GD_HI20 => (
515            RelocationKind::TlsGdGot,
516            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
517            None,
518            AllowedRange::no_check(),
519            1,
520            0,
521        ),
522        // TLS-related relocations (TLS descriptors).
523        object::elf::R_LARCH_TLS_DESC_PC_HI20 => (
524            RelocationKind::TlsDesc,
525            RelocationSize::bit_mask_loongarch64(12, 32, LoongArch64Instruction::Shift5),
526            Some(PageMask::GotEntryAndPosition(PAGE_MASK_4KB)),
527            AllowedRange::no_check(),
528            1,
529            SIZE_2KB,
530        ),
531        object::elf::R_LARCH_TLS_DESC_PC_LO12 => (
532            RelocationKind::TlsDescGot,
533            RelocationSize::bit_mask_loongarch64(0, 12, LoongArch64Instruction::Shift10),
534            None,
535            AllowedRange::no_check(),
536            1,
537            0,
538        ),
539        object::elf::R_LARCH_TLS_DESC64_PC_HI12 => (
540            RelocationKind::TlsDescLoongArch64,
541            RelocationSize::bit_mask_loongarch64(52, 64, LoongArch64Instruction::Shift10),
542            // Mark is applied directly in the relocation!
543            None,
544            AllowedRange::no_check(),
545            1,
546            0,
547        ),
548        object::elf::R_LARCH_TLS_DESC64_PC_LO20 => (
549            RelocationKind::TlsDescLoongArch64,
550            RelocationSize::bit_mask_loongarch64(32, 52, LoongArch64Instruction::Shift5),
551            // Mark is applied directly in the relocation!
552            None,
553            AllowedRange::no_check(),
554            1,
555            0,
556        ),
557        object::elf::R_LARCH_TLS_DESC_LD => (
558            RelocationKind::None,
559            RelocationSize::ByteSize(0),
560            None,
561            AllowedRange::no_check(),
562            1,
563            0,
564        ),
565        object::elf::R_LARCH_TLS_DESC_CALL => (
566            RelocationKind::TlsDescCall,
567            RelocationSize::ByteSize(0),
568            None,
569            AllowedRange::no_check(),
570            1,
571            0,
572        ),
573        // Debug info specific relocations.
574        object::elf::R_LARCH_TLS_DTPREL32 => (
575            RelocationKind::DtpOff,
576            RelocationSize::ByteSize(4),
577            None,
578            AllowedRange::no_check(),
579            1,
580            0,
581        ),
582        object::elf::R_LARCH_TLS_DTPREL64 => (
583            RelocationKind::DtpOff,
584            RelocationSize::ByteSize(8),
585            None,
586            AllowedRange::no_check(),
587            1,
588            0,
589        ),
590        // Unused relocations (https://github.com/loongson/la-abi-specs/issues/12).
591        object::elf::R_LARCH_TLS_LD_PCREL20_S2
592        | object::elf::R_LARCH_TLS_GD_PCREL20_S2
593        | object::elf::R_LARCH_TLS_DESC_PCREL20_S2 => {
594            return None;
595        }
596        // Misc relocations.
597        object::elf::R_LARCH_RELAX | object::elf::R_LARCH_TLS_LE_ADD_R => (
598            RelocationKind::None,
599            RelocationSize::ByteSize(0),
600            None,
601            AllowedRange::no_check(),
602            1,
603            0,
604        ),
605        object::elf::R_LARCH_ALIGN => (
606            RelocationKind::None,
607            RelocationSize::ByteSize(0),
608            None,
609            AllowedRange::no_check(),
610            1,
611            0,
612        ),
613        _ => return None,
614    };
615
616    Some(RelocationKindInfo {
617        kind,
618        size,
619        mask,
620        range,
621        alignment,
622        bias,
623        thunkable: false,
624    })
625}
626
627impl LoongArch64Instruction {
628    pub fn write_to_value(self, extracted_value: u64, _negative: bool, dest: &mut [u8]) {
629        match self {
630            LoongArch64Instruction::Shift5 => {
631                // j20 format: opcode[31:25], imm20[24:5], rd[4:0]
632                and_from_slice(dest, &0xFE00_001F_u32.to_le_bytes());
633                let mask = extracted_value << 5;
634                or_from_slice(dest, &(mask as u32).to_le_bytes());
635            }
636            LoongArch64Instruction::Shift10 => {
637                // k12 format: opcode[31:22], imm12[21:10], rj[9:5], rd[4:0]
638                and_from_slice(dest, &0xFFC0_03FF_u32.to_le_bytes());
639                let mask = extracted_value << 10;
640                or_from_slice(dest, &(mask as u32).to_le_bytes());
641            }
642            LoongArch64Instruction::Branch26 => {
643                // d10k16 format: opcode[31:26], imm_lo16[25:10], imm_hi10[9:0]
644                and_from_slice(dest, &0xFC00_0000_u32.to_le_bytes());
645                let low_part = (extracted_value & 0xffff) << 10;
646                let high_part = extracted_value >> 16;
647                or_from_slice(dest, &((low_part | high_part) as u32).to_le_bytes());
648            }
649            LoongArch64Instruction::Branch21 => {
650                // d5k16 format: opcode[31:26], imm_lo16[25:10], rj[9:5], imm_hi5[4:0]
651                and_from_slice(dest, &0xFC00_03E0_u32.to_le_bytes());
652                let low_part = (extracted_value & 0xffff) << 10;
653                let high_part = extracted_value >> 16;
654                or_from_slice(dest, &((low_part | high_part) as u32).to_le_bytes());
655            }
656            LoongArch64Instruction::Call30 => {
657                // Two instructions as u64 (little-endian):
658                //   insn1 (low  u32): bits[24:5]  = high part of immediate
659                //   insn2 (high u32): bits[18:10] = low part of immediate
660                const CLEAR: u64 = (0xFFF8_03FF_u64 << 32) | 0xFE00_001F_u64;
661                and_from_slice(dest, &CLEAR.to_le_bytes());
662                let low_part = (extracted_value & 0x1ff) << (32 + 10);
663                let high_part = (extracted_value & !0x1ff) << 5;
664                or_from_slice(dest, &(low_part | high_part).to_le_bytes());
665            }
666            LoongArch64Instruction::Call36 => {
667                // Two instructions as u64 (little-endian):
668                //   insn1 (low  u32, pcaddu18i): bits[24:5]  = high part of immediate
669                //   insn2 (high u32, jirl):      bits[25:10] = low part of immediate
670                const CLEAR: u64 = (0xFC00_03FF_u64 << 32) | 0xFE00_001F_u64;
671                and_from_slice(dest, &CLEAR.to_le_bytes());
672                let low_part = (extracted_value & 0xffff) << (32 + 10);
673                let high_part = ((extracted_value + 0x8000) >> 16) << 5;
674                or_from_slice(dest, &(low_part | high_part).to_le_bytes());
675            }
676        };
677    }
678
679    #[must_use]
680    pub fn read_value(self, bytes: &[u8]) -> (u64, bool) {
681        match self {
682            LoongArch64Instruction::Shift5 => {
683                // Value is in bits [24:5] (20 bits)
684                let value = u32_from_slice(bytes);
685                let imm = (value >> 5) & 0xfffff;
686
687                (u64::from(imm), false)
688            }
689            LoongArch64Instruction::Shift10 => {
690                // Value is in bits [21:10] (12 bits)
691                let value = u32_from_slice(bytes);
692                let imm = (value >> 10) & 0xfff;
693
694                (u64::from(imm), false)
695            }
696            LoongArch64Instruction::Branch26 => {
697                // B26: low 16 bits in [25:10], high 10 bits in [9:0]
698                let value = u32_from_slice(bytes);
699                let low_part = (value >> 10) & 0xffff;
700                let high_part = value & 0x3ff;
701                let imm = (high_part << 16) | low_part;
702                // Sign extend from bit 25
703                let sign_extended = ((imm as i32) << 6) >> 6;
704
705                (sign_extended as u64, sign_extended < 0)
706            }
707            LoongArch64Instruction::Branch21 => {
708                // B21: low 16 bits in [25:10], high 5 bits in [4:0]
709                let value = u32_from_slice(bytes);
710                let low_part = (value >> 10) & 0xffff;
711                let high_part = value & 0x1f;
712                let imm = (high_part << 16) | low_part;
713                // Sign extend from bit 20
714                let sign_extended = ((imm as i32) << 11) >> 11;
715
716                (sign_extended as u64, sign_extended < 0)
717            }
718            LoongArch64Instruction::Call30 => {
719                // Two instructions: pcaddu18i + jirl
720                // pcaddu18i: imm in bits [24:5] (20 bits, but only high 11 bits used)
721                // jirl: imm in bits [25:10] (16 bits, but only low 9 bits used)
722                let value = u64_from_slice(bytes);
723                let insn1 = (value >> 32) as u32;
724                let insn2 = value as u32;
725                let high_part = ((insn1 >> 5) & 0x7ffff) << 9; // 19 bits shifted
726                let low_part = (insn2 >> 10) & 0x1ff;
727                let imm = high_part | low_part;
728
729                (u64::from(imm), false)
730            }
731            LoongArch64Instruction::Call36 => {
732                // Two instructions: pcaddu18i + jirl
733                // pcaddu18i: imm in bits [24:5] (20 bits)
734                // jirl: imm in bits [25:10] (16 bits)
735                let value = u64_from_slice(bytes);
736                let insn1 = value as u32;
737                let insn2 = (value >> 32) as u32;
738                let high_part = u64::from((insn1 >> 5) & 0xfffff);
739                let low_part = u64::from((insn2 >> 10) & 0xffff);
740                // Reverse the adjustment done in write: high_part was ((value + 0x8000) >> 16)
741                // So we need: value = (high_part << 16) + low_part - adjustment
742                // But since we're reading, we just combine them
743                let imm = ((high_part << 16).wrapping_sub(0x8000) & 0xffffffff) | low_part;
744
745                (imm, false)
746            }
747        }
748    }
749}
750
751// Documentation definition:
752// (*(uint32_t *) PC) [24 ... 5] = (((S+A+0x8000'0000 + (((S+A) & 0x800) ?
753// (0x1000-0x1'0000'0000) : 0)) & ~0xfff) - (PC-8 & ~0xfff)) [51 ... 32]
754#[must_use]
755pub fn highest_relocation_with_bias(symbol_with_addend: u64, pc: u64) -> u64 {
756    ((symbol_with_addend.wrapping_add(SIZE_2GB).wrapping_add(
757        if symbol_with_addend & SIZE_2KB != 0 {
758            SIZE_4KB.wrapping_sub(SIZE_4GB)
759        } else {
760            0
761        },
762    )) & !PAGE_MASK_4KB)
763        .wrapping_sub((pc.wrapping_sub(8)) & !PAGE_MASK_4KB)
764}