1use alloc::collections::BTreeMap;
9#[allow(unused_imports)]
10use alloc::format;
11use alloc::string::String;
12use alloc::string::ToString;
13#[allow(unused_imports)]
14use alloc::vec;
15use alloc::vec::Vec;
16
17use crate::encoder::{InstrBytes, RelaxInfo, RelocKind, Relocation};
18use crate::error::{AsmError, Span};
19
20#[derive(Debug, Clone)]
29pub enum FragmentBytes {
30 Inline(InstrBytes),
32 Heap(Vec<u8>),
34}
35
36impl core::ops::Deref for FragmentBytes {
37 type Target = [u8];
38 #[inline]
39 fn deref(&self) -> &[u8] {
40 match self {
41 FragmentBytes::Inline(ib) => ib,
42 FragmentBytes::Heap(v) => v,
43 }
44 }
45}
46
47impl core::ops::DerefMut for FragmentBytes {
48 #[inline]
49 fn deref_mut(&mut self) -> &mut [u8] {
50 match self {
51 FragmentBytes::Inline(ib) => ib,
52 FragmentBytes::Heap(v) => v,
53 }
54 }
55}
56
57const MAX_RELAXATION_ITERS: usize = 100;
59
60#[cfg(any(feature = "arm", feature = "aarch64", feature = "riscv"))]
63fn read_le32(bytes: &[u8], offset: usize, label: &str, span: Span) -> Result<u32, AsmError> {
64 if offset + 4 > bytes.len() {
65 return Err(AsmError::Syntax {
66 msg: alloc::format!(
67 "relocation offset {offset} out of bounds (buffer len {}) for label '{label}'",
68 bytes.len()
69 ),
70 span,
71 });
72 }
73 let arr: [u8; 4] = match bytes[offset..offset + 4].try_into() {
76 Ok(a) => a,
77 Err(_) => {
78 return Err(AsmError::Syntax {
79 msg: alloc::format!(
80 "relocation offset {offset} out of bounds (buffer len {}) for label '{label}'",
81 bytes.len()
82 ),
83 span,
84 });
85 }
86 };
87 Ok(u32::from_le_bytes(arr))
88}
89
90#[cfg(any(feature = "arm", feature = "riscv"))]
92fn read_le16(bytes: &[u8], offset: usize, label: &str, span: Span) -> Result<u16, AsmError> {
93 if offset + 2 > bytes.len() {
94 return Err(AsmError::Syntax {
95 msg: alloc::format!(
96 "relocation offset {offset} out of bounds (buffer len {}) for label '{label}'",
97 bytes.len()
98 ),
99 span,
100 });
101 }
102 let arr: [u8; 2] = match bytes[offset..offset + 2].try_into() {
103 Ok(a) => a,
104 Err(_) => {
105 return Err(AsmError::Syntax {
106 msg: alloc::format!(
107 "relocation offset {offset} out of bounds (buffer len {}) for label '{label}'",
108 bytes.len()
109 ),
110 span,
111 });
112 }
113 };
114 Ok(u16::from_le_bytes(arr))
115}
116
117#[cfg(any(feature = "arm", feature = "aarch64", feature = "riscv"))]
126fn scaled_displacement(rel: i64, alignment: u8, label: &str, span: Span) -> Result<i64, AsmError> {
127 let mask = i64::from(alignment - 1);
128 if rel & mask != 0 {
129 return Err(AsmError::MisalignedBranchTarget {
130 label: String::from(label),
131 disp: rel,
132 alignment,
133 span,
134 });
135 }
136 Ok(rel >> alignment.trailing_zeros())
137}
138
139#[cfg(feature = "arm")]
154fn thumb_t1_t4_fields(offset: i64) -> (u16, u16, u16, u16, u16) {
155 let imm = offset as u32;
156 let s = ((imm >> 23) & 1) as u16;
157 let i1 = (imm >> 22) & 1;
158 let i2 = (imm >> 21) & 1;
159 let j1 = (!(i1 ^ u32::from(s)) & 1) as u16;
160 let j2 = (!(i2 ^ u32::from(s)) & 1) as u16;
161 let imm10 = ((imm >> 11) & 0x3FF) as u16;
162 let imm11 = (imm & 0x7FF) as u16;
163 (s, j1, j2, imm10, imm11)
164}
165
166#[cfg(feature = "arm")]
169fn encode_arm_imm_for_linker(value: u32) -> Option<(u8, u8)> {
170 for rot in 0..16u8 {
171 let shift = rot * 2;
172 let rotated = value.rotate_left(shift as u32);
173 if rotated <= 0xFF {
174 return Some((rotated as u8, rot));
175 }
176 }
177 None
178}
179
180type ResolveOutput = (
182 Vec<u8>,
183 Vec<(String, u64)>,
184 Vec<AppliedRelocation>,
185 Vec<u64>,
186);
187
188#[derive(Debug, Clone, PartialEq, Eq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192pub struct AppliedRelocation {
193 pub offset: usize,
195 pub size: u8,
197 pub label: String,
199 pub kind: RelocKind,
201 pub addend: i64,
203}
204
205#[derive(Debug, Clone)]
214pub enum Fragment {
215 Fixed {
217 bytes: FragmentBytes,
219 relocation: Option<Relocation>,
221 span: Span,
223 },
224 Align {
230 alignment: u32,
232 fill: u8,
234 max_skip: Option<u32>,
237 use_nop: bool,
239 span: Span,
241 },
242 Relaxable {
248 short_bytes: InstrBytes,
250 short_reloc_offset: usize,
252 short_relocation: Option<Relocation>,
256 long_bytes: InstrBytes,
258 long_relocation: Relocation,
260 is_long: bool,
262 span: Span,
264 },
265 Org {
269 target: u64,
271 fill: u8,
273 span: Span,
275 },
276}
277
278#[derive(Debug, Clone)]
282struct LabelDef {
283 fragment_index: usize,
284 span: Span,
285}
286
287#[derive(Debug, Clone, Default)]
289struct NumericLabels {
290 defs: BTreeMap<u32, Vec<usize>>,
291}
292
293#[derive(Debug)]
297pub struct Linker {
298 fragments: Vec<Fragment>,
299 labels: BTreeMap<String, LabelDef>,
300 externals: BTreeMap<String, u64>,
301 numeric: NumericLabels,
302 constants: BTreeMap<String, i128>,
303 base_address: u64,
304 resolved: bool,
306 max_output_bytes: usize,
313}
314
315impl Default for Linker {
316 fn default() -> Self {
317 Self::new()
318 }
319}
320
321impl Linker {
322 pub fn new() -> Self {
324 Self {
325 fragments: Vec::new(),
326 labels: BTreeMap::new(),
327 externals: BTreeMap::new(),
328 numeric: NumericLabels::default(),
329 constants: BTreeMap::new(),
330 base_address: 0,
331 resolved: false,
332 max_output_bytes: usize::MAX,
333 }
334 }
335
336 pub fn set_max_output_bytes(&mut self, max: usize) {
342 self.max_output_bytes = max;
343 }
344
345 pub fn set_base_address(&mut self, addr: u64) {
347 self.base_address = addr;
348 }
349
350 pub fn base_address(&self) -> u64 {
352 self.base_address
353 }
354
355 pub fn fragment_count(&self) -> usize {
357 self.fragments.len()
358 }
359
360 pub fn define_external(&mut self, name: &str, addr: u64) {
362 self.externals.insert(String::from(name), addr);
363 }
364
365 pub fn define_constant(&mut self, name: &str, value: i128) {
367 self.constants.insert(String::from(name), value);
368 }
369
370 pub fn get_constant(&self, name: &str) -> Option<&i128> {
372 self.constants.get(name)
373 }
374
375 pub fn add_label(&mut self, name: &str, span: Span) -> Result<(), AsmError> {
377 if let Ok(n) = name.parse::<u32>() {
379 self.numeric
380 .defs
381 .entry(n)
382 .or_default()
383 .push(self.fragments.len());
384 return Ok(());
385 }
386
387 if let Some(existing) = self.labels.get(name) {
388 return Err(AsmError::DuplicateLabel {
389 label: String::from(name),
390 span,
391 first_span: existing.span,
392 });
393 }
394 self.labels.insert(
395 String::from(name),
396 LabelDef {
397 fragment_index: self.fragments.len(),
398 span,
399 },
400 );
401 Ok(())
402 }
403
404 pub fn add_fragment(&mut self, fragment: Fragment) {
406 self.fragments.push(fragment);
407 }
408
409 pub fn add_bytes(&mut self, bytes: Vec<u8>, span: Span) {
411 self.fragments.push(Fragment::Fixed {
412 bytes: FragmentBytes::Heap(bytes),
413 relocation: None,
414 span,
415 });
416 }
417
418 pub fn add_encoded(
420 &mut self,
421 bytes: InstrBytes,
422 relocation: Option<Relocation>,
423 relax: Option<RelaxInfo>,
424 span: Span,
425 ) -> Result<(), AsmError> {
426 if let Some(ri) = relax {
427 let long_relocation = relocation.ok_or_else(|| AsmError::Syntax {
428 msg: String::from("internal: relaxable instruction missing relocation"),
429 span,
430 })?;
431 self.fragments.push(Fragment::Relaxable {
432 short_bytes: ri.short_bytes,
433 short_reloc_offset: ri.short_reloc_offset,
434 short_relocation: ri.short_relocation,
435 long_bytes: bytes,
436 long_relocation,
437 is_long: false,
438 span,
439 });
440 } else {
441 self.fragments.push(Fragment::Fixed {
442 bytes: FragmentBytes::Inline(bytes),
443 relocation,
444 span,
445 });
446 }
447 Ok(())
448 }
449
450 pub fn add_alignment(
452 &mut self,
453 alignment: u32,
454 fill: u8,
455 max_skip: Option<u32>,
456 use_nop: bool,
457 span: Span,
458 ) {
459 self.fragments.push(Fragment::Align {
460 alignment,
461 fill,
462 max_skip,
463 use_nop,
464 span,
465 });
466 }
467
468 pub fn add_org(&mut self, target: u64, fill: u8, span: Span) {
471 self.fragments.push(Fragment::Org { target, fill, span });
472 }
473
474 pub fn resolve(&mut self) -> Result<ResolveOutput, AsmError> {
486 if self.resolved {
490 return Err(AsmError::Syntax {
491 msg: String::from(
492 "linker already resolved: relocations are patched in place, \
493 so build a fresh Linker to re-link",
494 ),
495 span: Span::new(0, 0, 0, 0),
496 });
497 }
498 self.resolved = true;
499
500 let offsets = self.relax()?;
502
503 self.emit_final(offsets)
505 }
506
507 fn relax(&mut self) -> Result<Vec<u64>, AsmError> {
512 let mut offsets = Vec::with_capacity(self.fragments.len() + 1);
513 let mut to_expand: Vec<usize> = Vec::new();
514
515 for _iter in 0..MAX_RELAXATION_ITERS {
516 self.compute_offsets_into(&mut offsets);
517 self.check_layout_size(&offsets)?;
518 to_expand.clear();
519
520 for (i, frag) in self.fragments.iter().enumerate() {
521 if let Fragment::Relaxable {
522 short_bytes,
523 short_relocation,
524 long_relocation,
525 is_long,
526 ..
527 } = frag
528 {
529 if !is_long {
530 let frag_end = offsets[i].wrapping_add(short_bytes.len() as u64);
531 match self.resolve_label_with_offsets(&long_relocation.label, i, &offsets) {
532 Ok(target) => {
533 let disp = (target as i64)
541 .wrapping_sub(frag_end as i64)
542 .wrapping_add(long_relocation.addend);
543 let in_range = if let Some(ref sr) = short_relocation {
544 match sr.kind {
546 #[cfg(feature = "riscv")]
547 RelocKind::RvBranch12 => {
548 let pc_offset = disp + (short_bytes.len() as i64);
550 (-(1i64 << 12)..(1i64 << 12)).contains(&pc_offset)
551 }
552 #[cfg(feature = "riscv")]
553 RelocKind::RvCBranch8 => {
554 let pc_offset = disp + (short_bytes.len() as i64);
556 (-(1i64 << 8)..(1i64 << 8)).contains(&pc_offset)
557 }
558 #[cfg(feature = "riscv")]
559 RelocKind::RvCJump11 => {
560 let pc_offset = disp + (short_bytes.len() as i64);
562 (-(1i64 << 11)..(1i64 << 11)).contains(&pc_offset)
563 }
564 #[cfg(feature = "aarch64")]
565 RelocKind::Aarch64Branch19 => {
566 let pc_offset = disp + (short_bytes.len() as i64);
568 (-(1i64 << 20)..(1i64 << 20)).contains(&pc_offset)
569 }
570 #[cfg(feature = "aarch64")]
571 RelocKind::Aarch64Branch14 => {
572 let pc_offset = disp + (short_bytes.len() as i64);
574 (-(1i64 << 15)..(1i64 << 15)).contains(&pc_offset)
575 }
576 #[cfg(feature = "aarch64")]
577 RelocKind::Aarch64Adr21 => {
578 let pc_offset = disp + (short_bytes.len() as i64);
580 (-(1i64 << 20)..(1i64 << 20)).contains(&pc_offset)
581 }
582 #[cfg(feature = "arm")]
583 RelocKind::ThumbBranch8 => {
584 let pc_offset = disp + (short_bytes.len() as i64);
587 (-(1i64 << 8)..(1i64 << 8)).contains(&pc_offset)
588 }
589 #[cfg(feature = "arm")]
590 RelocKind::ThumbBranch11 => {
591 let pc_offset = disp + (short_bytes.len() as i64);
593 (-(1i64 << 11)..(1i64 << 11)).contains(&pc_offset)
594 }
595 _ => (-128..=127).contains(&disp),
596 }
597 } else {
598 (-128..=127).contains(&disp)
600 };
601 if !in_range {
602 to_expand.push(i);
603 }
604 }
605 Err(_) => {
606 to_expand.push(i);
609 }
610 }
611 }
612 }
613 }
614
615 if to_expand.is_empty() {
616 return Ok(offsets);
617 }
618
619 for &idx in &to_expand {
620 if let Fragment::Relaxable {
621 ref mut is_long, ..
622 } = self.fragments[idx]
623 {
624 *is_long = true;
625 }
626 }
627 }
628
629 Err(AsmError::RelaxationLimit {
630 max: MAX_RELAXATION_ITERS,
631 })
632 }
633
634 fn check_layout_size(&self, offsets: &[u64]) -> Result<(), AsmError> {
640 let end = offsets.last().copied().unwrap_or(self.base_address);
641 let size = end.saturating_sub(self.base_address);
642 if size > self.max_output_bytes as u64 {
643 return Err(AsmError::ResourceLimitExceeded {
644 resource: String::from("output bytes"),
645 limit: self.max_output_bytes,
646 });
647 }
648 Ok(())
649 }
650
651 fn compute_offsets_into(&self, offsets: &mut Vec<u64>) {
658 offsets.clear();
659 let mut current = self.base_address;
660 for frag in &self.fragments {
661 offsets.push(current);
662 match frag {
663 Fragment::Fixed { bytes, .. } => {
664 current += bytes.len() as u64;
665 }
666 Fragment::Align {
667 alignment,
668 max_skip,
669 ..
670 } => {
671 let a = *alignment as u64;
672 if a > 1 {
673 let aligned = current.div_ceil(a) * a;
674 let padding = aligned - current;
675 if max_skip.map_or(true, |ms| padding <= ms as u64) {
676 current = aligned;
677 }
678 }
679 }
680 Fragment::Relaxable {
681 short_bytes,
682 long_bytes,
683 is_long,
684 ..
685 } => {
686 if *is_long {
687 current += long_bytes.len() as u64;
688 } else {
689 current += short_bytes.len() as u64;
690 }
691 }
692 Fragment::Org { target, .. } => {
693 if *target > current {
694 current = *target;
695 }
696 }
698 }
699 }
700 offsets.push(current);
701 }
702
703 fn emit_final(&mut self, offsets: Vec<u64>) -> Result<ResolveOutput, AsmError> {
706 self.check_layout_size(&offsets)?;
710 let total_size = offsets
711 .last()
712 .copied()
713 .unwrap_or(self.base_address)
714 .saturating_sub(self.base_address);
715 let mut output = Vec::with_capacity(total_size as usize);
716 let mut applied_relocs = Vec::new();
717
718 let mut fragments = core::mem::take(&mut self.fragments);
721
722 for (i, frag) in fragments.iter_mut().enumerate() {
723 match frag {
724 Fragment::Fixed {
725 bytes,
726 relocation,
727 span,
728 } => {
729 if let Some(ref mut reloc) = relocation {
730 let frag_output_offset = output.len();
731 self.apply_relocation(bytes, reloc, offsets[i], &offsets, i, *span)?;
733 applied_relocs.push(AppliedRelocation {
734 offset: frag_output_offset + reloc.offset,
735 size: reloc.size,
736 label: reloc.label.to_string(),
738 kind: reloc.kind,
739 addend: reloc.addend,
740 });
741 output.extend_from_slice(bytes);
742 } else {
743 output.extend_from_slice(bytes);
744 }
745 }
746
747 Fragment::Align {
748 alignment,
749 fill,
750 max_skip,
751 use_nop,
752 ..
753 } => {
754 let a = *alignment as u64;
755 if a > 1 {
756 let current = offsets[i];
757 let aligned = current.div_ceil(a) * a;
758 let padding = (aligned - current) as usize;
759 if max_skip.is_some_and(|ms| padding > ms as usize) {
761 } else if *use_nop {
763 emit_nop_padding(&mut output, padding);
764 } else {
765 output.extend(core::iter::repeat(*fill).take(padding));
766 }
767 }
768 }
769
770 Fragment::Relaxable {
771 short_bytes,
772 short_reloc_offset,
773 short_relocation,
774 long_bytes,
775 long_relocation,
776 is_long,
777 span,
778 } => {
779 if *is_long {
780 let frag_output_offset = output.len();
781 self.apply_relocation(
783 long_bytes,
784 long_relocation,
785 offsets[i],
786 &offsets,
787 i,
788 *span,
789 )?;
790 applied_relocs.push(AppliedRelocation {
791 offset: frag_output_offset + long_relocation.offset,
792 size: long_relocation.size,
793 label: (*long_relocation.label).into(),
794 kind: long_relocation.kind,
795 addend: long_relocation.addend,
796 });
797 output.extend_from_slice(long_bytes);
798 } else if let Some(ref mut sr) = short_relocation {
799 let frag_output_offset = output.len();
802 self.apply_relocation(short_bytes, sr, offsets[i], &offsets, i, *span)?;
804 applied_relocs.push(AppliedRelocation {
805 offset: frag_output_offset + sr.offset,
806 size: sr.size,
807 label: (*sr.label).into(),
808 kind: sr.kind,
809 addend: sr.addend,
810 });
811 output.extend_from_slice(short_bytes);
812 } else {
813 let frag_output_offset = output.len();
815 let target =
816 self.resolve_label_with_offsets(&long_relocation.label, i, &offsets)?;
817 let frag_end = offsets[i].wrapping_add(short_bytes.len() as u64);
818 let disp = (target as i64)
819 .wrapping_sub(frag_end as i64)
820 .wrapping_add(long_relocation.addend);
821 if !(-128..=127).contains(&disp) {
822 return Err(AsmError::BranchOutOfRange {
823 label: long_relocation.label.to_string(),
824 disp,
825 max: 127,
826 span: *span,
827 });
828 }
829 short_bytes[*short_reloc_offset] = disp as i8 as u8;
831 applied_relocs.push(AppliedRelocation {
832 offset: frag_output_offset + *short_reloc_offset,
833 size: 1,
834 label: (*long_relocation.label).into(),
835 kind: RelocKind::X86Relative,
836 addend: long_relocation.addend,
837 });
838 output.extend_from_slice(short_bytes);
839 }
840 }
841
842 Fragment::Org {
843 target, fill, span, ..
844 } => {
845 let current = offsets[i];
846 if *target < current {
847 return Err(AsmError::Syntax {
848 msg: alloc::format!(
849 ".org target 0x{:X} is behind current position 0x{:X}",
850 target,
851 current
852 ),
853 span: *span,
854 });
855 }
856 let padding = (*target - current) as usize;
857 output.extend(core::iter::repeat(*fill).take(padding));
858 }
859 }
860 }
861
862 self.fragments = fragments;
864
865 let label_table: Vec<(String, u64)> = self
867 .labels
868 .iter()
869 .map(|(name, def)| (name.clone(), offsets[def.fragment_index]))
870 .collect();
871
872 Ok((output, label_table, applied_relocs, offsets))
873 }
874
875 fn apply_relocation(
878 &self,
879 bytes: &mut [u8],
880 reloc: &Relocation,
881 frag_abs: u64,
882 offsets: &[u64],
883 from_fragment: usize,
884 span: Span,
885 ) -> Result<(), AsmError> {
886 let target_addr = self.resolve_label_with_offsets(&reloc.label, from_fragment, offsets)?;
887 let reloc_abs = frag_abs + reloc.offset as u64;
888
889 match reloc.kind {
890 RelocKind::X86Relative => {
891 let rip = reloc_abs + reloc.size as u64 + reloc.trailing_bytes as u64;
894 let rel = (target_addr as i64)
895 .wrapping_sub(rip as i64)
896 .wrapping_add(reloc.addend);
897 match reloc.size {
898 1 => {
899 if rel < i8::MIN as i64 || rel > i8::MAX as i64 {
900 return Err(AsmError::BranchOutOfRange {
901 label: reloc.label.to_string(),
902 disp: rel,
903 max: 127,
904 span,
905 });
906 }
907 bytes[reloc.offset] = rel as i8 as u8;
908 }
909 4 => {
910 if rel < i32::MIN as i64 || rel > i32::MAX as i64 {
911 return Err(AsmError::BranchOutOfRange {
912 label: reloc.label.to_string(),
913 disp: rel,
914 max: i32::MAX as i64,
915 span,
916 });
917 }
918 let b = (rel as i32).to_le_bytes();
919 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&b);
920 }
921 other => {
922 return Err(AsmError::Syntax {
923 msg: alloc::format!(
924 "unsupported RIP-relative relocation size: {other}"
925 ),
926 span,
927 });
928 }
929 }
930 }
931 RelocKind::Absolute => {
932 let addr = target_addr.wrapping_add(reloc.addend as u64);
933 match reloc.size {
934 1 => {
935 if addr > u8::MAX as u64 {
936 return Err(AsmError::Syntax {
937 msg: alloc::format!(
938 "absolute address 0x{addr:X} exceeds 8-bit relocation range for '{}'",
939 reloc.label
940 ),
941 span,
942 });
943 }
944 bytes[reloc.offset] = addr as u8;
945 }
946 2 => {
947 if addr > u16::MAX as u64 {
948 return Err(AsmError::Syntax {
949 msg: alloc::format!(
950 "absolute address 0x{addr:X} exceeds 16-bit relocation range for '{}'",
951 reloc.label
952 ),
953 span,
954 });
955 }
956 bytes[reloc.offset..reloc.offset + 2]
957 .copy_from_slice(&(addr as u16).to_le_bytes());
958 }
959 4 => {
960 if addr > u32::MAX as u64 {
961 return Err(AsmError::Syntax {
962 msg: alloc::format!(
963 "absolute address 0x{addr:X} exceeds 32-bit relocation range for '{}'",
964 reloc.label
965 ),
966 span,
967 });
968 }
969 bytes[reloc.offset..reloc.offset + 4]
970 .copy_from_slice(&(addr as u32).to_le_bytes());
971 }
972 8 => {
973 bytes[reloc.offset..reloc.offset + 8].copy_from_slice(&addr.to_le_bytes());
974 }
975 other => {
976 return Err(AsmError::Syntax {
977 msg: alloc::format!("unsupported absolute relocation size: {other}"),
978 span,
979 });
980 }
981 }
982 }
983 #[cfg(feature = "arm")]
984 RelocKind::ArmBranch24 => {
985 let pc = reloc_abs + 8;
987 let rel = (target_addr as i64)
988 .wrapping_sub(pc as i64)
989 .wrapping_add(reloc.addend);
990 let offset = scaled_displacement(rel, 4, &reloc.label, span)?;
991 if !(-(1 << 23)..(1 << 23)).contains(&offset) {
992 return Err(AsmError::BranchOutOfRange {
993 label: reloc.label.to_string(),
994 disp: rel,
995 max: (1 << 25) - 4,
996 span,
997 });
998 }
999 let imm24 = (offset as u32) & 0x00FF_FFFF;
1000 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1001 word = (word & 0xFF00_0000) | imm24;
1002 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1003 }
1004 #[cfg(feature = "arm")]
1005 RelocKind::ArmLdrLit => {
1006 let pc = reloc_abs + 8;
1008 let rel = (target_addr as i64)
1009 .wrapping_sub(pc as i64)
1010 .wrapping_add(reloc.addend);
1011 let abs_rel = rel.unsigned_abs();
1012 if abs_rel > 4095 {
1013 return Err(AsmError::BranchOutOfRange {
1014 label: reloc.label.to_string(),
1015 disp: rel,
1016 max: 4095,
1017 span,
1018 });
1019 }
1020 let u_bit = if rel >= 0 { 1u32 } else { 0u32 };
1021 let imm12 = (abs_rel as u32) & 0xFFF;
1022 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1023 word = (word & 0xFF7F_F000) | (u_bit << 23) | imm12;
1024 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1025 }
1026 #[cfg(feature = "arm")]
1027 RelocKind::ArmAdr => {
1028 let pc = reloc_abs + 8;
1032 let rel = (target_addr as i64)
1033 .wrapping_sub(pc as i64)
1034 .wrapping_add(reloc.addend);
1035 let abs_rel = rel.unsigned_abs() as u32;
1036 let (op, imm8, rot) = if rel >= 0 {
1037 let (i, r) = encode_arm_imm_for_linker(abs_rel).ok_or_else(|| {
1039 AsmError::BranchOutOfRange {
1040 label: reloc.label.to_string(),
1041 disp: rel,
1042 max: 255, span,
1044 }
1045 })?;
1046 (0x4u32, i, r)
1047 } else {
1048 let (i, r) = encode_arm_imm_for_linker(abs_rel).ok_or_else(|| {
1050 AsmError::BranchOutOfRange {
1051 label: reloc.label.to_string(),
1052 disp: rel,
1053 max: 255,
1054 span,
1055 }
1056 })?;
1057 (0x2u32, i, r)
1058 };
1059 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1060 word = (word & 0xF1F0_F000) | (op << 21) | ((rot as u32) << 8) | (imm8 as u32);
1062 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1063 }
1064 #[cfg(feature = "arm")]
1065 RelocKind::ThumbBranch8 => {
1066 let pc = reloc_abs + 4;
1068 let rel = (target_addr as i64)
1069 .wrapping_sub(pc as i64)
1070 .wrapping_add(reloc.addend);
1071 let offset = scaled_displacement(rel, 2, &reloc.label, span)?;
1072 if !(-(1i64 << 7)..(1i64 << 7)).contains(&offset) {
1073 return Err(AsmError::BranchOutOfRange {
1074 label: reloc.label.to_string(),
1075 disp: rel,
1076 max: 254,
1077 span,
1078 });
1079 }
1080 let imm8 = (offset as u8) as u16;
1081 let mut hw = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1082 hw = (hw & 0xFF00) | (imm8 & 0xFF);
1083 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw.to_le_bytes());
1084 }
1085 #[cfg(feature = "arm")]
1086 RelocKind::ThumbBranch11 => {
1087 let pc = reloc_abs + 4;
1089 let rel = (target_addr as i64)
1090 .wrapping_sub(pc as i64)
1091 .wrapping_add(reloc.addend);
1092 let offset = scaled_displacement(rel, 2, &reloc.label, span)?;
1093 if !(-(1i64 << 10)..(1i64 << 10)).contains(&offset) {
1094 return Err(AsmError::BranchOutOfRange {
1095 label: reloc.label.to_string(),
1096 disp: rel,
1097 max: 2046,
1098 span,
1099 });
1100 }
1101 let imm11 = (offset as u16) & 0x7FF;
1102 let mut hw = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1103 hw = (hw & 0xF800) | imm11;
1104 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw.to_le_bytes());
1105 }
1106 #[cfg(feature = "arm")]
1107 RelocKind::ThumbBl => {
1108 let pc = reloc_abs + 4;
1110 let rel = (target_addr as i64)
1111 .wrapping_sub(pc as i64)
1112 .wrapping_add(reloc.addend);
1113 let offset = scaled_displacement(rel, 2, &reloc.label, span)?;
1114 if !(-(1i64 << 23)..(1i64 << 23)).contains(&offset) {
1115 return Err(AsmError::BranchOutOfRange {
1116 label: reloc.label.to_string(),
1117 disp: rel,
1118 max: (1 << 24) - 2,
1119 span,
1120 });
1121 }
1122 let (s, j1, j2, imm10, imm11) = thumb_t1_t4_fields(offset);
1123 let hw1 = 0xF000 | (s << 10) | imm10;
1124 let hw2 = 0xD000 | (j1 << 13) | (j2 << 11) | imm11;
1125 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw1.to_le_bytes());
1126 bytes[reloc.offset + 2..reloc.offset + 4].copy_from_slice(&hw2.to_le_bytes());
1127 }
1128 #[cfg(feature = "arm")]
1129 RelocKind::ThumbBranchW => {
1130 let pc = reloc_abs + 4;
1132 let rel = (target_addr as i64)
1133 .wrapping_sub(pc as i64)
1134 .wrapping_add(reloc.addend);
1135 let offset = scaled_displacement(rel, 2, &reloc.label, span)?;
1136 if !(-(1i64 << 23)..(1i64 << 23)).contains(&offset) {
1137 return Err(AsmError::BranchOutOfRange {
1138 label: reloc.label.to_string(),
1139 disp: rel,
1140 max: (1 << 24) - 2,
1141 span,
1142 });
1143 }
1144 let (s, j1, j2, imm10, imm11) = thumb_t1_t4_fields(offset);
1145 let hw1 = 0xF000 | (s << 10) | imm10;
1146 let hw2 = 0x9000 | (j1 << 13) | (j2 << 11) | imm11;
1147 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw1.to_le_bytes());
1148 bytes[reloc.offset + 2..reloc.offset + 4].copy_from_slice(&hw2.to_le_bytes());
1149 }
1150 #[cfg(feature = "arm")]
1151 RelocKind::ThumbCondBranchW => {
1152 let pc = reloc_abs + 4;
1154 let rel = (target_addr as i64)
1155 .wrapping_sub(pc as i64)
1156 .wrapping_add(reloc.addend);
1157 let offset = scaled_displacement(rel, 2, &reloc.label, span)?;
1158 if !(-(1i64 << 19)..(1i64 << 19)).contains(&offset) {
1159 return Err(AsmError::BranchOutOfRange {
1160 label: reloc.label.to_string(),
1161 disp: rel,
1162 max: (1 << 20) - 2,
1163 span,
1164 });
1165 }
1166 let s = if offset < 0 { 1_u16 } else { 0 };
1167 let imm = offset as u32;
1168 let imm6 = ((imm >> 11) & 0x3F) as u16;
1169 let imm11 = (imm & 0x7FF) as u16;
1170 let j1 = ((imm >> 17) & 1) as u16;
1171 let j2 = ((imm >> 18) & 1) as u16;
1172 let existing_hw1 = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1174 let cond = (existing_hw1 >> 6) & 0xF;
1175 let hw1 = 0xF000 | (s << 10) | (cond << 6) | imm6;
1176 let hw2 = 0x8000 | (j1 << 13) | (j2 << 11) | imm11;
1177 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw1.to_le_bytes());
1178 bytes[reloc.offset + 2..reloc.offset + 4].copy_from_slice(&hw2.to_le_bytes());
1179 }
1180 #[cfg(feature = "arm")]
1181 RelocKind::ThumbLdrLit8 => {
1182 let pc = (reloc_abs + 4) & !3;
1185 let rel = (target_addr as i64)
1186 .wrapping_sub(pc as i64)
1187 .wrapping_add(reloc.addend);
1188 if !(0..=1020).contains(&rel) || (rel & 3) != 0 {
1189 return Err(AsmError::BranchOutOfRange {
1190 label: reloc.label.to_string(),
1191 disp: rel,
1192 max: 1020,
1193 span,
1194 });
1195 }
1196 let imm8 = (rel >> 2) as u16;
1197 let existing = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1198 let hw = (existing & 0xFF00) | imm8;
1199 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw.to_le_bytes());
1200 }
1201 #[cfg(feature = "aarch64")]
1202 RelocKind::Aarch64Jump26 => {
1203 let rel = (target_addr as i64)
1205 .wrapping_sub(reloc_abs as i64)
1206 .wrapping_add(reloc.addend);
1207 let offset = scaled_displacement(rel, 4, &reloc.label, span)?;
1208 if !(-(1 << 25)..(1 << 25)).contains(&offset) {
1209 return Err(AsmError::BranchOutOfRange {
1210 label: reloc.label.to_string(),
1211 disp: rel,
1212 max: (1 << 27) - 4,
1213 span,
1214 });
1215 }
1216 let imm26 = (offset as u32) & 0x03FF_FFFF;
1217 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1218 word = (word & 0xFC00_0000) | imm26;
1219 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1220 }
1221 #[cfg(feature = "aarch64")]
1222 RelocKind::Aarch64Branch19 => {
1223 let rel = (target_addr as i64)
1225 .wrapping_sub(reloc_abs as i64)
1226 .wrapping_add(reloc.addend);
1227 let offset = scaled_displacement(rel, 4, &reloc.label, span)?;
1228 if !(-(1 << 18)..(1 << 18)).contains(&offset) {
1229 return Err(AsmError::BranchOutOfRange {
1230 label: reloc.label.to_string(),
1231 disp: rel,
1232 max: (1 << 20) - 4,
1233 span,
1234 });
1235 }
1236 let imm19 = (offset as u32) & 0x7FFFF;
1237 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1238 word = (word & 0xFF00_001F) | (imm19 << 5);
1239 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1240 }
1241 #[cfg(feature = "aarch64")]
1242 RelocKind::Aarch64Branch14 => {
1243 let rel = (target_addr as i64)
1245 .wrapping_sub(reloc_abs as i64)
1246 .wrapping_add(reloc.addend);
1247 let offset = scaled_displacement(rel, 4, &reloc.label, span)?;
1248 if !(-(1 << 13)..(1 << 13)).contains(&offset) {
1249 return Err(AsmError::BranchOutOfRange {
1250 label: reloc.label.to_string(),
1251 disp: rel,
1252 max: (1 << 15) - 4,
1253 span,
1254 });
1255 }
1256 let imm14 = (offset as u32) & 0x3FFF;
1257 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1258 word = (word & 0xFFF8_001F) | (imm14 << 5);
1259 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1260 }
1261 #[cfg(feature = "aarch64")]
1262 RelocKind::Aarch64LdrLit19 => {
1263 let rel = (target_addr as i64)
1265 .wrapping_sub(reloc_abs as i64)
1266 .wrapping_add(reloc.addend);
1267 let offset = scaled_displacement(rel, 4, &reloc.label, span)?;
1268 if !(-(1 << 18)..(1 << 18)).contains(&offset) {
1269 return Err(AsmError::BranchOutOfRange {
1270 label: reloc.label.to_string(),
1271 disp: rel,
1272 max: (1 << 20) - 4,
1273 span,
1274 });
1275 }
1276 let imm19 = (offset as u32) & 0x7FFFF;
1277 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1278 word = (word & 0xFF00_001F) | (imm19 << 5);
1279 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1280 }
1281 #[cfg(feature = "aarch64")]
1282 RelocKind::Aarch64Adr21 => {
1283 let rel = (target_addr as i64)
1285 .wrapping_sub(reloc_abs as i64)
1286 .wrapping_add(reloc.addend);
1287 if !(-(1 << 20)..(1 << 20)).contains(&rel) {
1288 return Err(AsmError::BranchOutOfRange {
1289 label: reloc.label.to_string(),
1290 disp: rel,
1291 max: (1 << 20) - 1,
1292 span,
1293 });
1294 }
1295 let immhi = ((rel >> 2) as u32) & 0x7FFFF;
1296 let immlo = (rel as u32) & 0x3;
1297 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1298 word = (word & 0x9F00_001F) | (immlo << 29) | (immhi << 5);
1299 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1300 }
1301 #[cfg(feature = "aarch64")]
1302 RelocKind::Aarch64Adrp => {
1303 let pc_page = reloc_abs & !0xFFF;
1305 let target_page = target_addr.wrapping_add(reloc.addend as u64) & !0xFFF;
1306 let rel = (target_page as i64).wrapping_sub(pc_page as i64);
1307 let page_off = rel >> 12;
1308 if !(-(1 << 20)..(1 << 20)).contains(&page_off) {
1309 return Err(AsmError::BranchOutOfRange {
1310 label: reloc.label.to_string(),
1311 disp: rel,
1312 max: (1i64 << 32) - 1,
1313 span,
1314 });
1315 }
1316 let immhi = ((page_off >> 2) as u32) & 0x7FFFF;
1317 let immlo = (page_off as u32) & 0x3;
1318 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1319 word = (word & 0x9F00_001F) | (immlo << 29) | (immhi << 5);
1320 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1321 }
1322 #[cfg(feature = "aarch64")]
1323 RelocKind::Aarch64AdrpAddPair => {
1324 let pc_page = reloc_abs & !0xFFF;
1327 let target_with_addend = target_addr.wrapping_add(reloc.addend as u64);
1328 let target_page = target_with_addend & !0xFFF;
1329 let rel = (target_page as i64).wrapping_sub(pc_page as i64);
1330 let page_off = rel >> 12;
1331 if !(-(1 << 20)..(1 << 20)).contains(&page_off) {
1332 return Err(AsmError::BranchOutOfRange {
1333 label: reloc.label.to_string(),
1334 disp: rel,
1335 max: (1i64 << 32) - 1,
1336 span,
1337 });
1338 }
1339 let immhi_p = ((page_off >> 2) as u32) & 0x7FFFF;
1340 let immlo_p = (page_off as u32) & 0x3;
1341 let mut adrp_word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1342 adrp_word = (adrp_word & 0x9F00_001F) | (immlo_p << 29) | (immhi_p << 5);
1343 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&adrp_word.to_le_bytes());
1344
1345 let lo12 = (target_with_addend & 0xFFF) as u32;
1347 let add_offset = reloc.offset + 4;
1348 let mut add_word = read_le32(bytes, add_offset, &reloc.label, span)?;
1349 add_word = (add_word & 0xFFC003FF) | (lo12 << 10);
1350 bytes[add_offset..add_offset + 4].copy_from_slice(&add_word.to_le_bytes());
1351 }
1352 #[cfg(feature = "riscv")]
1353 RelocKind::RvJal20 => {
1354 let rel = (target_addr as i64)
1357 .wrapping_sub(reloc_abs as i64)
1358 .wrapping_add(reloc.addend);
1359 if !(-(1i64 << 20)..(1i64 << 20)).contains(&rel) {
1360 return Err(AsmError::BranchOutOfRange {
1361 label: reloc.label.to_string(),
1362 disp: rel,
1363 max: (1 << 20) - 2,
1364 span,
1365 });
1366 }
1367 scaled_displacement(rel, 2, &reloc.label, span)?;
1369 let imm = rel as u32;
1370 let packed = ((imm & 0x0010_0000) << 11) | ((imm & 0x7FE) << 20) | ((imm & 0x800) << 9) | (imm & 0x000F_F000); let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1375 word = (word & 0xFFF) | packed;
1376 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1377 }
1378 #[cfg(feature = "riscv")]
1379 RelocKind::RvBranch12 => {
1380 let rel = (target_addr as i64)
1383 .wrapping_sub(reloc_abs as i64)
1384 .wrapping_add(reloc.addend);
1385 if !(-(1i64 << 12)..(1i64 << 12)).contains(&rel) {
1386 return Err(AsmError::BranchOutOfRange {
1387 label: reloc.label.to_string(),
1388 disp: rel,
1389 max: (1 << 12) - 2,
1390 span,
1391 });
1392 }
1393 scaled_displacement(rel, 2, &reloc.label, span)?;
1394 let imm = rel as u32;
1395 let packed_hi = ((imm & 0x1000) << 19) | ((imm & 0x7E0) << 20); let packed_lo = ((imm & 0x1E) << 7) | ((imm & 0x800) >> 4); let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1400 word = (word & 0x01FF_F07F) | packed_hi | packed_lo;
1401 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1402 }
1403 #[cfg(feature = "riscv")]
1404 RelocKind::RvAuipc20 => {
1405 let rel = (target_addr as i64)
1410 .wrapping_sub(reloc_abs as i64)
1411 .wrapping_add(reloc.addend);
1412 if !(-(1i64 << 31) - 0x800..(1i64 << 31) - 0x800).contains(&rel) {
1418 return Err(AsmError::BranchOutOfRange {
1419 label: reloc.label.to_string(),
1420 disp: rel,
1421 max: (1i64 << 31) - 0x800 - 1,
1422 span,
1423 });
1424 }
1425 let hi20 = ((rel + 0x800) >> 12) as u32;
1426 let lo12 = (rel as u32).wrapping_sub(hi20 << 12);
1427 let mut word = read_le32(bytes, reloc.offset, &reloc.label, span)?;
1429 word = (word & 0xFFF) | (hi20 << 12);
1430 bytes[reloc.offset..reloc.offset + 4].copy_from_slice(&word.to_le_bytes());
1431 let jalr_off = reloc.offset + 4;
1433 let mut jalr = read_le32(bytes, jalr_off, &reloc.label, span)?;
1434 jalr = (jalr & 0x000F_FFFF) | ((lo12 & 0xFFF) << 20);
1435 bytes[jalr_off..jalr_off + 4].copy_from_slice(&jalr.to_le_bytes());
1436 }
1437 #[cfg(feature = "riscv")]
1438 RelocKind::RvCBranch8 => {
1439 let rel = (target_addr as i64)
1442 .wrapping_sub(reloc_abs as i64)
1443 .wrapping_add(reloc.addend);
1444 if !(-(1i64 << 8)..(1i64 << 8)).contains(&rel) {
1445 return Err(AsmError::BranchOutOfRange {
1446 label: reloc.label.to_string(),
1447 disp: rel,
1448 max: (1 << 8) - 2,
1449 span,
1450 });
1451 }
1452 scaled_displacement(rel, 2, &reloc.label, span)?;
1453 let imm = rel as u16;
1454 let mut hw = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1456 hw &= 0xE383; hw |= ((imm >> 8) & 1) << 12;
1460 hw |= ((imm >> 3) & 3) << 10;
1461 hw |= ((imm >> 6) & 3) << 5;
1462 hw |= ((imm >> 1) & 3) << 3;
1463 hw |= ((imm >> 5) & 1) << 2;
1464 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw.to_le_bytes());
1465 }
1466 #[cfg(feature = "riscv")]
1467 RelocKind::RvCJump11 => {
1468 let rel = (target_addr as i64)
1471 .wrapping_sub(reloc_abs as i64)
1472 .wrapping_add(reloc.addend);
1473 if !(-(1i64 << 11)..(1i64 << 11)).contains(&rel) {
1474 return Err(AsmError::BranchOutOfRange {
1475 label: reloc.label.to_string(),
1476 disp: rel,
1477 max: (1 << 11) - 2,
1478 span,
1479 });
1480 }
1481 scaled_displacement(rel, 2, &reloc.label, span)?;
1482 let imm = rel as u16;
1483 let mut hw = read_le16(bytes, reloc.offset, &reloc.label, span)?;
1484 hw &= 0xE003; hw |= ((imm >> 11) & 1) << 12;
1488 hw |= ((imm >> 4) & 1) << 11;
1489 hw |= ((imm >> 8) & 3) << 9;
1490 hw |= ((imm >> 10) & 1) << 8;
1491 hw |= ((imm >> 6) & 1) << 7;
1492 hw |= ((imm >> 7) & 1) << 6;
1493 hw |= ((imm >> 1) & 7) << 3;
1494 hw |= ((imm >> 5) & 1) << 2;
1495 bytes[reloc.offset..reloc.offset + 2].copy_from_slice(&hw.to_le_bytes());
1496 }
1497 }
1498 Ok(())
1499 }
1500
1501 fn resolve_label_with_offsets(
1504 &self,
1505 name: &str,
1506 from_fragment: usize,
1507 offsets: &[u64],
1508 ) -> Result<u64, AsmError> {
1509 if let Some(&value) = self.constants.get(name) {
1511 return Ok(value as i64 as u64);
1514 }
1515 if let Some(&addr) = self.externals.get(name) {
1517 return Ok(addr);
1518 }
1519 if name.len() >= 2 {
1521 let last = name.as_bytes()[name.len() - 1];
1522 let num_part = &name[..name.len() - 1];
1523 if last == b'f' || last == b'b' {
1524 if let Ok(n) = num_part.parse::<u32>() {
1525 return self.resolve_numeric_with_offsets(
1526 n,
1527 from_fragment,
1528 last == b'f',
1529 offsets,
1530 );
1531 }
1532 }
1533 }
1534 if let Some(def) = self.labels.get(name) {
1536 return Ok(offsets[def.fragment_index]);
1537 }
1538
1539 Err(AsmError::UndefinedLabel {
1540 label: String::from(name),
1541 span: Span::new(0, 0, 0, 0),
1542 })
1543 }
1544
1545 fn resolve_numeric_with_offsets(
1546 &self,
1547 num: u32,
1548 from_fragment: usize,
1549 forward: bool,
1550 offsets: &[u64],
1551 ) -> Result<u64, AsmError> {
1552 if let Some(defs) = self.numeric.defs.get(&num) {
1553 if forward {
1554 for &def_idx in defs {
1555 if def_idx > from_fragment {
1556 return Ok(offsets[def_idx]);
1557 }
1558 }
1559 } else {
1560 for &def_idx in defs.iter().rev() {
1561 if def_idx <= from_fragment {
1562 return Ok(offsets[def_idx]);
1563 }
1564 }
1565 }
1566 }
1567 Err(AsmError::UndefinedLabel {
1568 label: alloc::format!("{}{}", num, if forward { 'f' } else { 'b' }),
1569 span: Span::new(0, 0, 0, 0),
1570 })
1571 }
1572}
1573
1574const NOP_SEQUENCES: [&[u8]; 10] = [
1582 &[], &[0x90], &[0x66, 0x90], &[0x0F, 0x1F, 0x00], &[0x0F, 0x1F, 0x40, 0x00], &[0x0F, 0x1F, 0x44, 0x00, 0x00], &[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00], &[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00], &[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00], &[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00], ];
1593
1594fn emit_nop_padding(output: &mut Vec<u8>, mut n: usize) {
1599 while n > 0 {
1600 let chunk = core::cmp::min(n, 9);
1601 output.extend_from_slice(NOP_SEQUENCES[chunk]);
1602 n -= chunk;
1603 }
1604}
1605
1606#[cfg(test)]
1609mod tests {
1610 use super::*;
1611
1612 fn span() -> Span {
1613 Span::new(1, 1, 0, 0)
1614 }
1615
1616 fn fixed(bytes: Vec<u8>, reloc: Option<Relocation>) -> Fragment {
1617 Fragment::Fixed {
1618 bytes: FragmentBytes::Heap(bytes),
1619 relocation: reloc,
1620 span: span(),
1621 }
1622 }
1623
1624 fn nop() -> Fragment {
1625 fixed(vec![0x90], None)
1626 }
1627
1628 fn relaxable_jmp(label: &str) -> Fragment {
1629 Fragment::Relaxable {
1630 short_bytes: InstrBytes::from_slice(&[0xEB, 0x00]),
1631 short_reloc_offset: 1,
1632 short_relocation: None,
1633 long_bytes: InstrBytes::from_slice(&[0xE9, 0, 0, 0, 0]),
1634 long_relocation: Relocation {
1635 offset: 1,
1636 size: 4,
1637 label: alloc::rc::Rc::from(label),
1638 kind: RelocKind::X86Relative,
1639 addend: 0,
1640 trailing_bytes: 0,
1641 },
1642 is_long: false,
1643 span: span(),
1644 }
1645 }
1646
1647 fn relaxable_jcc(cc: u8, label: &str) -> Fragment {
1648 Fragment::Relaxable {
1649 short_bytes: InstrBytes::from_slice(&[0x70 + cc, 0x00]),
1650 short_reloc_offset: 1,
1651 short_relocation: None,
1652 long_bytes: InstrBytes::from_slice(&[0x0F, 0x80 + cc, 0, 0, 0, 0]),
1653 long_relocation: Relocation {
1654 offset: 2,
1655 size: 4,
1656 label: alloc::rc::Rc::from(label),
1657 kind: RelocKind::X86Relative,
1658 addend: 0,
1659 trailing_bytes: 0,
1660 },
1661 is_long: false,
1662 span: span(),
1663 }
1664 }
1665
1666 #[test]
1669 fn resolve_forward_label() {
1670 let mut linker = Linker::new();
1671 linker.add_fragment(fixed(
1672 vec![0xE9, 0, 0, 0, 0],
1673 Some(Relocation {
1674 offset: 1,
1675 size: 4,
1676 label: alloc::rc::Rc::from("target"),
1677 kind: RelocKind::X86Relative,
1678 addend: 0,
1679 trailing_bytes: 0,
1680 }),
1681 ));
1682 linker.add_label("target", span()).unwrap();
1683 linker.add_fragment(nop());
1684
1685 let (output, _, _, _) = linker.resolve().unwrap();
1686 assert_eq!(output, vec![0xE9, 0x00, 0x00, 0x00, 0x00, 0x90]);
1687 }
1688
1689 #[test]
1690 fn resolve_backward_label() {
1691 let mut linker = Linker::new();
1692 linker.add_label("top", span()).unwrap();
1693 linker.add_fragment(nop());
1694 linker.add_fragment(fixed(
1695 vec![0xE9, 0, 0, 0, 0],
1696 Some(Relocation {
1697 offset: 1,
1698 size: 4,
1699 label: alloc::rc::Rc::from("top"),
1700 kind: RelocKind::X86Relative,
1701 addend: 0,
1702 trailing_bytes: 0,
1703 }),
1704 ));
1705
1706 let (output, _, _, _) = linker.resolve().unwrap();
1707 let rel = i32::from_le_bytes([output[2], output[3], output[4], output[5]]);
1708 assert_eq!(rel, -6);
1709 }
1710
1711 #[test]
1712 fn resolve_with_base_address() {
1713 let mut linker = Linker::new();
1714 linker.set_base_address(0x1000);
1715 linker.add_fragment(fixed(
1716 vec![0xE9, 0, 0, 0, 0],
1717 Some(Relocation {
1718 offset: 1,
1719 size: 4,
1720 label: alloc::rc::Rc::from("target"),
1721 kind: RelocKind::X86Relative,
1722 addend: 0,
1723 trailing_bytes: 0,
1724 }),
1725 ));
1726 linker.add_label("target", span()).unwrap();
1727 linker.add_fragment(nop());
1728
1729 let (output, _, _, _) = linker.resolve().unwrap();
1730 assert_eq!(output, vec![0xE9, 0x00, 0x00, 0x00, 0x00, 0x90]);
1731 }
1732
1733 #[test]
1734 fn resolve_external_label() {
1735 let mut linker = Linker::new();
1736 linker.define_external("printf", 0xDEAD_BEEF);
1737 linker.add_fragment(fixed(
1738 vec![0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0],
1739 Some(Relocation {
1740 offset: 2,
1741 size: 8,
1742 label: alloc::rc::Rc::from("printf"),
1743 kind: RelocKind::Absolute,
1744 addend: 0,
1745 trailing_bytes: 0,
1746 }),
1747 ));
1748
1749 let (output, _, _, _) = linker.resolve().unwrap();
1750 assert_eq!(output[2..10], 0xDEAD_BEEFu64.to_le_bytes());
1751 }
1752
1753 #[test]
1754 fn resolve_constant() {
1755 let mut linker = Linker::new();
1756 linker.define_constant("SYS_WRITE", 1);
1757 linker.add_fragment(fixed(
1758 vec![0xB8, 0, 0, 0, 0],
1759 Some(Relocation {
1760 offset: 1,
1761 size: 4,
1762 label: alloc::rc::Rc::from("SYS_WRITE"),
1763 kind: RelocKind::Absolute,
1764 addend: 0,
1765 trailing_bytes: 0,
1766 }),
1767 ));
1768
1769 let (output, _, _, _) = linker.resolve().unwrap();
1770 assert_eq!(output, vec![0xB8, 0x01, 0x00, 0x00, 0x00]);
1771 }
1772
1773 #[test]
1774 fn duplicate_label_error() {
1775 let mut linker = Linker::new();
1776 linker.add_label("foo", span()).unwrap();
1777 linker.add_fragment(nop());
1778 let err = linker.add_label("foo", span()).unwrap_err();
1779 assert!(matches!(err, AsmError::DuplicateLabel { .. }));
1780 }
1781
1782 #[test]
1783 fn undefined_label_error() {
1784 let mut linker = Linker::new();
1785 linker.add_fragment(fixed(
1786 vec![0xE9, 0, 0, 0, 0],
1787 Some(Relocation {
1788 offset: 1,
1789 size: 4,
1790 label: alloc::rc::Rc::from("nowhere"),
1791 kind: RelocKind::X86Relative,
1792 addend: 0,
1793 trailing_bytes: 0,
1794 }),
1795 ));
1796 let err = linker.resolve().unwrap_err();
1797 assert!(matches!(err, AsmError::UndefinedLabel { .. }));
1798 }
1799
1800 #[test]
1803 fn numeric_label_forward() {
1804 let mut linker = Linker::new();
1805 linker.add_fragment(fixed(
1806 vec![0xE9, 0, 0, 0, 0],
1807 Some(Relocation {
1808 offset: 1,
1809 size: 4,
1810 label: alloc::rc::Rc::from("1f"),
1811 kind: RelocKind::X86Relative,
1812 addend: 0,
1813 trailing_bytes: 0,
1814 }),
1815 ));
1816 linker.add_label("1", span()).unwrap();
1817 linker.add_fragment(nop());
1818
1819 let (output, _, _, _) = linker.resolve().unwrap();
1820 assert_eq!(&output[1..5], &[0, 0, 0, 0]);
1821 }
1822
1823 #[test]
1824 fn numeric_label_backward() {
1825 let mut linker = Linker::new();
1826 linker.add_label("1", span()).unwrap();
1827 linker.add_fragment(nop());
1828 linker.add_fragment(fixed(
1829 vec![0xE9, 0, 0, 0, 0],
1830 Some(Relocation {
1831 offset: 1,
1832 size: 4,
1833 label: alloc::rc::Rc::from("1b"),
1834 kind: RelocKind::X86Relative,
1835 addend: 0,
1836 trailing_bytes: 0,
1837 }),
1838 ));
1839
1840 let (output, _, _, _) = linker.resolve().unwrap();
1841 let rel = i32::from_le_bytes([output[2], output[3], output[4], output[5]]);
1842 assert_eq!(rel, -6);
1843 }
1844
1845 #[test]
1848 fn relaxation_short_jmp_forward() {
1849 let mut linker = Linker::new();
1850 linker.add_fragment(relaxable_jmp("target"));
1851 linker.add_label("target", span()).unwrap();
1852 linker.add_fragment(nop());
1853
1854 let (output, _, _, _) = linker.resolve().unwrap();
1855 assert_eq!(output, vec![0xEB, 0x00, 0x90]);
1857 }
1858
1859 #[test]
1860 fn relaxation_short_jmp_backward() {
1861 let mut linker = Linker::new();
1862 linker.add_label("top", span()).unwrap();
1863 linker.add_fragment(nop());
1864 linker.add_fragment(relaxable_jmp("top"));
1865
1866 let (output, _, _, _) = linker.resolve().unwrap();
1867 assert_eq!(output, vec![0x90, 0xEB, 0xFD]);
1869 }
1870
1871 #[test]
1872 fn relaxation_promotes_jmp_to_long() {
1873 let mut linker = Linker::new();
1874 linker.add_fragment(relaxable_jmp("target"));
1875 linker.add_fragment(fixed(vec![0x90; 200], None));
1876 linker.add_label("target", span()).unwrap();
1877 linker.add_fragment(nop());
1878
1879 let (output, _, _, _) = linker.resolve().unwrap();
1880 assert_eq!(output[0], 0xE9); assert_eq!(output.len(), 5 + 200 + 1);
1882 let rel = i32::from_le_bytes([output[1], output[2], output[3], output[4]]);
1883 assert_eq!(rel, 200);
1884 }
1885
1886 #[test]
1887 fn relaxation_short_jcc() {
1888 let mut linker = Linker::new();
1889 linker.add_fragment(relaxable_jcc(0x4, "done")); linker.add_label("done", span()).unwrap();
1891 linker.add_fragment(nop());
1892
1893 let (output, _, _, _) = linker.resolve().unwrap();
1894 assert_eq!(output, vec![0x74, 0x00, 0x90]);
1895 }
1896
1897 #[test]
1898 fn relaxation_promotes_jcc_to_long() {
1899 let mut linker = Linker::new();
1900 linker.add_fragment(relaxable_jcc(0x4, "done"));
1901 linker.add_fragment(fixed(vec![0x90; 200], None));
1902 linker.add_label("done", span()).unwrap();
1903 linker.add_fragment(nop());
1904
1905 let (output, _, _, _) = linker.resolve().unwrap();
1906 assert_eq!(output[0], 0x0F);
1907 assert_eq!(output[1], 0x84);
1908 let rel = i32::from_le_bytes([output[2], output[3], output[4], output[5]]);
1909 assert_eq!(rel, 200);
1910 }
1911
1912 #[test]
1913 fn relaxation_boundary_127() {
1914 let mut linker = Linker::new();
1916 linker.add_fragment(relaxable_jmp("target"));
1917 linker.add_fragment(fixed(vec![0x90; 125], None)); linker.add_label("target", span()).unwrap();
1919 linker.add_fragment(nop());
1920
1921 let (output, _, _, _) = linker.resolve().unwrap();
1922 assert_eq!(output[0], 0xEB); assert_eq!(output[1], 125u8); }
1925
1926 #[test]
1927 fn relaxation_boundary_128() {
1928 let mut linker = Linker::new();
1931 linker.add_fragment(relaxable_jmp("target"));
1932 linker.add_fragment(fixed(vec![0x90; 128], None));
1933 linker.add_label("target", span()).unwrap();
1934 linker.add_fragment(nop());
1935
1936 let (output, _, _, _) = linker.resolve().unwrap();
1937 assert_eq!(output[0], 0xE9); }
1939
1940 #[test]
1941 fn cascading_relaxation() {
1942 let mut linker = Linker::new();
1944
1945 linker.add_fragment(relaxable_jmp("L1"));
1947 linker.add_fragment(fixed(vec![0x90; 125], None));
1949 linker.add_fragment(relaxable_jcc(0x5, "L2"));
1951
1952 linker.add_label("L1", span()).unwrap();
1953 linker.add_fragment(fixed(vec![0x90; 130], None));
1954 linker.add_label("L2", span()).unwrap();
1955 linker.add_fragment(nop());
1956
1957 let (output, _, _, _) = linker.resolve().unwrap();
1958 assert_eq!(output[0], 0xE9); assert_eq!(output[5 + 125], 0x0F); assert_eq!(output[5 + 125 + 1], 0x85);
1962 }
1963
1964 #[test]
1967 fn alignment_fragment() {
1968 let mut linker = Linker::new();
1969 linker.add_fragment(nop()); linker.add_alignment(4, 0x00, None, false, span());
1971 linker.add_fragment(nop());
1972
1973 let (output, _, _, _) = linker.resolve().unwrap();
1974 assert_eq!(output, vec![0x90, 0x00, 0x00, 0x00, 0x90]);
1975 }
1976
1977 #[test]
1978 fn alignment_already_aligned() {
1979 let mut linker = Linker::new();
1980 linker.add_fragment(fixed(vec![0x90; 4], None));
1981 linker.add_alignment(4, 0xCC, None, false, span());
1982 linker.add_fragment(nop());
1983
1984 let (output, _, _, _) = linker.resolve().unwrap();
1985 assert_eq!(output, vec![0x90, 0x90, 0x90, 0x90, 0x90]);
1986 }
1987
1988 #[test]
1989 fn alignment_with_base_address() {
1990 let mut linker = Linker::new();
1991 linker.set_base_address(0x1001); linker.add_alignment(4, 0xCC, None, false, span());
1993 linker.add_fragment(nop());
1994
1995 let (output, _, _, _) = linker.resolve().unwrap();
1996 assert_eq!(output, vec![0xCC, 0xCC, 0xCC, 0x90]);
1998 }
1999
2000 #[test]
2003 fn label_table_exported() {
2004 let mut linker = Linker::new();
2005 linker.add_label("start", span()).unwrap();
2006 linker.add_fragment(nop());
2007 linker.add_fragment(nop());
2008 linker.add_label("end", span()).unwrap();
2009 linker.add_fragment(nop());
2010
2011 let (_, labels, _, _) = linker.resolve().unwrap();
2012 let m: BTreeMap<String, u64> = labels.into_iter().collect();
2013 assert_eq!(m["start"], 0);
2014 assert_eq!(m["end"], 2);
2015 }
2016
2017 #[test]
2018 fn label_table_with_base_address() {
2019 let mut linker = Linker::new();
2020 linker.set_base_address(0x1000);
2021 linker.add_label("func", span()).unwrap();
2022 linker.add_fragment(fixed(vec![0x90; 10], None));
2023
2024 let (_, labels, _, _) = linker.resolve().unwrap();
2025 assert_eq!(labels[0].1, 0x1000);
2026 }
2027
2028 #[test]
2031 fn multiple_fragments_no_reloc() {
2032 let mut linker = Linker::new();
2033 linker.add_fragment(nop());
2034 linker.add_fragment(fixed(vec![0xCC], None));
2035 linker.add_fragment(fixed(vec![0xC3], None));
2036 let (output, _, _, _) = linker.resolve().unwrap();
2037 assert_eq!(output, vec![0x90, 0xCC, 0xC3]);
2038 }
2039
2040 #[test]
2041 fn empty_linker() {
2042 let mut linker = Linker::new();
2043 let (output, labels, _, _) = linker.resolve().unwrap();
2044 assert!(output.is_empty());
2045 assert!(labels.is_empty());
2046 }
2047
2048 #[test]
2049 fn relocation_with_addend() {
2050 let mut linker = Linker::new();
2051 linker.add_label("data", span()).unwrap();
2052 linker.add_fragment(fixed(vec![0; 16], None));
2053 linker.add_fragment(fixed(
2054 vec![0x48, 0x8D, 0x05, 0, 0, 0, 0],
2055 Some(Relocation {
2056 offset: 3,
2057 size: 4,
2058 label: alloc::rc::Rc::from("data"),
2059 kind: RelocKind::X86Relative,
2060 addend: 4,
2061 trailing_bytes: 0,
2062 }),
2063 ));
2064
2065 let (output, _, _, _) = linker.resolve().unwrap();
2066 let rel = i32::from_le_bytes([output[19], output[20], output[21], output[22]]);
2067 assert_eq!(rel, -19);
2068 }
2069
2070 #[test]
2073 fn relaxation_with_alignment() {
2074 let mut linker = Linker::new();
2075 linker.add_label("top", span()).unwrap();
2076 linker.add_fragment(nop()); linker.add_alignment(16, 0xCC, None, false, span()); linker.add_fragment(relaxable_jcc(0x5, "top"));
2080
2081 let (output, _, _, _) = linker.resolve().unwrap();
2082 assert_eq!(output[0], 0x90);
2084 assert_eq!(output[16], 0x75); let disp = output[17] as i8;
2086 assert_eq!(disp, -18);
2087 }
2088
2089 #[test]
2092 fn add_encoded_creates_relaxable() {
2093 let mut linker = Linker::new();
2094 linker
2095 .add_encoded(
2096 InstrBytes::from_slice(&[0xE9, 0, 0, 0, 0]),
2097 Some(Relocation {
2098 offset: 1,
2099 size: 4,
2100 label: alloc::rc::Rc::from("target"),
2101 kind: RelocKind::X86Relative,
2102 addend: 0,
2103 trailing_bytes: 0,
2104 }),
2105 Some(RelaxInfo {
2106 short_bytes: InstrBytes::from_slice(&[0xEB, 0x00]),
2107 short_reloc_offset: 1,
2108 short_relocation: None,
2109 }),
2110 span(),
2111 )
2112 .unwrap();
2113 linker.add_label("target", span()).unwrap();
2114 linker.add_fragment(nop());
2115
2116 let (output, _, _, _) = linker.resolve().unwrap();
2117 assert_eq!(output, vec![0xEB, 0x00, 0x90]);
2118 }
2119
2120 #[test]
2121 fn add_encoded_creates_fixed() {
2122 let mut linker = Linker::new();
2123 linker
2124 .add_encoded(InstrBytes::from_slice(&[0x90]), None, None, span())
2125 .unwrap();
2126
2127 let (output, _, _, _) = linker.resolve().unwrap();
2128 assert_eq!(output, vec![0x90]);
2129 }
2130
2131 #[test]
2134 fn alignment_with_nop_padding() {
2135 let mut linker = Linker::new();
2136 linker.add_fragment(nop()); linker.add_alignment(4, 0x00, None, true, span());
2139 linker.add_fragment(nop());
2140
2141 let (output, _, _, _) = linker.resolve().unwrap();
2142 assert_eq!(output.len(), 5);
2144 assert_eq!(output[0], 0x90); assert_eq!(&output[1..4], &[0x0F, 0x1F, 0x00]);
2147 assert_eq!(output[4], 0x90); }
2149
2150 #[test]
2151 fn alignment_nop_padding_large() {
2152 let mut linker = Linker::new();
2153 linker.add_fragment(nop()); linker.add_alignment(16, 0x00, None, true, span());
2156 linker.add_fragment(nop());
2157
2158 let (output, _, _, _) = linker.resolve().unwrap();
2159 assert_eq!(output.len(), 17);
2161 assert_eq!(output[0], 0x90);
2162 assert_eq!(
2164 &output[1..10],
2165 &[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]
2166 );
2167 assert_eq!(&output[10..16], &[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
2168 assert_eq!(output[16], 0x90);
2169 }
2170
2171 #[test]
2172 fn alignment_max_skip_respected() {
2173 let mut linker = Linker::new();
2174 linker.add_fragment(nop()); linker.add_alignment(16, 0x00, Some(2), false, span());
2177 linker.add_fragment(nop());
2178
2179 let (output, _, _, _) = linker.resolve().unwrap();
2180 assert_eq!(output, vec![0x90, 0x90]);
2182 }
2183
2184 #[test]
2185 fn alignment_max_skip_allows_small_padding() {
2186 let mut linker = Linker::new();
2187 linker.add_fragment(fixed(vec![0x90; 3], None)); linker.add_alignment(4, 0xCC, Some(2), false, span());
2190 linker.add_fragment(nop());
2191
2192 let (output, _, _, _) = linker.resolve().unwrap();
2193 assert_eq!(output, vec![0x90, 0x90, 0x90, 0xCC, 0x90]);
2194 }
2195
2196 #[test]
2199 fn org_forward_padding() {
2200 let mut linker = Linker::new();
2201 linker.set_base_address(0x100);
2202 linker.add_fragment(nop()); linker.add_org(0x110, 0x00, span()); linker.add_fragment(nop());
2205
2206 let (output, _, _, _) = linker.resolve().unwrap();
2207 assert_eq!(output.len(), 17);
2209 assert_eq!(output[0], 0x90);
2210 assert!(output[1..16].iter().all(|&b| b == 0x00));
2212 assert_eq!(output[16], 0x90);
2213 }
2214
2215 #[test]
2216 fn org_already_at_target() {
2217 let mut linker = Linker::new();
2218 linker.set_base_address(0x100);
2219 linker.add_fragment(fixed(vec![0x90; 16], None)); linker.add_org(0x110, 0x00, span()); linker.add_fragment(nop());
2222
2223 let (output, _, _, _) = linker.resolve().unwrap();
2224 assert_eq!(output.len(), 17); }
2226
2227 #[test]
2228 fn org_backward_error() {
2229 let mut linker = Linker::new();
2230 linker.set_base_address(0x200);
2231 linker.add_fragment(fixed(vec![0x90; 16], None)); linker.add_org(0x100, 0x00, span()); let err = linker.resolve().unwrap_err();
2235 assert!(matches!(err, AsmError::Syntax { .. }));
2236 }
2237
2238 #[test]
2239 fn org_with_labels() {
2240 let mut linker = Linker::new();
2241 linker.set_base_address(0x1000);
2242 linker.add_fragment(nop());
2243 linker.add_org(0x1010, 0x00, span());
2244 linker.add_label("after_org", span()).unwrap();
2245 linker.add_fragment(nop());
2246
2247 let (_, labels, _, _) = linker.resolve().unwrap();
2248 let m: BTreeMap<String, u64> = labels.into_iter().collect();
2249 assert_eq!(m["after_org"], 0x1010);
2250 }
2251
2252 #[test]
2255 fn relaxable_jmp_with_positive_addend() {
2256 let mut linker = Linker::new();
2262 linker.add_label("target", span()).unwrap();
2263 linker.add_fragment(nop());
2264 linker.add_fragment(Fragment::Relaxable {
2265 short_bytes: InstrBytes::from_slice(&[0xEB, 0x00]),
2266 short_reloc_offset: 1,
2267 short_relocation: None,
2268 long_bytes: InstrBytes::from_slice(&[0xE9, 0, 0, 0, 0]),
2269 long_relocation: Relocation {
2270 offset: 1,
2271 size: 4,
2272 label: alloc::rc::Rc::from("target"),
2273 kind: RelocKind::X86Relative,
2274 addend: 1,
2275 trailing_bytes: 0,
2276 },
2277 is_long: false,
2278 span: span(),
2279 });
2280
2281 let (output, _, _, _) = linker.resolve().unwrap();
2282 assert_eq!(output, vec![0x90, 0xEB, 0xFE_u8]); }
2285
2286 #[test]
2287 fn relaxable_jmp_addend_forces_long_form() {
2288 let mut linker = Linker::new();
2294 linker.add_label("target", span()).unwrap();
2295 linker.add_fragment(fixed(vec![0x90; 126], None));
2297 linker.add_fragment(Fragment::Relaxable {
2298 short_bytes: InstrBytes::from_slice(&[0xEB, 0x00]),
2299 short_reloc_offset: 1,
2300 short_relocation: None,
2301 long_bytes: InstrBytes::from_slice(&[0xE9, 0, 0, 0, 0]),
2302 long_relocation: Relocation {
2303 offset: 1,
2304 size: 4,
2305 label: alloc::rc::Rc::from("target"),
2306 kind: RelocKind::X86Relative,
2307 addend: -200,
2308 trailing_bytes: 0,
2309 },
2310 is_long: false,
2311 span: span(),
2312 });
2313
2314 let (output, _, _, _) = linker.resolve().unwrap();
2315 assert_eq!(output.len(), 126 + 5); let disp = i32::from_le_bytes([output[127], output[128], output[129], output[130]]);
2319 assert_eq!(disp, -331);
2320 }
2321}