1use alloc::{borrow::Cow, collections::BinaryHeap, vec::Vec};
2use core::fmt;
3
4use smallvec::SmallVec;
5
6use crate::AsmError;
7use crate::core::arch_traits::Arch;
8use crate::core::patch::{
9 PatchBlock, PatchBlockId, PatchCatalog, PatchSite, PatchSiteId, PatchableBlock, fill_with_nops,
10 minimum_patch_alignment,
11};
12#[cfg(feature = "riscv")]
13use crate::riscv;
14
15#[cfg(feature = "jit")]
16use crate::core::jit_allocator::{JitAllocator, Span};
17
18use super::{
19 operand::{Label, Sym},
20 target::Environment,
21};
22
23pub struct CodeBuffer {
31 env: Environment,
32 data: SmallVec<[u8; 1024]>,
33 relocs: SmallVec<[AsmReloc; 16]>,
34 symbols: SmallVec<[SymData; 16]>,
35 defined_symbols: SmallVec<[(ExternalName, Label); 4]>,
36 label_offsets: SmallVec<[CodeOffset; 16]>,
37 pending_fixup_records: SmallVec<[AsmFixup; 16]>,
38 pending_fixup_deadline: u32,
39 pending_constants: SmallVec<[Constant; 16]>,
40 pending_constants_size: CodeOffset,
41 used_constants: SmallVec<[(Constant, CodeOffset); 4]>,
42 constants: SmallVec<[(ConstantData, AsmConstant); 4]>,
43 fixup_records: BinaryHeap<AsmFixup>,
44 patch_blocks: SmallVec<[PendingPatchBlock; 4]>,
45 patch_sites: SmallVec<[PendingPatchSite; 8]>,
46 #[cfg(feature = "x86")]
47 x86_branch_relaxations: SmallVec<[X86BranchRelaxation; 8]>,
48 #[cfg(feature = "x86")]
49 alignment_constraints: SmallVec<[(CodeOffset, CodeOffset); 4]>,
50 error: Option<AsmError>,
51}
52
53#[derive(Clone, Copy)]
59#[cfg(any(feature = "x86", feature = "aarch64", feature = "riscv"))]
60pub(crate) struct EmissionCheckpoint {
61 data_len: usize,
62 relocs_len: usize,
63 symbols_len: usize,
64 defined_symbols_len: usize,
65 label_offsets_len: usize,
66 pending_fixup_records_len: usize,
67 pending_fixup_deadline: u32,
68 pending_constants_len: usize,
69 pending_constants_size: CodeOffset,
70 used_constants_len: usize,
71 constants_len: usize,
72 patch_blocks_len: usize,
73 patch_sites_len: usize,
74 #[cfg(feature = "x86")]
75 x86_branch_relaxations_len: usize,
76 #[cfg(feature = "x86")]
77 alignment_constraints_len: usize,
78}
79
80#[cfg(feature = "x86")]
81#[derive(Clone, Copy)]
82struct X86BranchRelaxation {
83 opcode_offset: CodeOffset,
84 label: Label,
85 short_opcode: u8,
86 near_size: u8,
87}
88
89#[derive(Clone, Copy)]
90struct PendingPatchBlock {
91 offset: CodeOffset,
92 size: CodeOffset,
93 align: CodeOffset,
94}
95
96#[derive(Clone, Copy)]
97enum PendingPatchTarget {
98 Offset(CodeOffset),
99 Label(Label),
100}
101
102#[derive(Clone, Copy)]
103struct PendingPatchSite {
104 offset: CodeOffset,
105 kind: LabelUse,
106 target: PendingPatchTarget,
107 addend: i64,
108}
109
110#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
116pub struct UserExternalName {
117 pub namespace: u32,
118 pub index: u32,
119}
120
121impl UserExternalName {
122 pub const fn new(namespace: u32, index: u32) -> Self {
124 Self { namespace, index }
125 }
126}
127
128impl fmt::Display for UserExternalName {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "u{}:{}", self.namespace, self.index)
131 }
132}
133
134#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
140pub enum ExternalName {
141 Symbol(Cow<'static, str>),
142 User(UserExternalName),
143}
144
145impl ExternalName {
146 pub const fn user(namespace: u32, index: u32) -> Self {
148 Self::User(UserExternalName::new(namespace, index))
149 }
150}
151
152impl fmt::Display for ExternalName {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 match self {
155 Self::Symbol(name) => f.write_str(name),
156 Self::User(name) => fmt::Display::fmt(name, f),
157 }
158 }
159}
160
161impl From<UserExternalName> for ExternalName {
162 fn from(name: UserExternalName) -> Self {
163 Self::User(name)
164 }
165}
166
167impl From<&'static str> for ExternalName {
168 fn from(name: &'static str) -> Self {
169 Self::Symbol(Cow::Borrowed(name))
170 }
171}
172
173impl From<alloc::string::String> for ExternalName {
174 fn from(name: alloc::string::String) -> Self {
175 Self::Symbol(Cow::Owned(name))
176 }
177}
178
179impl From<Cow<'static, str>> for ExternalName {
180 fn from(name: Cow<'static, str>) -> Self {
181 Self::Symbol(name)
182 }
183}
184#[derive(Clone, PartialEq, Eq, Hash, Debug)]
185pub enum RelocTarget {
186 Sym(Sym),
187 Label(Label),
188}
189
190#[derive(Copy, Clone, PartialEq, Eq, Debug)]
191pub enum RelocDistance {
192 Near,
193 Far,
194}
195
196#[derive(Clone, PartialEq, Eq)]
197pub(crate) struct SymData {
198 pub(crate) name: ExternalName,
199 pub(crate) distance: RelocDistance,
200}
201
202#[derive(Clone, PartialEq, Eq, Hash, Debug)]
204pub struct AsmReloc {
205 pub offset: CodeOffset,
206 pub kind: Reloc,
207 pub addend: i64,
208 pub target: RelocTarget,
209}
210#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
214pub(crate) struct AsmFixup {
215 pub label: Label,
216 pub offset: CodeOffset,
217 pub kind: LabelUse,
218}
219
220impl AsmFixup {
221 fn deadline(&self) -> CodeOffset {
222 self.offset.saturating_sub(self.kind.max_pos_range())
223 }
224}
225
226#[derive(Clone, Copy)]
228struct AsmConstant {
229 upcoming_label: Option<Label>,
235 align: CodeOffset,
237 size: usize,
239}
240
241pub struct CodeBufferFinalized {
244 pub(crate) data: SmallVec<[u8; 1024]>,
245 pub(crate) relocs: SmallVec<[AsmReloc; 16]>,
246 pub(crate) symbols: SmallVec<[SymData; 16]>,
247 pub(crate) label_offsets: SmallVec<[CodeOffset; 16]>,
248 pub(crate) defined_symbols: SmallVec<[(ExternalName, CodeOffset); 4]>,
249 pub(crate) alignment: u32,
250 pub(crate) patch_catalog: PatchCatalog,
251}
252
253#[cfg(feature = "jit")]
255pub struct LoadedRelocatedCode {
256 span: Span,
257 code_size: usize,
258 got_targets: Vec<RelocTarget>,
259}
260
261#[cfg(feature = "jit")]
262impl LoadedRelocatedCode {
263 pub const fn rx(&self) -> *const u8 {
264 self.span.rx()
265 }
266
267 pub const fn rw(&self) -> *mut u8 {
268 self.span.rw()
269 }
270
271 pub const fn span(&self) -> &Span {
272 &self.span
273 }
274
275 pub const fn code_size(&self) -> usize {
276 self.code_size
277 }
278
279 pub fn got_targets(&self) -> &[RelocTarget] {
280 &self.got_targets
281 }
282
283 pub fn got_size(&self) -> usize {
284 self.got_targets.len() * core::mem::size_of::<usize>()
285 }
286
287 pub fn got_rx(&self) -> *const u8 {
288 self.rx().wrapping_add(self.code_size)
289 }
290
291 pub fn got_rw(&self) -> *mut u8 {
292 self.rw().wrapping_add(self.code_size)
293 }
294}
295
296pub fn reloc_uses_got(kind: Reloc) -> bool {
297 matches!(
298 kind,
299 Reloc::X86GOTPCRel4
300 | Reloc::RiscvGotHi20
301 | Reloc::RiscvPCRelLo12I
302 | Reloc::Aarch64AdrGotPage21
303 | Reloc::Aarch64Ld64GotLo12Nc
304 )
305}
306
307#[cfg(feature = "jit")]
308pub(crate) fn got_slot_index(got_targets: &[RelocTarget], target: &RelocTarget) -> Option<usize> {
309 got_targets.iter().position(|item| item == target)
310}
311
312impl CodeBufferFinalized {
313 pub fn total_size(&self) -> usize {
314 self.data.len()
315 }
316
317 pub fn data(&self) -> &[u8] {
318 &self.data[..]
319 }
320
321 pub fn symbol_name(&self, sym: Sym) -> Option<&ExternalName> {
322 self.symbols
323 .get(sym.id() as usize)
324 .map(|symbol| &symbol.name)
325 }
326
327 pub fn symbol_distance(&self, sym: Sym) -> Option<RelocDistance> {
328 self.symbols
329 .get(sym.id() as usize)
330 .map(|symbol| symbol.distance)
331 }
332
333 pub fn defined_symbol_offset(&self, name: &ExternalName) -> Option<CodeOffset> {
337 self.defined_symbols
338 .iter()
339 .find(|(defined, _)| defined == name)
340 .map(|(_, offset)| *offset)
341 }
342
343 pub fn defined_symbol_str(&self, name: &str) -> Option<CodeOffset> {
345 self.defined_symbols
346 .iter()
347 .find(|(defined, _)| matches!(defined, ExternalName::Symbol(s) if s.as_ref() == name))
348 .map(|(_, offset)| *offset)
349 }
350
351 pub fn relocs(&self) -> &[AsmReloc] {
352 &self.relocs[..]
353 }
354
355 pub fn alignment(&self) -> u32 {
356 self.alignment
357 }
358
359 #[cfg(feature = "jit")]
364 pub fn allocate(&self, jit_allocator: &mut JitAllocator) -> Result<Span, AsmError> {
365 let mut span = jit_allocator.alloc(self.data().len())?;
366
367 unsafe {
370 jit_allocator.write(&mut span, |span| {
371 span.rw()
372 .copy_from_nonoverlapping(self.data().as_ptr(), self.data().len());
373 })?;
374 }
375
376 Ok(span)
377 }
378
379 #[cfg(feature = "jit")]
387 pub fn allocate_relocated(
388 &self,
389 jit_allocator: &mut JitAllocator,
390 get_address: impl Fn(&RelocTarget) -> *const u8,
391 get_plt_entry: impl Fn(&RelocTarget) -> *const u8,
392 ) -> Result<LoadedRelocatedCode, AsmError> {
393 let mut got_targets = Vec::new();
394
395 for reloc in &self.relocs {
396 if reloc_uses_got(reloc.kind) && !got_targets.iter().any(|item| item == &reloc.target) {
397 got_targets.push(reloc.target.clone());
398 }
399 }
400
401 let got_size = got_targets
402 .len()
403 .checked_mul(core::mem::size_of::<usize>())
404 .ok_or(AsmError::TooLarge)?;
405 let total_size = self
406 .data()
407 .len()
408 .checked_add(got_size)
409 .ok_or(AsmError::TooLarge)?;
410 let mut span = jit_allocator.alloc(total_size)?;
411
412 let mut relocation_result = Ok(());
413 unsafe {
419 jit_allocator.write(&mut span, |span| {
420 relocation_result = (|| {
421 span.rw()
422 .copy_from_nonoverlapping(self.data().as_ptr(), self.data().len());
423
424 let rx = span.rx();
425 let resolve = |target: &RelocTarget,
426 fallback: &dyn Fn(&RelocTarget) -> *const u8|
427 -> Result<*const u8, AsmError> {
428 if let RelocTarget::Label(label) = target {
429 let offset = self
430 .label_offsets
431 .get(label.id() as usize)
432 .copied()
433 .ok_or(AsmError::InvalidArgument)?;
434 if offset == u32::MAX || offset as usize > self.data().len() {
435 return Err(AsmError::UnboundLabel);
436 }
437 return Ok(rx.add(offset as usize));
438 }
439 let address = fallback(target);
440 if address.is_null() {
441 return Err(AsmError::InvalidArgument);
442 }
443 Ok(address)
444 };
445
446 let got_rw = span.rw().add(self.data().len()) as *mut usize;
447 for (index, target) in got_targets.iter().enumerate() {
448 let addr = resolve(target, &get_address)?;
449 got_rw.add(index).write_unaligned(addr.addr());
450 }
451
452 let got_rx = rx.add(self.data().len());
453 perform_relocations(
454 span.rw(),
455 rx,
456 self.data().len(),
457 &self.relocs,
458 |target| resolve(target, &get_address),
459 |target| {
460 let index = got_slot_index(&got_targets, target)
461 .ok_or(AsmError::InvalidState)?;
462 let offset = index
463 .checked_mul(core::mem::size_of::<usize>())
464 .ok_or(AsmError::TooLarge)?;
465 Ok(got_rx.add(offset))
466 },
467 |target| resolve(target, &get_plt_entry),
468 )
469 })();
470 })?;
471 }
472 relocation_result?;
473
474 Ok(LoadedRelocatedCode {
475 span,
476 code_size: self.data().len(),
477 got_targets,
478 })
479 }
480
481 #[cfg(feature = "jit")]
491 pub fn allocate_resolved(
492 &self,
493 jit_allocator: &mut JitAllocator,
494 resolve: impl Fn(&ExternalName) -> *const u8,
495 ) -> Result<LoadedRelocatedCode, AsmError> {
496 let by_name = |target: &RelocTarget| match target {
497 RelocTarget::Sym(sym) => match self.symbol_name(*sym) {
498 Some(name) => resolve(name),
499 None => core::ptr::null(),
500 },
501 RelocTarget::Label(_) => core::ptr::null(),
503 };
504
505 self.allocate_relocated(jit_allocator, by_name, by_name)
506 }
507}
508
509impl CodeBuffer {
510 pub fn new(env: Environment) -> Self {
512 Self {
513 env,
514 data: SmallVec::new(),
515 relocs: SmallVec::new(),
516 symbols: SmallVec::new(),
517 defined_symbols: SmallVec::new(),
518 label_offsets: SmallVec::new(),
519 pending_fixup_records: SmallVec::new(),
520 pending_fixup_deadline: 0,
521 pending_constants: SmallVec::new(),
522 pending_constants_size: 0,
523 used_constants: SmallVec::new(),
524 constants: SmallVec::new(),
525 fixup_records: BinaryHeap::new(),
526 patch_blocks: SmallVec::new(),
527 patch_sites: SmallVec::new(),
528 #[cfg(feature = "x86")]
529 x86_branch_relaxations: SmallVec::new(),
530 #[cfg(feature = "x86")]
531 alignment_constraints: SmallVec::new(),
532 error: None,
533 }
534 }
535
536 pub fn host() -> Self {
538 Self::new(Environment::host())
539 }
540
541 pub fn clear(&mut self) {
542 self.data.clear();
543 self.relocs.clear();
544 self.label_offsets.clear();
545 self.pending_fixup_records.clear();
546 self.constants.clear();
547 self.fixup_records.clear();
548 self.symbols.clear();
549 self.defined_symbols.clear();
550 self.used_constants.clear();
551 self.pending_fixup_deadline = 0;
552 self.pending_constants_size = 0;
553 self.pending_constants.clear();
554 self.patch_blocks.clear();
555 self.patch_sites.clear();
556 #[cfg(feature = "x86")]
557 self.x86_branch_relaxations.clear();
558 #[cfg(feature = "x86")]
559 self.alignment_constraints.clear();
560 self.error = None;
561 }
562
563 pub fn error(&self) -> Option<&AsmError> {
565 self.error.as_ref()
566 }
567
568 pub(crate) fn record_error(&mut self, error: AsmError) {
569 if self.error.is_none() {
570 self.error = Some(error);
571 }
572 }
573
574 #[cfg(any(feature = "x86", feature = "aarch64", feature = "riscv"))]
575 pub(crate) fn checkpoint(&self) -> EmissionCheckpoint {
576 EmissionCheckpoint {
577 data_len: self.data.len(),
578 relocs_len: self.relocs.len(),
579 symbols_len: self.symbols.len(),
580 defined_symbols_len: self.defined_symbols.len(),
581 label_offsets_len: self.label_offsets.len(),
582 pending_fixup_records_len: self.pending_fixup_records.len(),
583 pending_fixup_deadline: self.pending_fixup_deadline,
584 pending_constants_len: self.pending_constants.len(),
585 pending_constants_size: self.pending_constants_size,
586 used_constants_len: self.used_constants.len(),
587 constants_len: self.constants.len(),
588 patch_blocks_len: self.patch_blocks.len(),
589 patch_sites_len: self.patch_sites.len(),
590 #[cfg(feature = "x86")]
591 x86_branch_relaxations_len: self.x86_branch_relaxations.len(),
592 #[cfg(feature = "x86")]
593 alignment_constraints_len: self.alignment_constraints.len(),
594 }
595 }
596
597 #[cfg(any(feature = "x86", feature = "aarch64", feature = "riscv"))]
598 pub(crate) fn rollback(&mut self, checkpoint: EmissionCheckpoint) {
599 self.data.truncate(checkpoint.data_len);
600 self.relocs.truncate(checkpoint.relocs_len);
601 self.symbols.truncate(checkpoint.symbols_len);
602 self.defined_symbols
603 .truncate(checkpoint.defined_symbols_len);
604 self.label_offsets.truncate(checkpoint.label_offsets_len);
605 self.pending_fixup_records
606 .truncate(checkpoint.pending_fixup_records_len);
607 self.pending_fixup_deadline = checkpoint.pending_fixup_deadline;
608 self.pending_constants
609 .truncate(checkpoint.pending_constants_len);
610 self.pending_constants_size = checkpoint.pending_constants_size;
611 self.used_constants.truncate(checkpoint.used_constants_len);
612 self.constants.truncate(checkpoint.constants_len);
613 self.patch_blocks.truncate(checkpoint.patch_blocks_len);
614 self.patch_sites.truncate(checkpoint.patch_sites_len);
615 #[cfg(feature = "x86")]
616 self.x86_branch_relaxations
617 .truncate(checkpoint.x86_branch_relaxations_len);
618 #[cfg(feature = "x86")]
619 self.alignment_constraints
620 .truncate(checkpoint.alignment_constraints_len);
621 }
622 pub fn env(&self) -> &Environment {
623 &self.env
624 }
625
626 pub fn data(&self) -> &[u8] {
627 &self.data
628 }
629
630 #[cfg(feature = "x86")]
634 pub(crate) fn byte_at(&self, offset: CodeOffset) -> u8 {
635 self.data[offset as usize]
636 }
637
638 #[cfg(feature = "x86")]
640 pub(crate) fn set_byte_at(&mut self, offset: CodeOffset, value: u8) {
641 self.data[offset as usize] = value;
642 }
643
644 #[cfg(feature = "x86")]
648 pub(crate) fn insert_at(&mut self, offset: CodeOffset, value: u8) {
649 self.data.insert(offset as usize, value);
650 }
651
652 #[cfg(feature = "x86")]
656 pub(crate) fn remove_at(&mut self, offset: CodeOffset) {
657 self.data.remove(offset as usize);
658 }
659
660 #[cfg(feature = "x86")]
661 pub(crate) fn record_x86_branch_relaxation(
662 &mut self,
663 opcode_offset: CodeOffset,
664 label: Label,
665 short_opcode: u8,
666 near_size: u8,
667 ) {
668 debug_assert!(matches!(near_size, 5 | 6));
669 debug_assert!((opcode_offset as usize) + near_size as usize <= self.data.len());
670 debug_assert!(self.label_offsets.get(label.id() as usize).is_some());
671 self.x86_branch_relaxations.push(X86BranchRelaxation {
672 opcode_offset,
673 label,
674 short_opcode,
675 near_size,
676 });
677 }
678
679 pub fn relocs(&self) -> &[AsmReloc] {
680 &self.relocs
681 }
682
683 pub fn put1(&mut self, value: u8) {
684 if self.error.is_some() {
685 return;
686 }
687 self.data.push(value);
688 }
689
690 pub fn put2(&mut self, value: u16) {
691 if self.error.is_some() {
692 return;
693 }
694 self.data.extend_from_slice(&value.to_ne_bytes());
695 }
696
697 pub fn put4(&mut self, value: u32) {
698 if self.error.is_some() {
699 return;
700 }
701 self.data.extend_from_slice(&value.to_ne_bytes());
702 }
703
704 pub fn put8(&mut self, value: u64) {
705 if self.error.is_some() {
706 return;
707 }
708 self.data.extend_from_slice(&value.to_ne_bytes());
709 }
710
711 pub fn write_u8(&mut self, value: u8) {
712 if self.error.is_some() {
713 return;
714 }
715 self.data.push(value);
716 }
717
718 pub fn write_u16(&mut self, value: u16) {
719 if self.error.is_some() {
720 return;
721 }
722 self.data.extend_from_slice(&value.to_ne_bytes());
723 }
724
725 pub fn write_u32(&mut self, value: u32) {
726 if self.error.is_some() {
727 return;
728 }
729 self.data.extend_from_slice(&value.to_ne_bytes());
730 }
731
732 pub fn write_u64(&mut self, value: u64) {
733 if self.error.is_some() {
734 return;
735 }
736 self.data.extend_from_slice(&value.to_ne_bytes());
737 }
738
739 pub fn add_symbol(&mut self, name: impl Into<ExternalName>, distance: RelocDistance) -> Sym {
740 if self.error.is_some() {
741 return Sym::new();
742 }
743 let ix = self.symbols.len();
744 self.symbols.push(SymData {
745 distance,
746 name: name.into(),
747 });
748
749 Sym::from_id(ix as u32)
750 }
751
752 pub fn extern_sym(
760 &mut self,
761 name: impl Into<Cow<'static, str>>,
762 distance: RelocDistance,
763 ) -> Sym {
764 if self.error.is_some() {
765 return Sym::new();
766 }
767 let name = ExternalName::Symbol(name.into());
768 if let Some(ix) = self.symbols.iter().position(|sym| sym.name == name) {
769 return Sym::from_id(ix as u32);
770 }
771
772 self.add_symbol(name, distance)
773 }
774
775 pub fn extern_user(&mut self, namespace: u32, index: u32, distance: RelocDistance) -> Sym {
779 if self.error.is_some() {
780 return Sym::new();
781 }
782 let name = ExternalName::user(namespace, index);
783 if let Some(ix) = self.symbols.iter().position(|sym| sym.name == name) {
784 return Sym::from_id(ix as u32);
785 }
786
787 self.add_symbol(name, distance)
788 }
789
790 pub fn bind_symbol(&mut self, name: impl Into<ExternalName>, label: Label) {
796 if self.error.is_some() {
797 return;
798 }
799 if self.label_offsets.get(label.id() as usize).is_none() {
800 self.record_error(AsmError::InvalidArgument);
801 return;
802 }
803 self.defined_symbols.push((name.into(), label));
804 }
805
806 pub fn symbol_distance(&self, sym: Sym) -> Option<RelocDistance> {
807 self.symbols
808 .get(sym.id() as usize)
809 .map(|symbol| symbol.distance)
810 }
811
812 pub fn symbol_name(&self, sym: Sym) -> Option<&ExternalName> {
813 self.symbols
814 .get(sym.id() as usize)
815 .map(|symbol| &symbol.name)
816 }
817
818 pub fn get_label(&mut self) -> Label {
819 if self.error.is_some() {
820 return Label::new();
821 }
822 let l = self.label_offsets.len();
823 self.label_offsets.push(u32::MAX);
824 Label::from_id(l as _)
825 }
826
827 pub fn is_bound(&self, label: Label) -> bool {
828 self.label_offsets
829 .get(label.id() as usize)
830 .is_some_and(|offset| *offset != u32::MAX)
831 }
832
833 pub fn label_count(&self) -> u32 {
835 self.label_offsets.len() as u32
836 }
837
838 pub fn get_label_for_constant(&mut self, constant: Constant) -> Label {
839 if self.error.is_some() {
840 return Label::new();
841 }
842 let Some((_, metadata)) = self.constants.get(constant.0 as usize) else {
843 self.record_error(AsmError::InvalidArgument);
844 return Label::new();
845 };
846 let metadata = *metadata;
847 let AsmConstant {
848 upcoming_label,
849 size,
850 ..
851 } = metadata;
852 if let Some(label) = upcoming_label {
853 return label;
854 }
855
856 let label = self.get_label();
857 self.pending_constants.push(constant);
858 self.pending_constants_size += size as u32;
859 self.constants[constant.0 as usize].1.upcoming_label = Some(label);
860 label
861 }
862
863 pub fn add_constant(&mut self, constant: impl Into<ConstantData>) -> Constant {
864 if self.error.is_some() {
865 return Constant(u32::MAX);
866 }
867 let c = self.constants.len() as u32;
868 let data = constant.into();
869 let x = AsmConstant {
870 upcoming_label: None,
871 align: data.alignment() as _,
872 size: data.as_slice().len(),
873 };
874 self.constants.push((data, x));
875 Constant(c)
876 }
877
878 pub fn use_label_at_offset(&mut self, offset: CodeOffset, label: Label, kind: LabelUse) {
879 if self.error.is_some() {
880 return;
881 }
882 if let Err(error) = kind.validate_for_arch(self.env.arch()) {
883 self.record_error(error);
884 return;
885 }
886 if (offset as usize).checked_add(kind.patch_size()).is_none() {
887 self.record_error(AsmError::TooLarge);
888 return;
889 }
890 if self.label_offsets.get(label.id() as usize).is_none() {
891 self.record_error(AsmError::InvalidArgument);
892 return;
893 }
894 let fixup = AsmFixup {
895 kind,
896 label,
897 offset,
898 };
899
900 self.pending_fixup_records.push(fixup);
901 self.pending_fixup_deadline = self.pending_fixup_deadline.min(fixup.deadline());
902 }
903
904 pub fn try_align_to(&mut self, align_to: CodeOffset) -> Result<(), AsmError> {
906 if let Some(error) = self.error.clone() {
907 return Err(error);
908 }
909 if !align_to.is_power_of_two() {
910 return Err(AsmError::InvalidArgument);
911 }
912 while self.cur_offset() & (align_to - 1) != 0 {
913 self.write_u8(0);
914 }
915 #[cfg(feature = "x86")]
916 if align_to > 1 {
917 self.alignment_constraints
918 .push((self.cur_offset(), align_to));
919 }
920 Ok(())
921 }
922
923 pub fn align_to(&mut self, align_to: CodeOffset) {
924 if let Err(error) = self.try_align_to(align_to) {
925 self.record_error(error);
926 }
927 }
928
929 pub fn cur_offset(&self) -> CodeOffset {
930 self.data.len() as _
931 }
932
933 pub fn try_bind_label(&mut self, label: Label) -> Result<(), AsmError> {
934 if let Some(error) = self.error.clone() {
935 return Err(error);
936 }
937 let current_offset = self.cur_offset();
938 let Some(offset) = self.label_offsets.get_mut(label.id() as usize) else {
939 return Err(AsmError::InvalidArgument);
940 };
941 if *offset != u32::MAX {
942 return Err(AsmError::InvalidState);
943 }
944 *offset = current_offset;
945 Ok(())
946 }
947
948 pub fn bind_label(&mut self, label: Label) {
949 if self.error.is_some() {
950 return;
951 }
952 if let Err(error) = self.try_bind_label(label) {
953 self.record_error(error);
954 }
955 }
956
957 pub fn label_offset(&self, label: Label) -> u32 {
958 self.label_offsets
959 .get(label.id() as usize)
960 .copied()
961 .unwrap_or(u32::MAX)
962 }
963
964 pub fn add_reloc(&mut self, kind: Reloc, target: RelocTarget, addend: i64) {
965 let offset = self.cur_offset();
966 self.add_reloc_at_offset(offset, kind, target, addend);
967 }
968
969 pub fn add_reloc_at_offset(
970 &mut self,
971 offset: CodeOffset,
972 kind: Reloc,
973 target: RelocTarget,
974 addend: i64,
975 ) {
976 if self.error.is_some() {
977 return;
978 }
979 if !kind.supports_arch(self.env.arch()) {
980 self.record_error(AsmError::InvalidArch);
981 return;
982 }
983 let valid_target = match target {
984 RelocTarget::Sym(sym) => self.symbols.get(sym.id() as usize).is_some(),
985 RelocTarget::Label(label) => self.label_offsets.get(label.id() as usize).is_some(),
986 };
987 if !valid_target {
988 self.record_error(AsmError::InvalidArgument);
989 return;
990 }
991 self.relocs.push(AsmReloc {
992 addend,
993 kind,
994 offset,
995 target,
996 })
997 }
998
999 pub fn reserve_patch_block(
1004 &mut self,
1005 size: CodeOffset,
1006 align: CodeOffset,
1007 ) -> Result<PatchableBlock, AsmError> {
1008 if let Some(error) = self.error.clone() {
1009 return Err(error);
1010 }
1011 let min_align = minimum_patch_alignment(self.env.arch());
1012 let align = align.max(min_align);
1013 if size == 0 || !align.is_power_of_two() {
1014 return Err(AsmError::InvalidArgument);
1015 }
1016 let nop_size = match self.env.arch() {
1017 Arch::X86 | Arch::X64 => 1,
1018 Arch::AArch64 | Arch::RISCV32 | Arch::RISCV64 => 4,
1019 _ => return Err(AsmError::InvalidArch),
1020 };
1021 if size as usize % nop_size != 0 {
1022 return Err(AsmError::InvalidArgument);
1023 }
1024
1025 self.try_align_to(align)?;
1026 let arch = self.env.arch();
1027 let offset = self.cur_offset();
1028 let block = self.get_appended_space(size as usize);
1029 fill_with_nops(arch, block)?;
1030
1031 self.patch_blocks.push(PendingPatchBlock {
1032 offset,
1033 size,
1034 align,
1035 });
1036 Ok(unsafe { PatchableBlock::new(offset, size, arch) })
1038 }
1039
1040 pub fn record_patch_block(
1041 &mut self,
1042 offset: CodeOffset,
1043 size: CodeOffset,
1044 align: CodeOffset,
1045 ) -> PatchBlockId {
1046 match self.try_record_patch_block(offset, size, align) {
1047 Ok(id) => id,
1048 Err(error) => {
1049 self.record_error(error);
1050 PatchBlockId::from_index(usize::MAX)
1051 }
1052 }
1053 }
1054
1055 pub fn try_record_patch_block(
1056 &mut self,
1057 offset: CodeOffset,
1058 size: CodeOffset,
1059 align: CodeOffset,
1060 ) -> Result<PatchBlockId, AsmError> {
1061 if let Some(error) = self.error.clone() {
1062 return Err(error);
1063 }
1064 if size == 0 || align == 0 || !align.is_power_of_two() || offset & (align - 1) != 0 {
1065 return Err(AsmError::InvalidArgument);
1066 }
1067 let end = (offset as usize)
1068 .checked_add(size as usize)
1069 .ok_or(AsmError::TooLarge)?;
1070 if end > self.data.len() {
1071 return Err(AsmError::InvalidArgument);
1072 }
1073 let id = PatchBlockId::from_index(self.patch_blocks.len());
1074 self.patch_blocks.push(PendingPatchBlock {
1075 offset,
1076 size,
1077 align,
1078 });
1079 Ok(id)
1080 }
1081
1082 pub fn record_patch_site(
1083 &mut self,
1084 offset: CodeOffset,
1085 kind: LabelUse,
1086 target_offset: CodeOffset,
1087 ) -> PatchSiteId {
1088 match self.try_record_patch_site(offset, kind, target_offset) {
1089 Ok(id) => id,
1090 Err(error) => {
1091 self.record_error(error);
1092 PatchSiteId::from_index(usize::MAX)
1093 }
1094 }
1095 }
1096
1097 pub fn try_record_patch_site(
1098 &mut self,
1099 offset: CodeOffset,
1100 kind: LabelUse,
1101 target_offset: CodeOffset,
1102 ) -> Result<PatchSiteId, AsmError> {
1103 if let Some(error) = self.error.clone() {
1104 return Err(error);
1105 }
1106 self.validate_patch_site_offset(offset, kind)?;
1107 let id = PatchSiteId::from_index(self.patch_sites.len());
1108 self.patch_sites.push(PendingPatchSite {
1109 offset,
1110 kind,
1111 target: PendingPatchTarget::Offset(target_offset),
1112 addend: 0,
1113 });
1114 Ok(id)
1115 }
1116
1117 pub fn record_label_patch_site(
1118 &mut self,
1119 offset: CodeOffset,
1120 label: Label,
1121 kind: LabelUse,
1122 ) -> PatchSiteId {
1123 match self.try_record_label_patch_site(offset, label, kind) {
1124 Ok(id) => id,
1125 Err(error) => {
1126 self.record_error(error);
1127 PatchSiteId::from_index(usize::MAX)
1128 }
1129 }
1130 }
1131
1132 pub fn try_record_label_patch_site(
1133 &mut self,
1134 offset: CodeOffset,
1135 label: Label,
1136 kind: LabelUse,
1137 ) -> Result<PatchSiteId, AsmError> {
1138 if let Some(error) = self.error.clone() {
1139 return Err(error);
1140 }
1141 if self.label_offsets.get(label.id() as usize).is_none() {
1142 return Err(AsmError::InvalidArgument);
1143 }
1144 self.validate_patch_site_offset(offset, kind)?;
1145 let id = PatchSiteId::from_index(self.patch_sites.len());
1146 self.patch_sites.push(PendingPatchSite {
1147 offset,
1148 kind,
1149 target: PendingPatchTarget::Label(label),
1150 addend: 0,
1151 });
1152 Ok(id)
1153 }
1154
1155 fn validate_patch_site_offset(
1156 &self,
1157 offset: CodeOffset,
1158 kind: LabelUse,
1159 ) -> Result<(), AsmError> {
1160 kind.validate_for_arch(self.env.arch())?;
1161 let end = (offset as usize)
1162 .checked_add(kind.patch_size())
1163 .ok_or(AsmError::TooLarge)?;
1164 if end > self.data.len() {
1165 return Err(AsmError::InvalidArgument);
1166 }
1167 Ok(())
1168 }
1169
1170 fn handle_fixup(&mut self, fixup: AsmFixup) -> Result<(), AsmError> {
1171 let AsmFixup {
1172 kind,
1173 label,
1174 offset,
1175 } = fixup;
1176 let start = offset;
1177 let end = (offset as usize)
1178 .checked_add(kind.patch_size())
1179 .ok_or(AsmError::TooLarge)?;
1180 if end > self.data.len() {
1181 return Err(AsmError::InvalidArgument);
1182 }
1183
1184 let label_offset = self.label_offset(label);
1185 if label_offset != u32::MAX {
1186 if !kind.can_reach(offset, label_offset) {
1187 if label_offset < offset {
1188 self.emit_veneer(label, offset, kind)?;
1189 } else {
1190 return Err(AsmError::TooLarge);
1191 }
1192 } else {
1193 let slice = &mut self.data[start as usize..end];
1194
1195 kind.patch(slice, start, label_offset);
1196 }
1197 } else {
1198 self.emit_veneer(label, offset, kind)?;
1202 }
1203 Ok(())
1204 }
1205
1206 pub fn emit_veneer(
1213 &mut self,
1214 label: Label,
1215 offset: CodeOffset,
1216 kind: LabelUse,
1217 ) -> Result<(), AsmError> {
1218 if let Some(error) = self.error.clone() {
1219 return Err(error);
1220 }
1221 kind.validate_for_arch(self.env.arch())?;
1222 if !kind.supports_veneer() {
1223 return Err(AsmError::UnsupportedInstruction {
1224 reason: "branch range requires an unsupported veneer",
1225 });
1226 }
1227 if self.label_offsets.get(label.id() as usize).is_none() {
1228 return Err(AsmError::InvalidArgument);
1229 }
1230 let start = offset as usize;
1231 let end = start
1232 .checked_add(kind.patch_size())
1233 .ok_or(AsmError::TooLarge)?;
1234 if end > self.data.len() {
1235 return Err(AsmError::InvalidArgument);
1236 }
1237 #[cfg(not(feature = "riscv"))]
1238 {
1239 let _ = (label, offset, kind);
1240 Err(AsmError::UnsupportedInstruction {
1241 reason: "RISC-V veneer support is disabled",
1242 })
1243 }
1244
1245 #[cfg(feature = "riscv")]
1246 {
1247 self.try_align_to(kind.align() as _)?;
1248 let veneer_offset = self.cur_offset();
1249 let slice = &mut self.data[start..end];
1250
1251 kind.patch(slice, offset, veneer_offset);
1252 let veneer_slice = self.get_appended_space(kind.veneer_size());
1253 let (veneer_fixup_off, veneer_label_use) =
1254 kind.generate_veneer(veneer_slice, veneer_offset);
1255
1256 self.use_label_at_offset(veneer_fixup_off, label, veneer_label_use);
1261 if let Some(error) = self.error.clone() {
1262 return Err(error);
1263 }
1264 Ok(())
1265 }
1266 }
1267
1268 pub(crate) fn get_appended_space(&mut self, len: usize) -> &mut [u8] {
1270 let off = self.data.len();
1271 let new_len = self.data.len() + len;
1272 self.data.resize(new_len, 0);
1273 &mut self.data[off..]
1274
1275 }
1277
1278 fn worst_case_end_of_island(&self, distance: CodeOffset) -> CodeOffset {
1284 let island_worst_case_size =
1289 ((self.fixup_records.len() + self.pending_fixup_records.len()) as u32) * 20
1290 + self.pending_constants_size;
1291 self.cur_offset()
1292 .saturating_add(distance)
1293 .saturating_add(island_worst_case_size)
1294 }
1295
1296 fn should_apply_fixup(&self, fixup: &AsmFixup, forced_threshold: CodeOffset) -> bool {
1297 let label_offset = self.label_offset(fixup.label);
1298 label_offset != u32::MAX
1299 || fixup.offset.saturating_add(fixup.kind.max_pos_range()) < forced_threshold
1300 }
1301 pub fn island_needed(&mut self, distance: CodeOffset) -> bool {
1303 let deadline = match self.fixup_records.peek() {
1304 Some(fixup) => fixup
1305 .offset
1306 .saturating_add(fixup.kind.max_pos_range())
1307 .min(self.pending_fixup_deadline),
1308 None => self.pending_fixup_deadline,
1309 };
1310
1311 deadline < u32::MAX && self.worst_case_end_of_island(distance) > deadline
1312 }
1313
1314 pub fn emit_island(&mut self, distance: CodeOffset) -> Result<(), AsmError> {
1316 if let Some(error) = self.error.clone() {
1317 return Err(error);
1318 }
1319 let forced_threshold = self.worst_case_end_of_island(distance);
1320
1321 for constant in core::mem::take(&mut self.pending_constants) {
1322 let (_, AsmConstant { align, size, .. }) = self.constants[constant.0 as usize];
1323 let label = match self.constants[constant.0 as usize].1.upcoming_label.take() {
1324 Some(label) => label,
1325 None => unreachable!("pending constants always have an upcoming label"),
1326 };
1327 self.try_align_to(align as _)?;
1328 self.try_bind_label(label)?;
1329 self.used_constants.push((constant, self.cur_offset()));
1330 self.get_appended_space(size);
1331 }
1332 for fixup in core::mem::take(&mut self.pending_fixup_records) {
1336 if self.should_apply_fixup(&fixup, forced_threshold) {
1337 self.handle_fixup(fixup)?;
1338 } else {
1339 self.fixup_records.push(fixup);
1340 }
1341 }
1342
1343 self.pending_fixup_deadline = u32::MAX;
1344
1345 while let Some(fixup) = self.fixup_records.peek() {
1346 if !self.should_apply_fixup(fixup, forced_threshold) {
1352 break;
1353 }
1354 let fixup = match self.fixup_records.pop() {
1355 Some(fixup) => fixup,
1356 None => unreachable!("peek confirmed a fixup is present"),
1357 };
1358 self.handle_fixup(fixup)?;
1359 }
1360 Ok(())
1361 }
1362
1363 fn finish_emission_maybe_forcing_veneers(&mut self) -> Result<(), AsmError> {
1364 while !self.pending_constants.is_empty()
1365 || !self.pending_fixup_records.is_empty()
1366 || !self.fixup_records.is_empty()
1367 {
1368 self.emit_island(u32::MAX)?;
1372 }
1373 Ok(())
1374 }
1375
1376 #[cfg(feature = "x86")]
1377 fn relax_x86_branches(&mut self) -> Result<(), AsmError> {
1378 loop {
1379 let mut selected = None;
1380
1381 for (index, &candidate) in self.x86_branch_relaxations.iter().enumerate() {
1382 let start = candidate.opcode_offset as usize;
1383 let near_size = candidate.near_size as usize;
1384 let end = start.checked_add(near_size).ok_or(AsmError::TooLarge)?;
1385 if end > self.data.len() {
1386 return Err(AsmError::InvalidState);
1387 }
1388 let valid_encoding = match candidate.near_size {
1389 5 => self.data[start] == 0xE9 && candidate.short_opcode == 0xEB,
1390 6 => {
1391 self.data[start] == 0x0F
1392 && self.data[start + 1] == candidate.short_opcode.wrapping_add(0x10)
1393 }
1394 _ => false,
1395 };
1396 if !valid_encoding {
1397 return Err(AsmError::InvalidState);
1398 }
1399
1400 let target = self.label_offset(candidate.label);
1401 if target == u32::MAX {
1402 return Err(AsmError::UnboundLabel);
1403 }
1404 let old_end = candidate
1405 .opcode_offset
1406 .checked_add(candidate.near_size as u32)
1407 .ok_or(AsmError::TooLarge)?;
1408 if target > candidate.opcode_offset && target < old_end {
1409 return Err(AsmError::InvalidState);
1410 }
1411
1412 let removed = candidate.near_size as u32 - 2;
1413 let target_after = if target >= old_end {
1414 target - removed
1415 } else {
1416 target
1417 };
1418 let short_end = candidate
1419 .opcode_offset
1420 .checked_add(2)
1421 .ok_or(AsmError::TooLarge)?;
1422 let displacement = i64::from(target_after) - i64::from(short_end);
1423 if i8::try_from(displacement).is_err() {
1424 continue;
1425 }
1426
1427 let cut_start = short_end;
1428 let cut_end = old_end;
1429 let preserves_alignment =
1430 self.alignment_constraints.iter().all(|&(offset, align)| {
1431 offset < cut_end || (offset - removed) & (align - 1) == 0
1432 }) && self.patch_blocks.iter().all(|block| {
1433 block.offset < cut_end || (block.offset - removed) & (block.align - 1) == 0
1434 });
1435 if !preserves_alignment {
1436 continue;
1437 }
1438
1439 selected = Some((index, candidate, cut_start, cut_end));
1440 break;
1441 }
1442
1443 let Some((index, candidate, cut_start, cut_end)) = selected else {
1444 return Ok(());
1445 };
1446 let removed = cut_end - cut_start;
1447 let fixup_offset = cut_end - LabelUse::X86JmpRel32.patch_size() as u32;
1448 let is_candidate_fixup = |fixup: &AsmFixup| {
1449 fixup.offset == fixup_offset
1450 && fixup.label == candidate.label
1451 && fixup.kind == LabelUse::X86JmpRel32
1452 };
1453
1454 let overlaps_cut = |offset: CodeOffset, size: usize| -> Result<bool, AsmError> {
1455 let end = offset
1456 .checked_add(u32::try_from(size).map_err(|_| AsmError::TooLarge)?)
1457 .ok_or(AsmError::TooLarge)?;
1458 Ok(offset < cut_end && end > cut_start)
1459 };
1460 for reloc in &self.relocs {
1461 if overlaps_cut(reloc.offset, relocation_patch_size(reloc.kind)?)? {
1462 return Err(AsmError::InvalidState);
1463 }
1464 }
1465 for fixup in self
1466 .pending_fixup_records
1467 .iter()
1468 .chain(self.fixup_records.iter())
1469 {
1470 if !is_candidate_fixup(fixup)
1471 && overlaps_cut(fixup.offset, fixup.kind.patch_size())?
1472 {
1473 return Err(AsmError::InvalidState);
1474 }
1475 }
1476 for block in &self.patch_blocks {
1477 if overlaps_cut(block.offset, block.size as usize)? {
1478 return Err(AsmError::InvalidState);
1479 }
1480 }
1481 for site in &self.patch_sites {
1482 if overlaps_cut(site.offset, site.kind.patch_size())?
1483 || matches!(site.target, PendingPatchTarget::Offset(offset) if (cut_start..cut_end).contains(&offset))
1484 {
1485 return Err(AsmError::InvalidState);
1486 }
1487 }
1488 if self
1489 .label_offsets
1490 .iter()
1491 .any(|&offset| offset != u32::MAX && (cut_start..cut_end).contains(&offset))
1492 || self
1493 .used_constants
1494 .iter()
1495 .any(|&(_, offset)| (cut_start..cut_end).contains(&offset))
1496 || self
1497 .alignment_constraints
1498 .iter()
1499 .any(|&(offset, _)| (cut_start..cut_end).contains(&offset))
1500 {
1501 return Err(AsmError::InvalidState);
1502 }
1503 for (other_index, other) in self.x86_branch_relaxations.iter().enumerate() {
1504 if other_index != index
1505 && overlaps_cut(other.opcode_offset, other.near_size as usize)?
1506 {
1507 return Err(AsmError::InvalidState);
1508 }
1509 }
1510
1511 let start = candidate.opcode_offset as usize;
1512 self.data[start] = candidate.short_opcode;
1513 self.data.drain(cut_start as usize..cut_end as usize);
1514 self.x86_branch_relaxations.remove(index);
1515
1516 let disp_offset = cut_start - 1;
1521 let mut rerecorded = false;
1522 for fixup in &mut self.pending_fixup_records {
1523 if is_candidate_fixup(fixup) {
1524 fixup.kind = LabelUse::X86BranchRel8;
1525 fixup.offset = disp_offset;
1526 rerecorded = true;
1527 }
1528 }
1529 let mut fixups = core::mem::take(&mut self.fixup_records).into_vec();
1530 for fixup in &mut fixups {
1531 if is_candidate_fixup(fixup) {
1532 fixup.kind = LabelUse::X86BranchRel8;
1533 fixup.offset = disp_offset;
1534 rerecorded = true;
1535 }
1536 }
1537 if !rerecorded {
1538 self.pending_fixup_records.push(AsmFixup {
1539 label: candidate.label,
1540 offset: disp_offset,
1541 kind: LabelUse::X86BranchRel8,
1542 });
1543 }
1544
1545 let rebase = |offset: &mut CodeOffset| -> Result<(), AsmError> {
1546 if *offset >= cut_end {
1547 *offset -= removed;
1548 } else if *offset >= cut_start {
1549 return Err(AsmError::InvalidState);
1550 }
1551 Ok(())
1552 };
1553
1554 for reloc in &mut self.relocs {
1555 rebase(&mut reloc.offset)?;
1556 }
1557 for offset in &mut self.label_offsets {
1558 if *offset != u32::MAX {
1559 rebase(offset)?;
1560 }
1561 }
1562 for fixup in &mut self.pending_fixup_records {
1563 rebase(&mut fixup.offset)?;
1564 }
1565 for fixup in &mut fixups {
1566 rebase(&mut fixup.offset)?;
1567 }
1568 self.fixup_records = BinaryHeap::from(fixups);
1569 self.pending_fixup_deadline = self
1570 .pending_fixup_records
1571 .iter()
1572 .map(AsmFixup::deadline)
1573 .min()
1574 .unwrap_or(u32::MAX);
1575 for (_, offset) in &mut self.used_constants {
1576 rebase(offset)?;
1577 }
1578 for block in &mut self.patch_blocks {
1579 rebase(&mut block.offset)?;
1580 }
1581 for site in &mut self.patch_sites {
1582 rebase(&mut site.offset)?;
1583 if let PendingPatchTarget::Offset(offset) = &mut site.target {
1584 rebase(offset)?;
1585 }
1586 }
1587 for relaxation in &mut self.x86_branch_relaxations {
1588 rebase(&mut relaxation.opcode_offset)?;
1589 }
1590 for (offset, _) in &mut self.alignment_constraints {
1591 rebase(offset)?;
1592 }
1593 }
1594 }
1595
1596 fn preflight_finalization(&self) -> Result<(), AsmError> {
1599 for fixup in self
1600 .pending_fixup_records
1601 .iter()
1602 .chain(self.fixup_records.iter())
1603 {
1604 let end = (fixup.offset as usize)
1605 .checked_add(fixup.kind.patch_size())
1606 .ok_or(AsmError::TooLarge)?;
1607 if end > self.data.len() {
1608 return Err(AsmError::InvalidArgument);
1609 }
1610 let label_offset = self.label_offset(fixup.label);
1611 if label_offset == u32::MAX {
1612 return Err(AsmError::UnboundLabel);
1613 }
1614 if fixup.kind.can_reach(fixup.offset, label_offset) {
1615 continue;
1616 }
1617 if matches!(
1618 fixup.kind,
1619 LabelUse::A64Branch14 | LabelUse::A64Branch19 | LabelUse::A64Branch26
1620 ) && !fixup.kind.supports_veneer()
1621 {
1622 return Err(AsmError::UnsupportedInstruction {
1623 reason: "AArch64 branch veneers are not implemented",
1624 });
1625 }
1626 if label_offset >= fixup.offset {
1627 return Err(AsmError::TooLarge);
1628 }
1629 if !fixup.kind.supports_veneer() {
1630 return Err(AsmError::UnsupportedInstruction {
1631 reason: "branch range requires an unsupported veneer",
1632 });
1633 }
1634 #[cfg(not(feature = "riscv"))]
1635 return Err(AsmError::UnsupportedInstruction {
1636 reason: "RISC-V veneer support is disabled",
1637 });
1638 }
1639 Ok(())
1640 }
1641
1642 fn finish_constants(&mut self) -> u32 {
1643 let mut alignment = 32;
1644
1645 for (constant, offset) in core::mem::take(&mut self.used_constants) {
1646 let constant = &self.constants[constant.0 as usize].0;
1647 let data = constant.as_slice();
1648 self.data[offset as usize..][..data.len()].copy_from_slice(data);
1649 alignment = constant.alignment().max(alignment);
1650 }
1651
1652 alignment as _
1653 }
1654
1655 fn resolve_patch_catalog(&self, validate_ranges: bool) -> Result<PatchCatalog, AsmError> {
1656 let mut blocks = SmallVec::new();
1657 let mut sites = SmallVec::new();
1658
1659 for block in &self.patch_blocks {
1660 blocks.push(PatchBlock {
1661 offset: block.offset,
1662 size: block.size,
1663 align: block.align,
1664 });
1665 }
1666
1667 for site in &self.patch_sites {
1668 let target_offset = match site.target {
1669 PendingPatchTarget::Offset(offset) => offset,
1670 PendingPatchTarget::Label(label) => self.label_offset(label),
1671 };
1672
1673 if target_offset == u32::MAX {
1674 return Err(AsmError::InvalidState);
1675 }
1676
1677 if validate_ranges && !site.kind.can_reach(site.offset, target_offset) {
1678 return Err(AsmError::TooLarge);
1679 }
1680
1681 sites.push(PatchSite {
1682 offset: site.offset,
1683 kind: site.kind,
1684 current_target: target_offset,
1685 addend: site.addend,
1686 });
1687 }
1688
1689 Ok(PatchCatalog::with_parts(self.env.arch(), blocks, sites))
1690 }
1691
1692 fn resolved_defined_symbols(&self) -> SmallVec<[(ExternalName, CodeOffset); 4]> {
1693 self.defined_symbols
1696 .iter()
1697 .map(|(name, label)| (name.clone(), self.label_offset(*label)))
1698 .collect()
1699 }
1700
1701 pub fn finish_patched(mut self) -> Result<CodeBufferFinalized, AsmError> {
1702 if let Some(error) = self.error.take() {
1703 return Err(error);
1704 }
1705 if self.has_unbound_labels() {
1706 return Err(AsmError::UnboundLabel);
1707 }
1708 self.preflight_finalization()?;
1709 #[cfg(feature = "x86")]
1710 self.relax_x86_branches()?;
1711 #[cfg(feature = "x86")]
1712 self.preflight_finalization()?;
1713 self.finish_emission_maybe_forcing_veneers()?;
1714 let patch_catalog = self.resolve_patch_catalog(true)?;
1715 let alignment = self.finish_constants();
1716 let defined_symbols = self.resolved_defined_symbols();
1717 Ok(CodeBufferFinalized {
1718 data: self.data,
1719 relocs: self.relocs,
1720 symbols: self.symbols,
1721 label_offsets: self.label_offsets,
1722 defined_symbols,
1723 alignment,
1724 patch_catalog,
1725 })
1726 }
1727
1728 pub fn finish(&mut self) -> Result<CodeBufferFinalized, AsmError> {
1729 if let Some(error) = self.error.clone() {
1730 return Err(error);
1731 }
1732 if self.has_unbound_labels() {
1733 return Err(AsmError::UnboundLabel);
1734 }
1735 self.preflight_finalization()?;
1736 #[cfg(feature = "x86")]
1737 self.relax_x86_branches()?;
1738 #[cfg(feature = "x86")]
1739 self.preflight_finalization()?;
1740 self.finish_emission_maybe_forcing_veneers()?;
1741 let patch_catalog = self.resolve_patch_catalog(false)?;
1742 let alignment = self.finish_constants();
1743 Ok(CodeBufferFinalized {
1744 data: self.data.clone(),
1745 relocs: self.relocs.clone(),
1746 symbols: self.symbols.clone(),
1747 label_offsets: self.label_offsets.clone(),
1748 defined_symbols: self.resolved_defined_symbols(),
1749 alignment,
1750 patch_catalog,
1751 })
1752 }
1753
1754 fn has_unbound_labels(&self) -> bool {
1755 let is_unbound = |label: Label| {
1756 self.label_offsets
1757 .get(label.id() as usize)
1758 .is_none_or(|offset| *offset == u32::MAX)
1759 };
1760 self.pending_fixup_records
1761 .iter()
1762 .any(|fixup| is_unbound(fixup.label))
1763 || self
1764 .fixup_records
1765 .iter()
1766 .any(|fixup| is_unbound(fixup.label))
1767 || self
1768 .relocs
1769 .iter()
1770 .any(|reloc| matches!(reloc.target, RelocTarget::Label(label) if is_unbound(label)))
1771 || self
1772 .defined_symbols
1773 .iter()
1774 .any(|(_, label)| is_unbound(*label))
1775 || self.patch_sites.iter().any(
1776 |site| matches!(site.target, PendingPatchTarget::Label(label) if is_unbound(label)),
1777 )
1778 }
1779}
1780
1781#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1782pub enum ConstantData {
1783 WellKnown(&'static [u8]),
1784 U64([u8; 8]),
1785 Bytes(Vec<u8>),
1786}
1787
1788impl ConstantData {
1789 pub fn as_slice(&self) -> &[u8] {
1790 match self {
1791 ConstantData::WellKnown(data) => data,
1792 ConstantData::U64(data) => data.as_ref(),
1793 ConstantData::Bytes(data) => data,
1794 }
1795 }
1796
1797 pub fn alignment(&self) -> usize {
1798 if self.as_slice().len() <= 8 { 8 } else { 16 }
1799 }
1800}
1801
1802#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1804pub struct Constant(pub(crate) u32);
1805
1806impl From<&'static str> for ConstantData {
1807 fn from(value: &'static str) -> Self {
1808 Self::WellKnown(value.as_bytes())
1809 }
1810}
1811
1812impl From<[u8; 8]> for ConstantData {
1813 fn from(value: [u8; 8]) -> Self {
1814 Self::U64(value)
1815 }
1816}
1817
1818impl From<Vec<u8>> for ConstantData {
1819 fn from(value: Vec<u8>) -> Self {
1820 Self::Bytes(value)
1821 }
1822}
1823
1824impl From<&'static [u8]> for ConstantData {
1825 fn from(value: &'static [u8]) -> Self {
1826 Self::WellKnown(value)
1827 }
1828}
1829
1830impl From<u64> for ConstantData {
1831 fn from(value: u64) -> Self {
1832 Self::U64(value.to_ne_bytes())
1833 }
1834}
1835
1836pub type CodeOffset = u32;
1841
1842pub type Addend = i64;
1844
1845#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1847pub enum Reloc {
1848 Abs4,
1850 Abs8,
1852 X86PCRel4,
1854 X86CallPCRel4,
1856 X86CallPLTRel4,
1858 X86GOTPCRel4,
1860 X86SecRel,
1864 Arm32Call,
1866 Arm64Call,
1870
1871 ElfX86_64TlsGd,
1873
1874 MachOX86_64Tlv,
1876
1877 MachOAarch64TlsAdrPage21,
1880
1881 MachOAarch64TlsAdrPageOff12,
1884
1885 Aarch64TlsDescAdrPage21,
1888
1889 Aarch64TlsDescLd64Lo12,
1892
1893 Aarch64TlsDescAddLo12,
1896
1897 Aarch64TlsDescCall,
1900
1901 Aarch64AdrGotPage21,
1905
1906 Aarch64Ld64GotLo12Nc,
1911
1912 Aarch64AdrPrelPgHi21,
1914 Aarch64AddAbsLo12Nc,
1916
1917 RiscvAbs8,
1919
1920 RiscvCallPlt,
1928
1929 RiscvTlsGdHi20,
1934
1935 RiscvPCRelLo12I,
1940
1941 RiscvGotHi20,
1946}
1947
1948impl Reloc {
1949 const fn supports_arch(self, arch: Arch) -> bool {
1950 match self {
1951 Self::Abs4 | Self::Abs8 => true,
1952 Self::X86PCRel4
1953 | Self::X86CallPCRel4
1954 | Self::X86CallPLTRel4
1955 | Self::X86GOTPCRel4
1956 | Self::X86SecRel
1957 | Self::ElfX86_64TlsGd
1958 | Self::MachOX86_64Tlv => {
1959 cfg!(feature = "x86") && matches!(arch, Arch::X86 | Arch::X64)
1960 }
1961 Self::Arm32Call => false,
1962 Self::Arm64Call
1963 | Self::MachOAarch64TlsAdrPage21
1964 | Self::MachOAarch64TlsAdrPageOff12
1965 | Self::Aarch64TlsDescAdrPage21
1966 | Self::Aarch64TlsDescLd64Lo12
1967 | Self::Aarch64TlsDescAddLo12
1968 | Self::Aarch64TlsDescCall
1969 | Self::Aarch64AdrGotPage21
1970 | Self::Aarch64Ld64GotLo12Nc
1971 | Self::Aarch64AdrPrelPgHi21
1972 | Self::Aarch64AddAbsLo12Nc => {
1973 cfg!(feature = "aarch64") && matches!(arch, Arch::AArch64)
1974 }
1975 Self::RiscvAbs8
1976 | Self::RiscvCallPlt
1977 | Self::RiscvTlsGdHi20
1978 | Self::RiscvPCRelLo12I
1979 | Self::RiscvGotHi20 => {
1980 cfg!(feature = "riscv") && matches!(arch, Arch::RISCV32 | Arch::RISCV64)
1981 }
1982 }
1983 }
1984}
1985
1986#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1987pub enum LabelUse {
1988 X86JmpRel32,
1989 X86BranchRel8,
1993 RVJal20,
1996 RVPCRel32,
2005
2006 RVB12,
2011
2012 RVPCRelHi20,
2015
2016 RVPCRelLo12I,
2023
2024 RVCJump,
2026 RVCB9,
2028 A64Branch14,
2031 A64Branch19,
2034 A64Branch26,
2037 A64Ldr19,
2040 A64Adr21,
2043 A64Adrp21,
2046 A64Ldr12,
2047
2048 A64AddAbsLo12,
2054}
2055
2056impl LabelUse {
2057 fn validate_for_arch(self, arch: Arch) -> Result<(), AsmError> {
2058 if self == Self::A64Ldr12 {
2059 return Err(AsmError::UnsupportedInstruction {
2060 reason: "AArch64 LDR12 label patching is not implemented",
2061 });
2062 }
2063 if !self.supports_arch(arch) {
2064 return Err(AsmError::InvalidArch);
2065 }
2066 Ok(())
2067 }
2068
2069 const fn supports_arch(self, arch: Arch) -> bool {
2070 match self {
2071 Self::X86JmpRel32 | Self::X86BranchRel8 => {
2072 cfg!(feature = "x86") && matches!(arch, Arch::X86 | Arch::X64)
2073 }
2074 Self::RVJal20
2075 | Self::RVPCRel32
2076 | Self::RVB12
2077 | Self::RVPCRelHi20
2078 | Self::RVPCRelLo12I
2079 | Self::RVCJump
2080 | Self::RVCB9 => {
2081 cfg!(feature = "riscv") && matches!(arch, Arch::RISCV32 | Arch::RISCV64)
2082 }
2083 Self::A64Branch14
2084 | Self::A64Branch19
2085 | Self::A64Branch26
2086 | Self::A64Ldr19
2087 | Self::A64Adr21
2088 | Self::A64Adrp21
2089 | Self::A64Ldr12
2090 | Self::A64AddAbsLo12 => cfg!(feature = "aarch64") && matches!(arch, Arch::AArch64),
2091 }
2092 }
2093
2094 pub fn can_reach(&self, use_offset: CodeOffset, label_offset: CodeOffset) -> bool {
2095 let delta = (label_offset as i64) - (use_offset as i64);
2096
2097 match self {
2098 Self::X86JmpRel32 => {
2099 let disp = delta - 4;
2100 i32::try_from(disp).is_ok()
2101 }
2102 Self::X86BranchRel8 => {
2103 let disp = delta - 1;
2104 i8::try_from(disp).is_ok()
2105 }
2106 Self::RVJal20 => delta % 2 == 0 && (-(1 << 20)..=((1 << 20) - 2)).contains(&delta),
2107 Self::RVB12 => delta % 2 == 0 && (-(1 << 12)..=((1 << 12) - 2)).contains(&delta),
2108 Self::RVCJump => delta % 2 == 0 && (-(1 << 11)..=((1 << 11) - 2)).contains(&delta),
2109 Self::RVCB9 => delta % 2 == 0 && (-(1 << 8)..=((1 << 8) - 2)).contains(&delta),
2110 Self::RVPCRelHi20 | Self::RVPCRelLo12I | Self::RVPCRel32 => {
2111 i32::try_from(delta).is_ok()
2112 }
2113 Self::A64Branch14 => delta % 4 == 0 && (-(1 << 15)..=((1 << 15) - 4)).contains(&delta),
2114 Self::A64Branch19 | Self::A64Ldr19 => {
2115 delta % 4 == 0 && (-(1 << 20)..=((1 << 20) - 4)).contains(&delta)
2116 }
2117 Self::A64Branch26 => delta % 4 == 0 && (-(1 << 27)..=((1 << 27) - 4)).contains(&delta),
2118 Self::A64Adr21 => (-(1 << 20)..=((1 << 20) - 1)).contains(&delta),
2119 Self::A64Adrp21 => {
2120 let page_delta = ((label_offset & !0xfff) as i64) - ((use_offset & !0xfff) as i64);
2121 page_delta % 4096 == 0 && (-(1 << 32)..=((1 << 32) - 4096)).contains(&page_delta)
2122 }
2123
2124 Self::A64AddAbsLo12 => {
2125 let page_delta = ((label_offset & !0xfff) as i64) - ((use_offset & !0xfff) as i64);
2128 page_delta % 4096 == 0 && (-(1 << 32)..=((1 << 32) - 4096)).contains(&page_delta)
2129 }
2130
2131 Self::A64Ldr12 => true,
2132 }
2133 }
2134
2135 pub const fn max_pos_range(self) -> CodeOffset {
2137 match self {
2138 LabelUse::RVJal20 => ((1 << 19) - 1) * 2,
2139 LabelUse::RVPCRelLo12I | LabelUse::RVPCRelHi20 | LabelUse::RVPCRel32 => {
2140 let imm20_max: i64 = ((1 << 19) - 1) << 12;
2141 let imm12_max = (1 << 11) - 1;
2142 (imm20_max + imm12_max) as _
2143 }
2144 LabelUse::RVB12 => ((1 << 11) - 1) * 2,
2145 LabelUse::RVCB9 => ((1 << 8) - 1) * 2,
2146 LabelUse::RVCJump => ((1 << 10) - 1) * 2,
2147 LabelUse::X86JmpRel32 => i32::MAX as _,
2148 LabelUse::X86BranchRel8 => 127,
2149 _ => u32::MAX,
2150 }
2151 }
2152
2153 pub const fn max_neg_range(self) -> CodeOffset {
2154 match self {
2155 LabelUse::RVPCRel32 => {
2156 let imm20_max: i64 = (1 << 19) << 12;
2157 let imm12_max = 1 << 11;
2158 (-imm20_max - imm12_max) as CodeOffset
2159 }
2160 _ => self.max_pos_range() + 2,
2161 }
2162 }
2163
2164 pub const fn patch_size(&self) -> usize {
2165 match self {
2166 Self::X86JmpRel32 => 4,
2167 Self::X86BranchRel8 => 1,
2168 Self::RVCJump | Self::RVCB9 => 2,
2169 Self::RVJal20 | Self::RVB12 | Self::RVPCRelHi20 | Self::RVPCRelLo12I => 4,
2170 Self::RVPCRel32 => 8,
2171 _ => 4,
2172 }
2173 }
2174
2175 pub const fn align(&self) -> usize {
2176 match self {
2177 Self::X86JmpRel32 | Self::X86BranchRel8 => 1,
2178 Self::RVCJump => 4,
2179 Self::RVJal20 | Self::RVB12 | Self::RVCB9 | Self::RVPCRelHi20 | Self::RVPCRelLo12I => 4,
2180 Self::RVPCRel32 => 4,
2181 _ => 4,
2182 }
2183 }
2184
2185 pub const fn supports_veneer(&self) -> bool {
2186 matches!(self, Self::RVB12 | Self::RVJal20 | Self::RVCJump)
2187 }
2188
2189 #[cfg(feature = "riscv")]
2190 pub(crate) fn veneer_size(&self) -> usize {
2191 debug_assert!(self.supports_veneer());
2192 8
2193 }
2194
2195 #[cfg(feature = "riscv")]
2196 pub(crate) fn generate_veneer(
2197 &self,
2198 buffer: &mut [u8],
2199 veneer_offset: CodeOffset,
2200 ) -> (CodeOffset, Self) {
2201 debug_assert!(self.supports_veneer());
2202 let base = riscv::X31;
2203
2204 {
2205 let x = riscv::opcodes::Inst::new(riscv::Opcode::AUIPC)
2206 .encode()
2207 .set_rd(base.id())
2208 .value
2209 .to_le_bytes();
2210 buffer[0] = x[0];
2211 buffer[1] = x[1];
2212 buffer[2] = x[2];
2213 buffer[3] = x[3];
2214 }
2215
2216 {
2217 let x = riscv::opcodes::Inst::new(riscv::Opcode::JALR)
2218 .encode()
2219 .set_rd(riscv::ZERO.id())
2220 .set_rs1(base.id())
2221 .value
2222 .to_le_bytes();
2223 buffer[4] = x[0];
2224 buffer[5] = x[1];
2225 buffer[6] = x[2];
2226 buffer[7] = x[3];
2227 }
2228
2229 (veneer_offset, LabelUse::RVPCRel32)
2230 }
2231 pub(crate) fn patch(
2232 &self,
2233 buffer: &mut [u8],
2234 use_offset: CodeOffset,
2235 label_offset: CodeOffset,
2236 ) {
2237 let addend = match self {
2238 Self::X86JmpRel32 => i64::from(u32::from_le_bytes([
2239 buffer[0], buffer[1], buffer[2], buffer[3],
2240 ])),
2241 _ => 0,
2242 };
2243
2244 self.patch_with_addend(buffer, use_offset, label_offset, addend);
2245 }
2246
2247 pub(crate) fn patch_with_addend(
2248 &self,
2249 buffer: &mut [u8],
2250 use_offset: CodeOffset,
2251 label_offset: CodeOffset,
2252 addend: i64,
2253 ) {
2254 let pc_reli = (label_offset as i64) - (use_offset as i64);
2255
2256 let pc_rel = pc_reli as u32;
2257
2258 match self {
2259 Self::X86JmpRel32 => {
2260 let value = pc_rel.wrapping_add(addend as u32).wrapping_sub(4);
2261
2262 buffer.copy_from_slice(&value.to_le_bytes());
2263 }
2264
2265 Self::X86BranchRel8 => {
2266 let value = pc_reli.wrapping_add(addend).wrapping_sub(1) as u8;
2267
2268 buffer[0] = value;
2269 }
2270
2271 Self::RVJal20 => {
2272 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2273 let offset = pc_rel;
2274 let v = ((offset >> 12 & 0b1111_1111) << 12)
2275 | ((offset >> 11 & 0b1) << 20)
2276 | ((offset >> 1 & 0b11_1111_1111) << 21)
2277 | ((offset >> 20 & 0b1) << 31);
2278 buffer[0..4].clone_from_slice(&u32::to_le_bytes((insn & 0xFFF) | v));
2281 }
2282
2283 Self::RVPCRel32 => {
2284 #[cfg(feature = "riscv")]
2285 {
2286 let (imm20, imm12) = generate_imm(pc_rel as u64);
2287 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2288 let insn2 = u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
2289
2290 let auipc = riscv::opcodes::Inst::new(riscv::Opcode::AUIPC)
2291 .encode()
2292 .set_imm20(0);
2293 let jalr = riscv::opcodes::Inst::new(riscv::Opcode::JALR)
2294 .encode()
2295 .set_rd(0)
2296 .set_rs1(0)
2297 .set_imm12(0);
2298
2299 buffer[0..4].copy_from_slice(&(insn | auipc.value | imm20).to_le_bytes());
2300 buffer[4..8].copy_from_slice(&(insn2 | jalr.value | imm12).to_le_bytes());
2301 }
2302 #[cfg(not(feature = "riscv"))]
2303 {
2304 panic!("RISC-V veneers aren't supported without the `riscv` feature");
2305 }
2306 }
2307
2308 Self::RVB12 => {
2309 #[cfg(feature = "riscv")]
2310 {
2311 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2312 let offset = pc_rel;
2313 let v = ((offset >> 11 & 0b1) << 7)
2314 | ((offset >> 1 & 0b1111) << 8)
2315 | ((offset >> 5 & 0b11_1111) << 25)
2316 | ((offset >> 12 & 0b1) << 31);
2317 buffer[0..4].clone_from_slice(&u32::to_le_bytes((insn & 0x01FF_F07F) | v));
2320 }
2321 #[cfg(not(feature = "riscv"))]
2322 {
2323 panic!("RISC-V veneers aren't supported without the `riscv` feature");
2324 }
2325 }
2326
2327 Self::RVPCRelHi20 => {
2328 let offset = pc_reli as u32;
2334 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2335 let hi20 = offset.wrapping_add(0x800) >> 12;
2336 let insn = (insn & 0xfff) | (hi20 << 12);
2337 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2338 }
2339
2340 Self::RVPCRelLo12I => {
2341 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2352
2353 let lo12 = (pc_reli + 4) as u32 & 0xfff;
2354 let insn = (insn & 0xFFFFF) | (lo12 << 20);
2355 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2356 }
2357
2358 Self::RVCJump => {
2359 debug_assert!(pc_rel & 1 == 0);
2360
2361 #[cfg(feature = "riscv")]
2362 {
2363 let insn = riscv::opcodes::Inst::new(riscv::Opcode::CJ)
2364 .encode()
2365 .set_c_imm12(pc_rel as _);
2366 buffer[0..2].clone_from_slice(&(insn.value as u16).to_le_bytes());
2367 }
2368 #[cfg(not(feature = "riscv"))]
2369 {
2370 panic!("RISC-V jumps aren't supported without the `riscv` feature");
2371 }
2372 }
2373
2374 Self::RVCB9 => {
2375 debug_assert!(pc_rel & 1 == 0);
2376
2377 #[cfg(feature = "riscv")]
2378 {
2379 let insn = riscv::opcodes::Inst::new(riscv::Opcode::BEQZ)
2380 .encode()
2381 .set_c_bimm9lohi(pc_rel as _);
2382 buffer[0..2].clone_from_slice(&(insn.value as u16).to_le_bytes());
2383 }
2384 #[cfg(not(feature = "riscv"))]
2385 {
2386 panic!("RISC-V veneers aren't supported without the `riscv` feature");
2387 }
2388 }
2389
2390 Self::A64Branch14 => {
2391 debug_assert!(pc_reli & 0b11 == 0);
2392
2393 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2394 let imm14 = ((pc_reli >> 2) as i32 as u32) & 0x3fff;
2395 let insn = (insn & !0x0007ffe0) | (imm14 << 5);
2396 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2397 }
2398
2399 Self::A64Branch19 | Self::A64Ldr19 => {
2400 debug_assert!(pc_reli & 0b11 == 0);
2401
2402 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2403 let imm19 = ((pc_reli >> 2) as i32 as u32) & 0x7ffff;
2404 let insn = (insn & !0x00ffffe0) | (imm19 << 5);
2405 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2406 }
2407
2408 Self::A64Branch26 => {
2409 debug_assert!(pc_reli & 0b11 == 0);
2410
2411 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2412 let imm26 = ((pc_reli >> 2) as i32 as u32) & 0x03ff_ffff;
2413 let insn = (insn & !0x03ff_ffff) | imm26;
2414 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2415 }
2416
2417 Self::A64Adr21 => {
2418 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2419 let imm21 = (pc_reli as i32 as u32) & 0x1f_ffff;
2420 let immlo = imm21 & 0x3;
2421 let immhi = (imm21 >> 2) & 0x7ffff;
2422 let insn = (insn & !0x60ff_ffe0) | (immlo << 29) | (immhi << 5);
2423 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2424 }
2425
2426 Self::A64Adrp21 => {
2427 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2428
2429 let pc_page = (use_offset as i64) & !0xFFF;
2431 let target_page = ((label_offset as i64) + addend) & !0xFFF;
2432
2433 let page_offset = (target_page - pc_page) >> 12;
2435
2436 let imm21 = (page_offset as u32) & 0x1F_FFFF;
2438 let immlo = imm21 & 0x3; let immhi = (imm21 >> 2) & 0x7FFFF; let insn = (insn & !0x60FF_FFE0) | (immlo << 29) | (immhi << 5);
2444
2445 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2446 }
2447
2448 Self::A64AddAbsLo12 => {
2449 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2450
2451 let imm12 = (label_offset & 0xfff) << 10;
2453 let insn = insn | imm12;
2454 buffer[0..4].copy_from_slice(&insn.to_le_bytes());
2455 }
2456
2457 _ => todo!(),
2458 }
2459 }
2460}
2461
2462pub const fn is_imm12(val: i64) -> bool {
2463 val >= -2048 && val <= 2047
2464}
2465
2466#[allow(dead_code)]
2467pub(crate) fn generate_imm(value: u64) -> (u32, u32) {
2468 #[cfg(not(feature = "riscv"))]
2469 {
2470 let _ = value;
2471 panic!("Can't generate RISC-V immediates without the `riscv` feature");
2472 }
2473 #[cfg(feature = "riscv")]
2474 {
2475 if is_imm12(value as _) {
2476 return (
2477 0,
2478 riscv::opcodes::InstructionValue::new(0)
2479 .set_imm12(value as i64 as i32)
2480 .value,
2481 );
2482 }
2483
2484 let value = value as i64;
2485
2486 let mod_num = 4096i64;
2487 let (imm20, imm12) = if value > 0 {
2488 let mut imm20 = value / mod_num;
2489 let mut imm12 = value % mod_num;
2490
2491 if imm12 >= 2048 {
2492 imm12 -= mod_num;
2493 imm20 += 1;
2494 }
2495
2496 (imm20, imm12)
2497 } else {
2498 let value_abs = value.abs();
2499 let imm20 = value_abs / mod_num;
2500 let imm12 = value_abs % mod_num;
2501 let mut imm20 = -imm20;
2502 let mut imm12 = -imm12;
2503 if imm12 < -2048 {
2504 imm12 += mod_num;
2505 imm20 -= 1;
2506 }
2507 (imm20, imm12)
2508 };
2509 (
2510 riscv::opcodes::InstructionValue::new(0)
2511 .set_imm20(imm20 as _)
2512 .value,
2513 riscv::opcodes::InstructionValue::new(0)
2514 .set_imm12(imm12 as _)
2515 .value,
2516 )
2517 }
2518}
2519
2520pub(crate) fn relocation_patch_size(kind: Reloc) -> Result<usize, AsmError> {
2521 match kind {
2522 Reloc::Abs4
2523 | Reloc::X86PCRel4
2524 | Reloc::X86CallPCRel4
2525 | Reloc::X86CallPLTRel4
2526 | Reloc::X86GOTPCRel4
2527 | Reloc::RiscvGotHi20
2528 | Reloc::RiscvPCRelLo12I
2529 | Reloc::Aarch64AdrPrelPgHi21
2530 | Reloc::Aarch64AddAbsLo12Nc
2531 | Reloc::Aarch64AdrGotPage21
2532 | Reloc::Aarch64Ld64GotLo12Nc => Ok(4),
2533 Reloc::Abs8 | Reloc::RiscvAbs8 | Reloc::RiscvCallPlt => Ok(8),
2534 _ => Err(AsmError::InvalidArgument),
2535 }
2536}
2537
2538fn checked_address(base: usize, addend: i64) -> Result<usize, AsmError> {
2539 if addend >= 0 {
2540 base.checked_add(usize::try_from(addend).map_err(|_| AsmError::TooLarge)?)
2541 } else {
2542 base.checked_sub(usize::try_from(addend.unsigned_abs()).map_err(|_| AsmError::TooLarge)?)
2543 }
2544 .ok_or(AsmError::TooLarge)
2545}
2546
2547fn checked_pcrel(target: usize, instruction: usize) -> Result<i64, AsmError> {
2548 i64::try_from(target as i128 - instruction as i128).map_err(|_| AsmError::TooLarge)
2549}
2550
2551pub unsafe fn perform_relocations(
2562 code: *mut u8,
2563 code_rx: *const u8,
2564 code_size: usize,
2565 relocs: &[AsmReloc],
2566 get_address: impl Fn(&RelocTarget) -> Result<*const u8, AsmError>,
2567 get_got_entry: impl Fn(&RelocTarget) -> Result<*const u8, AsmError>,
2568 get_plt_entry: impl Fn(&RelocTarget) -> Result<*const u8, AsmError>,
2569) -> Result<(), AsmError> {
2570 use core::ptr::write_unaligned;
2571
2572 if code.is_null() || code_rx.is_null() {
2573 return Err(AsmError::InvalidArgument);
2574 }
2575
2576 for &AsmReloc {
2577 addend,
2578 kind,
2579 offset,
2580 ref target,
2581 } in relocs
2582 {
2583 let patch_size = relocation_patch_size(kind)?;
2584 let patch_start = offset as usize;
2585 let patch_end = patch_start
2586 .checked_add(patch_size)
2587 .ok_or(AsmError::TooLarge)?;
2588 if patch_end > code_size {
2589 return Err(AsmError::InvalidArgument);
2590 }
2591 let at = unsafe { code.add(patch_start) };
2594 let atrx = code_rx
2595 .addr()
2596 .checked_add(patch_start)
2597 .ok_or(AsmError::TooLarge)?;
2598 let resolve = |resolver: &dyn Fn(&RelocTarget) -> Result<*const u8, AsmError>| {
2599 let base = resolver(target)?;
2600 if base.is_null() {
2601 return Err(AsmError::InvalidArgument);
2602 }
2603 checked_address(base.addr(), addend)
2604 };
2605
2606 match kind {
2607 Reloc::Abs4 => {
2608 let what = resolve(&get_address)?;
2609 let what = u32::try_from(what).map_err(|_| AsmError::TooLarge)?;
2610 unsafe {
2614 write_unaligned(at as *mut u32, what);
2615 }
2616 }
2617
2618 Reloc::Abs8 | Reloc::RiscvAbs8 => {
2619 let what = resolve(&get_address)?;
2620 let what = u64::try_from(what).map_err(|_| AsmError::TooLarge)?;
2621 unsafe {
2624 write_unaligned(at as *mut u64, what);
2625 }
2626 }
2627
2628 Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
2629 let what = resolve(&get_address)?;
2630 let pcrel =
2631 i32::try_from(checked_pcrel(what, atrx)?).map_err(|_| AsmError::TooLarge)?;
2632
2633 unsafe {
2635 write_unaligned(at as *mut i32, pcrel);
2636 }
2637 }
2638
2639 Reloc::X86GOTPCRel4 => {
2640 let what = resolve(&get_got_entry)?;
2641 let pcrel =
2642 i32::try_from(checked_pcrel(what, atrx)?).map_err(|_| AsmError::TooLarge)?;
2643
2644 unsafe {
2646 write_unaligned(at as *mut i32, pcrel);
2647 }
2648 }
2649
2650 Reloc::X86CallPLTRel4 => {
2651 let what = resolve(&get_plt_entry)?;
2652 let pcrel =
2653 i32::try_from(checked_pcrel(what, atrx)?).map_err(|_| AsmError::TooLarge)?;
2654 unsafe { write_unaligned(at as *mut i32, pcrel) };
2656 }
2657
2658 Reloc::RiscvGotHi20 => {
2659 let what = resolve(&get_got_entry)?;
2660 let pc_rel =
2661 i32::try_from(checked_pcrel(what, atrx)?).map_err(|_| AsmError::TooLarge)?;
2662 unsafe {
2665 let buffer = core::slice::from_raw_parts_mut(at, 4);
2666 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2667 let hi20 = (pc_rel as u32).wrapping_add(0x800) >> 12;
2668 let insn = (insn & 0xfff) | (hi20 << 12);
2669 buffer.copy_from_slice(&insn.to_le_bytes());
2670 }
2671 }
2672
2673 Reloc::RiscvPCRelLo12I => {
2674 let what = resolve(&get_got_entry)?;
2675 let pc_rel = checked_pcrel(what, atrx)?;
2676 let pc_rel = i32::try_from(pc_rel).map_err(|_| AsmError::TooLarge)?;
2677
2678 unsafe {
2681 let buffer = core::slice::from_raw_parts_mut(at, 4);
2682 let insn = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
2683 let lo12 = ((pc_rel as i64 + 4) as u32) & 0xfff;
2684 let insn = (insn & 0xFFFFF) | (lo12 << 20);
2685 buffer.copy_from_slice(&insn.to_le_bytes());
2686 }
2687 }
2688
2689 Reloc::RiscvCallPlt => {
2690 #[cfg(not(feature = "riscv"))]
2691 {
2692 return Err(AsmError::InvalidArgument);
2693 }
2694 #[cfg(feature = "riscv")]
2695 {
2696 let what = resolve(&get_address)?;
2702 let pcrel = i32::try_from(checked_pcrel(what, atrx)?)
2703 .map_err(|_| AsmError::TooLarge)?;
2704
2705 let hi20 = pcrel.wrapping_add(0x800) as u32 & 0xFFFFF000u32;
2719 let lo12 = (pcrel as u32).wrapping_sub(hi20) & 0xFFF;
2720
2721 unsafe {
2726 let auipc_addr = at as *mut u32;
2727 let auipc = riscv::opcodes::Inst::new(riscv::Opcode::AUIPC)
2728 .encode()
2729 .set_imm20(hi20 as _)
2730 .value;
2731 write_unaligned(auipc_addr, auipc_addr.read_unaligned() | auipc);
2732
2733 let jalr_addr = at.add(4) as *mut u32;
2734 let jalr = riscv::opcodes::Inst::new(riscv::Opcode::JALR)
2735 .encode()
2736 .set_imm12(lo12 as _)
2737 .value;
2738 write_unaligned(jalr_addr, jalr_addr.read_unaligned() | jalr);
2739 }
2740 }
2741 }
2742
2743 Reloc::Aarch64AdrPrelPgHi21 => {
2744 let what = resolve(&get_address)?;
2745 let pages = ((what & !0xfff) as i128 - (atrx & !0xfff) as i128) >> 12;
2746 if !(-(1i128 << 20)..(1i128 << 20)).contains(&pages) {
2747 return Err(AsmError::TooLarge);
2748 }
2749 let iptr = at as *mut u32;
2750 let imm21 = pages as u32 & 0x1f_ffff;
2751 let lo = (imm21 & 0x3) << 29;
2752 let hi = ((imm21 >> 2) & 0x7ffff) << 5;
2753 unsafe {
2757 let insn = iptr.read_unaligned();
2758 write_unaligned(iptr, insn | lo | hi);
2759 }
2760 }
2761
2762 Reloc::Aarch64AddAbsLo12Nc => {
2763 let what = resolve(&get_address)?;
2764 let iptr = at as *mut u32;
2765 let imm12 = (what as u32 & 0xfff) << 10;
2766 unsafe {
2768 let insn = iptr.read_unaligned();
2769 write_unaligned(iptr, insn | imm12);
2770 }
2771 }
2772
2773 Reloc::Aarch64AdrGotPage21 => {
2774 let what = resolve(&get_got_entry)?;
2775 let pages = ((what & !0xfff) as i128 - (atrx & !0xfff) as i128) >> 12;
2776 if !(-(1i128 << 20)..(1i128 << 20)).contains(&pages) {
2777 return Err(AsmError::TooLarge);
2778 }
2779 let iptr = at as *mut u32;
2780 let imm21 = pages as u32 & 0x1f_ffff;
2781 let lo = (imm21 & 0x3) << 29;
2782 let hi = ((imm21 >> 2) & 0x7ffff) << 5;
2783 unsafe {
2785 let insn = iptr.read_unaligned();
2786 write_unaligned(iptr, insn | lo | hi);
2787 }
2788 }
2789
2790 Reloc::Aarch64Ld64GotLo12Nc => {
2791 let what = resolve(&get_got_entry)?;
2792 if what & 7 != 0 {
2793 return Err(AsmError::InvalidArgument);
2794 }
2795 let iptr = at as *mut u32;
2796 let imm12 = ((what as u32 & 0xfff) >> 3) << 10;
2797 unsafe {
2799 let insn = iptr.read_unaligned();
2800 write_unaligned(iptr, insn | imm12);
2801 }
2802 }
2803
2804 _ => return Err(AsmError::InvalidArgument),
2805 }
2806 }
2807 Ok(())
2808}
2809
2810#[cfg(test)]
2811mod tests {
2812 use super::*;
2813
2814 fn resolved(address: usize) -> impl Fn(&RelocTarget) -> Result<*const u8, AsmError> {
2815 move |_| Ok(address as *const u8)
2816 }
2817
2818 fn unresolved(_: &RelocTarget) -> Result<*const u8, AsmError> {
2819 Ok(core::ptr::null())
2820 }
2821
2822 #[test]
2823 fn relocation_rejects_patch_outside_code() {
2824 let mut code = [0u8; 4];
2825 let relocs = [AsmReloc {
2826 offset: 1,
2827 kind: Reloc::Abs4,
2828 addend: 0,
2829 target: RelocTarget::Label(Label::from_id(0)),
2830 }];
2831
2832 let result = unsafe {
2833 perform_relocations(
2834 code.as_mut_ptr(),
2835 code.as_ptr(),
2836 code.len(),
2837 &relocs,
2838 resolved(1),
2839 resolved(1),
2840 resolved(1),
2841 )
2842 };
2843
2844 assert_eq!(result, Err(AsmError::InvalidArgument));
2845 assert_eq!(code, [0; 4]);
2846 }
2847
2848 #[test]
2849 fn relocation_rejects_null_and_overflowing_targets() {
2850 let mut code = [0u8; 8];
2851 let reloc = AsmReloc {
2852 offset: 0,
2853 kind: Reloc::Abs8,
2854 addend: 0,
2855 target: RelocTarget::Label(Label::from_id(0)),
2856 };
2857
2858 let null_result = unsafe {
2859 perform_relocations(
2860 code.as_mut_ptr(),
2861 code.as_ptr(),
2862 code.len(),
2863 core::slice::from_ref(&reloc),
2864 unresolved,
2865 unresolved,
2866 unresolved,
2867 )
2868 };
2869 assert_eq!(null_result, Err(AsmError::InvalidArgument));
2870
2871 let overflowing = AsmReloc {
2872 addend: -2,
2873 ..reloc
2874 };
2875 let overflow_result = unsafe {
2876 perform_relocations(
2877 code.as_mut_ptr(),
2878 code.as_ptr(),
2879 code.len(),
2880 core::slice::from_ref(&overflowing),
2881 resolved(1),
2882 resolved(1),
2883 resolved(1),
2884 )
2885 };
2886 assert_eq!(overflow_result, Err(AsmError::TooLarge));
2887 }
2888
2889 #[test]
2890 fn poisoned_buffer_rejects_raw_mutation_and_patch_metadata() {
2891 let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
2892 buffer.write_u32(0);
2893 let bytes = buffer.data().to_vec();
2894 buffer.record_error(AsmError::InvalidOperand);
2895
2896 buffer.write_u8(1);
2897 buffer.put8(2);
2898 assert!(
2899 !buffer
2900 .add_symbol(ExternalName::user(0, 1), RelocDistance::Far)
2901 .is_valid()
2902 );
2903 assert_eq!(buffer.add_constant(3u64), Constant(u32::MAX));
2904 assert!(!buffer.get_label().is_valid());
2905 buffer.add_reloc(Reloc::Abs4, RelocTarget::Label(Label::from_id(0)), 0);
2906 let patch = buffer.try_record_patch_site(0, LabelUse::X86JmpRel32, 0);
2907
2908 assert_eq!(patch, Err(AsmError::InvalidOperand));
2909 assert_eq!(buffer.data(), bytes);
2910 assert!(buffer.relocs().is_empty());
2911 assert!(buffer.patch_sites.is_empty());
2912 assert!(matches!(buffer.finish(), Err(AsmError::InvalidOperand)));
2913 }
2914
2915 #[test]
2916 fn target_rejects_foreign_relocations_and_patch_kinds() {
2917 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
2918 let label = buffer.get_label();
2919 buffer.add_reloc(Reloc::X86PCRel4, RelocTarget::Label(label), 0);
2920 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2921 assert!(buffer.relocs().is_empty());
2922
2923 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
2924 buffer.write_u32(0);
2925 assert_eq!(
2926 buffer.try_record_patch_site(0, LabelUse::X86JmpRel32, 0),
2927 Err(AsmError::InvalidArch)
2928 );
2929 assert!(buffer.patch_sites.is_empty());
2930
2931 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
2932 let label = buffer.get_label();
2933 buffer.use_label_at_offset(0, label, LabelUse::RVJal20);
2934 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2935 assert!(buffer.pending_fixup_records.is_empty());
2936
2937 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
2938 let label = buffer.get_label();
2939 assert_eq!(
2940 buffer.emit_veneer(label, 0, LabelUse::RVJal20),
2941 Err(AsmError::InvalidArch)
2942 );
2943 assert!(buffer.data().is_empty());
2944
2945 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
2946 buffer.write_u32(0);
2947 assert_eq!(
2948 buffer.try_record_patch_site(0, LabelUse::A64Ldr12, 0),
2949 Err(AsmError::UnsupportedInstruction {
2950 reason: "AArch64 LDR12 label patching is not implemented",
2951 })
2952 );
2953 }
2954
2955 #[cfg(not(feature = "x86"))]
2956 #[test]
2957 fn disabled_x86_label_use_is_rejected_without_a_fixup() {
2958 let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
2959 let label = buffer.get_label();
2960 buffer.write_u32(0);
2961 let bytes = buffer.data().to_vec();
2962
2963 buffer.use_label_at_offset(0, label, LabelUse::X86JmpRel32);
2964
2965 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2966 assert_eq!(buffer.data(), bytes);
2967 assert!(buffer.pending_fixup_records.is_empty());
2968 assert_eq!(buffer.finish().err(), Some(AsmError::InvalidArch));
2969
2970 let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
2971 let label = buffer.get_label();
2972 buffer.add_reloc(Reloc::X86PCRel4, RelocTarget::Label(label), 0);
2973 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2974 assert!(buffer.relocs().is_empty());
2975 }
2976
2977 #[cfg(not(feature = "riscv"))]
2978 #[test]
2979 fn disabled_riscv_label_use_is_rejected_without_a_fixup() {
2980 let mut buffer = CodeBuffer::new(Environment::new(Arch::RISCV64));
2981 let label = buffer.get_label();
2982 buffer.write_u32(0);
2983 let bytes = buffer.data().to_vec();
2984
2985 buffer.use_label_at_offset(0, label, LabelUse::RVPCRel32);
2986
2987 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2988 assert_eq!(buffer.data(), bytes);
2989 assert!(buffer.pending_fixup_records.is_empty());
2990 assert_eq!(buffer.finish().err(), Some(AsmError::InvalidArch));
2991
2992 let mut buffer = CodeBuffer::new(Environment::new(Arch::RISCV64));
2993 let label = buffer.get_label();
2994 buffer.add_reloc(Reloc::RiscvCallPlt, RelocTarget::Label(label), 0);
2995 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
2996 assert!(buffer.relocs().is_empty());
2997 }
2998
2999 #[cfg(not(feature = "aarch64"))]
3000 #[test]
3001 fn disabled_aarch64_label_use_is_rejected_without_a_fixup() {
3002 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
3003 let label = buffer.get_label();
3004 buffer.write_u32(0);
3005 let bytes = buffer.data().to_vec();
3006
3007 buffer.use_label_at_offset(0, label, LabelUse::A64Branch26);
3008
3009 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
3010 assert_eq!(buffer.data(), bytes);
3011 assert!(buffer.pending_fixup_records.is_empty());
3012 assert_eq!(buffer.finish().err(), Some(AsmError::InvalidArch));
3013
3014 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
3015 let label = buffer.get_label();
3016 buffer.add_reloc(Reloc::Arm64Call, RelocTarget::Label(label), 0);
3017 assert_eq!(buffer.error(), Some(&AsmError::InvalidArch));
3018 assert!(buffer.relocs().is_empty());
3019 }
3020
3021 #[test]
3022 fn poisoned_buffer_does_not_consume_pending_island_state() {
3023 let mut buffer = CodeBuffer::new(Environment::new(Arch::AArch64));
3024 buffer.add_constant(7u64);
3025 let pending_constants = buffer.pending_constants.len();
3026 buffer.record_error(AsmError::InvalidOperand);
3027
3028 assert_eq!(buffer.emit_island(0), Err(AsmError::InvalidOperand));
3029 assert_eq!(buffer.pending_constants.len(), pending_constants);
3030 assert!(buffer.data().is_empty());
3031 }
3032
3033 #[cfg(feature = "jit")]
3034 #[test]
3035 fn allocate_resolved_rejects_unresolved_symbols() {
3036 let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
3037 let symbol = buffer.add_symbol(ExternalName::user(0, 7), RelocDistance::Far);
3038 buffer.add_reloc(Reloc::Abs8, RelocTarget::Sym(symbol), 0);
3039 buffer.write_u64(0);
3040 let code = buffer.finish().unwrap();
3041 let mut allocator = JitAllocator::new(Default::default());
3042
3043 let result = code.allocate_resolved(&mut allocator, |_| core::ptr::null());
3044
3045 assert_eq!(result.err(), Some(AsmError::InvalidArgument));
3046 }
3047
3048 #[cfg(feature = "jit")]
3049 #[test]
3050 fn allocate_resolved_resolves_user_external_names() {
3051 let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
3052 let symbol = buffer.extern_user(1, 2, RelocDistance::Far);
3053 buffer.add_reloc(Reloc::Abs8, RelocTarget::Sym(symbol), 0);
3054 buffer.write_u64(0);
3055 let code = buffer.finish().unwrap();
3056 let mut allocator = JitAllocator::new(Default::default());
3057
3058 let target = 0x1000usize as *const u8;
3060 let loaded = code
3061 .allocate_resolved(&mut allocator, |name| match name {
3062 ExternalName::User(u) if u.namespace == 1 && u.index == 2 => target,
3063 _ => core::ptr::null(),
3064 })
3065 .unwrap();
3066
3067 unsafe {
3068 let patched = core::ptr::read_unaligned(loaded.rx() as *const usize);
3069 assert_eq!(patched, target as usize);
3070 }
3071 }
3072
3073 #[test]
3074 fn extern_sym_deduplicates_by_name() {
3075 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3076 let first = buf.extern_sym("puts", RelocDistance::Far);
3077 let other = buf.extern_sym("printf", RelocDistance::Near);
3078 let again = buf.extern_sym("puts", RelocDistance::Near);
3079
3080 assert_eq!(first, again);
3081 assert_ne!(first, other);
3082 assert_eq!(buf.symbol_distance(first), Some(RelocDistance::Far));
3084 assert_eq!(buf.symbol_distance(other), Some(RelocDistance::Near));
3085 }
3086
3087 #[test]
3088 fn extern_user_deduplicates_by_namespace_and_index() {
3089 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3090 let first = buf.extern_user(0, 1, RelocDistance::Far);
3091 let other_ns = buf.extern_user(1, 1, RelocDistance::Near);
3092 let other_idx = buf.extern_user(0, 2, RelocDistance::Near);
3093 let again = buf.extern_user(0, 1, RelocDistance::Near);
3094
3095 assert_eq!(first, again);
3096 assert_ne!(first, other_ns);
3097 assert_ne!(first, other_idx);
3098 assert_eq!(buf.symbol_distance(first), Some(RelocDistance::Far));
3099 assert_eq!(buf.symbol_name(first), Some(&ExternalName::user(0, 1)));
3100 }
3101
3102 #[test]
3103 fn unknown_symbols_are_fallible() {
3104 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3105 let missing = Sym::from_id(u32::MAX);
3106
3107 assert_eq!(buf.symbol_name(missing), None);
3108 assert_eq!(buf.symbol_distance(missing), None);
3109
3110 let finalized = buf.finish().unwrap();
3111 assert_eq!(finalized.symbol_name(missing), None);
3112 assert_eq!(finalized.symbol_distance(missing), None);
3113 }
3114
3115 #[test]
3116 fn defined_symbols_are_resolved_at_finish() {
3117 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3118 buf.write_u8(0x90);
3119 let entry = buf.get_label();
3120 buf.bind_label(entry);
3121 buf.bind_symbol("entry", entry);
3122 buf.write_u8(0xC3);
3123
3124 let result = buf.finish().unwrap();
3125 assert_eq!(result.defined_symbol_str("entry"), Some(1));
3126 assert_eq!(result.defined_symbol_str("missing"), None);
3127 assert_eq!(
3128 result.defined_symbol_offset(&ExternalName::from("entry")),
3129 Some(1)
3130 );
3131 }
3132
3133 #[test]
3134 fn invalid_label_binding_is_reported_without_mutating_labels() {
3135 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3136 let invalid = Label::from_id(0);
3137
3138 assert_eq!(buf.try_bind_label(invalid), Err(AsmError::InvalidArgument));
3139 assert_eq!(buf.label_count(), 0);
3140
3141 buf.bind_label(invalid);
3142 assert_eq!(buf.error(), Some(&AsmError::InvalidArgument));
3143 assert!(matches!(buf.finish(), Err(AsmError::InvalidArgument)));
3144 }
3145
3146 #[cfg(feature = "x86")]
3147 #[test]
3148 fn invalid_patch_registration_does_not_create_metadata() {
3149 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3150
3151 assert_eq!(
3152 buf.try_record_patch_site(0, LabelUse::X86JmpRel32, 0),
3153 Err(AsmError::InvalidArgument)
3154 );
3155 assert!(buf.patch_sites.is_empty());
3156
3157 buf.record_patch_site(0, LabelUse::X86JmpRel32, 0);
3158 assert_eq!(buf.error(), Some(&AsmError::InvalidArgument));
3159 assert!(buf.patch_sites.is_empty());
3160 }
3161
3162 #[cfg(feature = "x86")]
3163 #[test]
3164 fn finish_rejects_an_unbound_fixup() {
3165 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3166 let label = buf.get_label();
3167 buf.write_u32(0);
3168 buf.use_label_at_offset(0, label, LabelUse::X86JmpRel32);
3169
3170 assert!(matches!(buf.finish(), Err(AsmError::UnboundLabel)));
3171 }
3172
3173 #[cfg(feature = "x86")]
3174 #[test]
3175 fn unsupported_veneer_leaves_buffer_unchanged() {
3176 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3177 let label = buf.get_label();
3178 buf.write_u32(0);
3179 let original = buf.data().to_vec();
3180
3181 assert_eq!(
3182 buf.emit_veneer(label, 0, LabelUse::X86JmpRel32),
3183 Err(AsmError::UnsupportedInstruction {
3184 reason: "branch range requires an unsupported veneer",
3185 })
3186 );
3187 assert_eq!(buf.data(), original);
3188 }
3189
3190 #[test]
3191 fn branch_ranges_include_the_last_encodable_offset() {
3192 for (kind, range) in [
3193 (LabelUse::A64Branch14, 1 << 15),
3194 (LabelUse::A64Branch19, 1 << 20),
3195 (LabelUse::A64Branch26, 1 << 27),
3196 ] {
3197 assert!(kind.can_reach(0, range - 4));
3198 assert!(!kind.can_reach(0, range));
3199 assert!(kind.can_reach(range, 0));
3200 assert!(!kind.can_reach(range + 4, 0));
3201 }
3202
3203 assert!(LabelUse::RVB12.can_reach(0, (1 << 12) - 2));
3204 assert!(!LabelUse::RVB12.can_reach(0, 1 << 12));
3205 assert!(LabelUse::RVB12.can_reach(1 << 12, 0));
3206 assert!(!LabelUse::RVB12.can_reach((1 << 12) + 2, 0));
3207 }
3208
3209 #[test]
3210 fn supported_veneer_ranges_cover_both_exact_boundaries() {
3211 for (kind, positive, negative, step) in [
3212 (LabelUse::RVJal20, (1 << 20) - 2, 1 << 20, 2),
3213 (LabelUse::RVB12, (1 << 12) - 2, 1 << 12, 2),
3214 (LabelUse::RVCJump, (1 << 11) - 2, 1 << 11, 2),
3215 ] {
3216 assert!(kind.supports_veneer());
3217 assert!(kind.can_reach(0, positive));
3218 assert!(!kind.can_reach(0, positive + step));
3219 assert!(kind.can_reach(negative, 0));
3220 assert!(!kind.can_reach(negative + step, 0));
3221 }
3222 }
3223
3224 #[cfg(feature = "riscv")]
3225 #[test]
3226 fn riscv_veneer_is_finalized_as_a_fixup() {
3227 let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
3228 let label = buf.get_label();
3229 buf.bind_label(label);
3230 buf.write_u32(0);
3231
3232 buf.emit_veneer(label, 0, LabelUse::RVB12).unwrap();
3233 let code = buf.finish().unwrap();
3234 assert_eq!(code.data().len(), 12);
3235 assert_eq!(&code.data()[0..4], &[0x00, 0x02, 0x00, 0x00]);
3237 }
3238
3239 #[cfg(feature = "riscv")]
3240 #[test]
3241 fn riscv_supported_veneers_are_inserted_by_islands() {
3242 for kind in [LabelUse::RVJal20, LabelUse::RVB12, LabelUse::RVCJump] {
3243 let mut buf = CodeBuffer::new(Environment::new(Arch::RISCV64));
3244 let label = buf.get_label();
3245 let patch_size = kind.patch_size();
3246 buf.get_appended_space(patch_size);
3247 buf.use_label_at_offset(0, label, kind);
3248
3249 buf.emit_island(u32::MAX).unwrap();
3250 buf.bind_label(label);
3251 let code = buf.finish().unwrap();
3252
3253 assert_eq!(code.data().len(), 12);
3254 assert!(kind.can_reach(0, 4));
3255 }
3256 }
3257
3258 #[cfg(feature = "riscv")]
3259 #[test]
3260 fn riscv_large_images_hit_exact_branch_boundaries() {
3261 for (kind, positive, step) in [
3262 (LabelUse::RVJal20, (1 << 20) - 2, 2),
3263 (LabelUse::RVB12, (1 << 12) - 2, 2),
3264 (LabelUse::RVCJump, (1 << 11) - 2, 2),
3265 ] {
3266 let mut exact = CodeBuffer::new(Environment::new(Arch::RISCV64));
3267 let label = exact.get_label();
3268 exact.get_appended_space(kind.patch_size());
3269 exact.use_label_at_offset(0, label, kind);
3270 exact.get_appended_space(positive as usize - kind.patch_size());
3271 exact.bind_label(label);
3272 assert!(exact.finish().is_ok());
3273
3274 let mut outside = CodeBuffer::new(Environment::new(Arch::RISCV64));
3275 let label = outside.get_label();
3276 outside.get_appended_space(kind.patch_size());
3277 outside.use_label_at_offset(0, label, kind);
3278 outside.get_appended_space((positive + step) as usize - kind.patch_size());
3279 outside.bind_label(label);
3280 assert_eq!(outside.finish().err(), Some(AsmError::TooLarge));
3281 }
3282 }
3283
3284 #[cfg(feature = "aarch64")]
3285 #[test]
3286 fn out_of_range_aarch64_branch_reports_unsupported_veneer() {
3287 let mut buf = CodeBuffer::new(Environment::new(Arch::AArch64));
3288 let label = buf.get_label();
3289 buf.write_u32(0);
3290 buf.use_label_at_offset(0, label, LabelUse::A64Branch14);
3291 buf.get_appended_space((1 << 15) - 4);
3292 buf.bind_label(label);
3293
3294 assert_eq!(
3295 buf.finish().err(),
3296 Some(AsmError::UnsupportedInstruction {
3297 reason: "AArch64 branch veneers are not implemented",
3298 })
3299 );
3300 }
3301
3302 #[cfg(feature = "x86")]
3303 #[test]
3304 fn x86_branch_relaxation_honors_rel8_boundaries() {
3305 use crate::x86::{Assembler, JEmitter, JmpEmitter};
3306
3307 let forward = |padding: usize, conditional: bool| {
3308 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3309 let target = buf.get_label();
3310 {
3311 let mut asm = Assembler::new(&mut buf);
3312 if conditional {
3313 asm.jz(target);
3314 } else {
3315 asm.jmp(target);
3316 }
3317 }
3318 for _ in 0..padding {
3319 buf.write_u8(0x90);
3320 }
3321 buf.bind_label(target);
3322 buf.finish().unwrap().data().to_vec()
3323 };
3324 for conditional in [false, true] {
3325 let short = forward(127, conditional);
3326 assert_eq!(short[0], if conditional { 0x74 } else { 0xEB });
3327 assert_eq!(short[1], 127);
3328
3329 let near = forward(128, conditional);
3330 if conditional {
3331 assert_eq!(&near[..2], &[0x0F, 0x84]);
3332 } else {
3333 assert_eq!(near[0], 0xE9);
3334 }
3335 }
3336
3337 let backward = |padding: usize, conditional: bool| {
3338 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3339 let target = buf.get_label();
3340 buf.bind_label(target);
3341 for _ in 0..padding {
3342 buf.write_u8(0x90);
3343 }
3344 {
3345 let mut asm = Assembler::new(&mut buf);
3346 if conditional {
3347 asm.jz(target);
3348 } else {
3349 asm.jmp(target);
3350 }
3351 }
3352 buf.finish().unwrap().data().to_vec()
3353 };
3354 for conditional in [false, true] {
3355 let short = backward(126, conditional);
3356 assert_eq!(
3357 &short[126..],
3358 &[if conditional { 0x74 } else { 0xEB }, 0x80]
3359 );
3360
3361 let near = backward(127, conditional);
3362 assert_eq!(near[127], if conditional { 0x0F } else { 0xE9 });
3363 }
3364 }
3365
3366 #[cfg(feature = "x86")]
3367 #[test]
3368 fn x86_branch_relaxation_reaches_a_bounded_fixed_point() {
3369 use crate::x86::{Assembler, JmpEmitter};
3370
3371 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3372 let target = buf.get_label();
3373 {
3374 let mut asm = Assembler::new(&mut buf);
3375 asm.jmp(target);
3376 }
3377 for _ in 0..120 {
3378 buf.write_u8(0x90);
3379 }
3380 {
3381 let mut asm = Assembler::new(&mut buf);
3382 asm.jmp(target);
3383 }
3384 for _ in 0..3 {
3385 buf.write_u8(0x90);
3386 }
3387 buf.bind_label(target);
3388
3389 let code = buf.finish().unwrap();
3390 assert_eq!(&code.data()[..2], &[0xEB, 125]);
3391 assert_eq!(&code.data()[122..124], &[0xEB, 3]);
3392 assert_eq!(code.label_offsets[target.id() as usize], 127);
3393 }
3394
3395 #[cfg(feature = "x86")]
3396 #[test]
3397 fn x86_branch_relaxation_rebases_metadata_and_is_deterministic() {
3398 use crate::x86::{Assembler, JmpEmitter};
3399
3400 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3401 let target = buf.get_label();
3402 {
3403 let mut asm = Assembler::new(&mut buf);
3404 asm.jmp(target);
3405 }
3406 buf.write_u8(0x90);
3407 buf.bind_label(target);
3408 buf.bind_symbol("target", target);
3409
3410 buf.add_reloc(Reloc::Abs4, RelocTarget::Label(target), 0);
3411 buf.write_u32(0);
3412 let block = buf.reserve_patch_block(4, 1).unwrap();
3413 let block_offset = block.offset();
3414 let site = buf
3415 .try_record_patch_site(
3416 block_offset,
3417 LabelUse::X86JmpRel32,
3418 buf.label_offset(target),
3419 )
3420 .unwrap();
3421
3422 let later = buf.get_label();
3423 {
3424 let mut asm = Assembler::new(&mut buf);
3425 asm.long().jmp(later);
3426 }
3427 buf.write_u8(0x90);
3428 buf.bind_label(later);
3429
3430 let first = buf.finish().unwrap();
3431 assert_eq!(&first.data()[..3], &[0xEB, 1, 0x90]);
3432 assert_eq!(first.defined_symbol_str("target"), Some(3));
3433 assert_eq!(first.relocs()[0].offset, 3);
3434 assert_eq!(
3435 first
3436 .patch_catalog()
3437 .block(PatchBlockId::from_index(0))
3438 .unwrap()
3439 .offset,
3440 7
3441 );
3442 let patch_site = first.patch_catalog().site(site).unwrap();
3443 assert_eq!(patch_site.offset, 7);
3444 assert_eq!(patch_site.current_target, 3);
3445 assert_eq!(&first.data()[11..], &[0xE9, 1, 0, 0, 0, 0x90]);
3446
3447 let second = buf.finish().unwrap();
3448 assert_eq!(second.data(), first.data());
3449 assert_eq!(second.relocs(), first.relocs());
3450 assert_eq!(second.patch_catalog(), first.patch_catalog());
3451 }
3452
3453 #[cfg(feature = "x86")]
3454 #[test]
3455 fn x86_branch_relaxation_preserves_recorded_alignment() {
3456 use crate::x86::{Assembler, JmpEmitter};
3457
3458 let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
3459 let target = buf.get_label();
3460 {
3461 let mut asm = Assembler::new(&mut buf);
3462 asm.jmp(target);
3463 }
3464 buf.try_align_to(16).unwrap();
3465 buf.bind_label(target);
3466
3467 let code = buf.finish().unwrap();
3468 assert_eq!(code.data()[0], 0xE9);
3469 assert_eq!(code.label_offsets[target.id() as usize], 16);
3470 }
3471}