1#[allow(unused_imports)]
7use alloc::format;
8use alloc::string::String;
9use alloc::string::ToString;
10#[allow(unused_imports)]
11use alloc::vec;
12use alloc::vec::Vec;
13
14use crate::encoder;
15use crate::error::{AsmError, Span};
16use crate::ir::*;
17use crate::lexer;
18use crate::linker::{AppliedRelocation, Linker};
19use crate::parser;
20use crate::preprocessor::Preprocessor;
21
22#[derive(Debug, Clone)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[must_use]
26pub struct AssemblyResult {
27 bytes: Vec<u8>,
29 labels: Vec<(String, u64)>,
31 relocations: Vec<AppliedRelocation>,
33 base_address: u64,
35 source_annotations: Vec<(u64, String)>,
37}
38
39impl AssemblyResult {
40 #[must_use]
54 pub fn bytes(&self) -> &[u8] {
55 &self.bytes
56 }
57
58 #[must_use]
72 pub fn into_bytes(self) -> Vec<u8> {
73 self.bytes
74 }
75
76 #[must_use]
90 pub fn len(&self) -> usize {
91 self.bytes.len()
92 }
93
94 #[must_use]
106 pub fn is_empty(&self) -> bool {
107 self.bytes.is_empty()
108 }
109
110 #[must_use]
129 pub fn labels(&self) -> &[(String, u64)] {
130 &self.labels
131 }
132
133 #[must_use]
153 pub fn label_address(&self, name: &str) -> Option<u64> {
154 self.labels
155 .binary_search_by(|(n, _)| n.as_str().cmp(name))
156 .ok()
157 .map(|i| self.labels[i].1)
158 }
159
160 #[must_use]
176 pub fn relocations(&self) -> &[AppliedRelocation] {
177 &self.relocations
178 }
179
180 #[must_use]
195 pub fn base_address(&self) -> u64 {
196 self.base_address
197 }
198
199 #[must_use]
212 pub fn listing(&self) -> String {
213 use core::fmt::Write;
214
215 let mut out = String::new();
216 let base = self.base_address;
217
218 let mut sorted_labels = self.labels.clone();
220 sorted_labels.sort_by_key(|(_, addr)| *addr);
221
222 let mut label_at: alloc::collections::BTreeMap<u64, Vec<&str>> =
224 alloc::collections::BTreeMap::new();
225 for (name, addr) in &sorted_labels {
226 label_at.entry(*addr).or_default().push(name);
227 }
228
229 let mut source_at: alloc::collections::BTreeMap<u64, &str> =
231 alloc::collections::BTreeMap::new();
232 for (offset, text) in &self.source_annotations {
233 if !text.is_empty() {
234 source_at.insert(*offset, text);
235 }
236 }
237
238 let mut split_offsets: alloc::collections::BTreeSet<u64> =
240 label_at.keys().copied().collect();
241
242 for &ann_off in source_at.keys() {
244 split_offsets.insert(ann_off);
245 }
246
247 let bytes = &self.bytes;
249 let mut offset: u64 = base;
250 let mut i = 0;
251
252 while i < bytes.len() {
253 if let Some(names) = label_at.get(&offset) {
255 for name in names {
256 let _ = writeln!(out, "{:08X} {}:", offset, name);
257 }
258 }
259
260 let max_end = core::cmp::min(i + 8, bytes.len());
262 let mut chunk_end = max_end;
263
264 let range_end = offset + (max_end - i) as u64;
266 if range_end > offset + 1 {
267 for &split_off in split_offsets.range((offset + 1)..range_end) {
268 let split_at = (split_off - base) as usize;
269 if split_at < chunk_end && split_at > i {
270 chunk_end = split_at;
271 break;
272 }
273 }
274 }
275
276 let chunk = &bytes[i..chunk_end];
277 let hex: String = chunk.iter().fold(String::new(), |mut acc, b| {
278 let _ = write!(acc, "{:02X}", b);
279 acc
280 });
281
282 if let Some(source_text) = source_at.get(&offset) {
284 let _ = writeln!(out, "{:08X} {:<16} {}", offset, hex, source_text);
285 } else {
286 let _ = writeln!(out, "{:08X} {:<16}", offset, hex);
287 }
288
289 let chunk_len = chunk.len();
290 i += chunk_len;
291 offset += chunk_len as u64;
292 }
293
294 if let Some(names) = label_at.get(&offset) {
296 for name in names {
297 let _ = writeln!(out, "{:08X} {}:", offset, name);
298 }
299 }
300
301 out
302 }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
331#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
332pub struct ResourceLimits {
333 pub max_statements: usize,
336 pub max_labels: usize,
338 pub max_output_bytes: usize,
340 pub max_errors: usize,
342 pub max_recursion_depth: usize,
344 pub max_source_bytes: usize,
348 pub max_iterations: usize,
351 pub max_expanded_bytes: usize,
359}
360
361impl Default for ResourceLimits {
362 fn default() -> Self {
363 Self {
364 max_statements: 1_000_000,
365 max_labels: 100_000,
366 max_output_bytes: 16 * 1024 * 1024,
367 max_errors: 64,
368 max_recursion_depth: 32,
373 max_source_bytes: 64 * 1024 * 1024,
374 max_iterations: 100_000,
375 max_expanded_bytes: 64 * 1024 * 1024,
376 }
377 }
378}
379
380#[derive(Debug)]
396pub struct Assembler {
397 arch: Arch,
398 x86_mode: crate::ir::X86Mode,
401 syntax: Syntax,
402 opt_level: OptLevel,
403 linker: Linker,
404 preprocessor: Preprocessor,
406 errors: Vec<AsmError>,
408 fragment_annotations: Vec<(usize, String)>,
410 listing_enabled: bool,
413 resource_limits: ResourceLimits,
415 statement_count: usize,
417 label_count: usize,
419 literal_pool: Vec<LiteralPoolEntry>,
422 literal_pool_index: alloc::collections::BTreeMap<(i128, u8), usize>,
425 literal_pool_counter: usize,
427 rvc_enabled: bool,
431 thumb_func_pending: bool,
434 thumb_labels: Vec<String>,
437 estimated_output_bytes: usize,
441}
442
443#[derive(Debug, Clone)]
445struct LiteralPoolEntry {
446 value: i128,
448 size: u8,
450 label: String,
452}
453
454impl Assembler {
455 pub fn new(arch: Arch) -> Self {
457 let syntax = match arch {
458 Arch::Arm | Arch::Thumb | Arch::Aarch64 => Syntax::Ual,
459 Arch::Rv32 | Arch::Rv64 => Syntax::RiscV,
460 _ => Syntax::Intel,
461 };
462 let x86_mode = match arch {
463 Arch::X86 => crate::ir::X86Mode::Mode32,
464 Arch::X86_64 => crate::ir::X86Mode::Mode64,
465 _ => crate::ir::X86Mode::Mode64, };
467 let resource_limits = ResourceLimits::default();
468 let mut linker = Linker::new();
469 linker.set_max_output_bytes(resource_limits.max_output_bytes);
470 let mut preprocessor = Preprocessor::new();
471 preprocessor.set_max_recursion_depth(resource_limits.max_recursion_depth);
472 preprocessor.set_max_iterations(resource_limits.max_iterations);
473 preprocessor.set_max_expanded_bytes(resource_limits.max_expanded_bytes);
474 Self {
475 arch,
476 x86_mode,
477 syntax,
478 opt_level: OptLevel::default(),
479 linker,
480 preprocessor,
481 errors: Vec::new(),
482 fragment_annotations: Vec::new(),
483 listing_enabled: false,
484 resource_limits,
485 statement_count: 0,
486 label_count: 0,
487 literal_pool: Vec::new(),
488 literal_pool_index: alloc::collections::BTreeMap::new(),
489 literal_pool_counter: 0,
490 rvc_enabled: false,
491 thumb_func_pending: false,
492 thumb_labels: Vec::new(),
493 estimated_output_bytes: 0,
494 }
495 }
496
497 pub fn limits(&mut self, limits: ResourceLimits) -> &mut Self {
501 self.resource_limits = limits;
502 self.preprocessor
503 .set_max_recursion_depth(limits.max_recursion_depth);
504 self.preprocessor.set_max_iterations(limits.max_iterations);
505 self.preprocessor
506 .set_max_expanded_bytes(limits.max_expanded_bytes);
507 self.linker.set_max_output_bytes(limits.max_output_bytes);
508 self
509 }
510
511 pub fn syntax(&mut self, syntax: Syntax) -> &mut Self {
516 self.syntax = syntax;
517 self
518 }
519
520 pub fn optimize(&mut self, level: OptLevel) -> &mut Self {
547 self.opt_level = level;
548 self
549 }
550
551 pub fn enable_listing(&mut self) -> &mut Self {
572 self.listing_enabled = true;
573 self
574 }
575
576 pub fn base_address(&mut self, addr: u64) -> &mut Self {
578 self.linker.set_base_address(addr);
579 self
580 }
581
582 pub fn define_external(&mut self, name: &str, addr: u64) -> &mut Self {
597 self.linker.define_external(name, addr);
598 self
599 }
600
601 pub fn define_constant(&mut self, name: &str, value: i128) -> &mut Self {
603 self.linker.define_constant(name, value);
604 self
605 }
606
607 pub fn emit(&mut self, source: &str) -> Result<&mut Self, AsmError> {
614 if source.len() > self.resource_limits.max_source_bytes {
616 return Err(AsmError::ResourceLimitExceeded {
617 resource: String::from("source bytes"),
618 limit: self.resource_limits.max_source_bytes,
619 });
620 }
621 let expanded = self.preprocessor.process(source)?;
623 let tokens = lexer::tokenize_with_syntax(&expanded, self.syntax)?;
630 let (arch, syntax) = (self.arch, self.syntax);
632 parser::parse_streaming(&tokens, arch, syntax, |mut stmt| {
633 self.process_statement(&mut stmt, &expanded)
634 })?;
635 Ok(self)
636 }
637
638 pub fn define_preprocessor_symbol(&mut self, name: &str, value: i128) -> &mut Self {
643 self.preprocessor.define_symbol(name, value);
644 self
645 }
646
647 pub fn label(&mut self, name: &str) -> Result<&mut Self, AsmError> {
667 self.label_count += 1;
668 if self.label_count > self.resource_limits.max_labels {
669 return Err(AsmError::ResourceLimitExceeded {
670 resource: String::from("labels"),
671 limit: self.resource_limits.max_labels,
672 });
673 }
674 self.linker.add_label(name, Span::new(0, 0, 0, 0))?;
675 Ok(self)
676 }
677
678 pub fn db(&mut self, bytes: &[u8]) -> Result<&mut Self, AsmError> {
685 self.check_output_limit(bytes.len())?;
686 self.linker.add_bytes(bytes.to_vec(), Span::new(0, 0, 0, 0));
687 Ok(self)
688 }
689
690 pub fn dw(&mut self, value: u16) -> Result<&mut Self, AsmError> {
697 self.check_output_limit(2)?;
698 self.linker
699 .add_bytes(value.to_le_bytes().to_vec(), Span::new(0, 0, 0, 0));
700 Ok(self)
701 }
702
703 pub fn dd(&mut self, value: u32) -> Result<&mut Self, AsmError> {
710 self.check_output_limit(4)?;
711 self.linker
712 .add_bytes(value.to_le_bytes().to_vec(), Span::new(0, 0, 0, 0));
713 Ok(self)
714 }
715
716 pub fn dq(&mut self, value: u64) -> Result<&mut Self, AsmError> {
723 self.check_output_limit(8)?;
724 self.linker
725 .add_bytes(value.to_le_bytes().to_vec(), Span::new(0, 0, 0, 0));
726 Ok(self)
727 }
728
729 pub fn ascii(&mut self, s: &str) -> Result<&mut Self, AsmError> {
736 self.check_output_limit(s.len())?;
737 self.linker
738 .add_bytes(s.as_bytes().to_vec(), Span::new(0, 0, 0, 0));
739 Ok(self)
740 }
741
742 pub fn asciz(&mut self, s: &str) -> Result<&mut Self, AsmError> {
749 self.check_output_limit(s.len() + 1)?;
750 let mut bytes = s.as_bytes().to_vec();
751 bytes.push(0);
752 self.linker.add_bytes(bytes, Span::new(0, 0, 0, 0));
753 Ok(self)
754 }
755
756 pub fn align(&mut self, alignment: u32) -> &mut Self {
760 let use_nop = matches!(self.arch, Arch::X86 | Arch::X86_64);
761 self.linker
762 .add_alignment(alignment, 0x00, None, use_nop, Span::new(0, 0, 0, 0));
763 self
764 }
765
766 pub fn align_with_fill(&mut self, alignment: u32, fill: u8) -> &mut Self {
768 self.linker
769 .add_alignment(alignment, fill, None, false, Span::new(0, 0, 0, 0));
770 self
771 }
772
773 pub fn org(&mut self, target: u64) -> &mut Self {
775 self.linker.add_org(target, 0x00, Span::new(0, 0, 0, 0));
776 self
777 }
778
779 pub fn org_with_fill(&mut self, target: u64, fill: u8) -> &mut Self {
781 self.linker.add_org(target, fill, Span::new(0, 0, 0, 0));
782 self
783 }
784
785 pub fn fill(&mut self, count: u32, size: u8, value: i64) -> Result<&mut Self, AsmError> {
794 let total = (count as usize).saturating_mul(size as usize);
795 self.check_output_limit(total)?;
796 let mut bytes = Vec::with_capacity(total);
797 let val_bytes = value.to_le_bytes();
799 for _ in 0..count {
800 for &b in val_bytes.iter().take(size as usize) {
801 bytes.push(b);
802 }
803 if (size as usize) > 8 {
805 bytes.resize(bytes.len() + size as usize - 8, 0);
806 }
807 }
808 self.linker.add_bytes(bytes, Span::new(0, 0, 0, 0));
809 Ok(self)
810 }
811
812 pub fn space(&mut self, n: u32) -> Result<&mut Self, AsmError> {
819 self.check_output_limit(n as usize)?;
820 let bytes = alloc::vec![0u8; n as usize];
821 self.linker.add_bytes(bytes, Span::new(0, 0, 0, 0));
822 Ok(self)
823 }
824
825 pub fn current_fragment_count(&self) -> usize {
829 self.linker.fragment_count()
830 }
831
832 pub fn encode_one(&self, source: &str) -> Result<Vec<u8>, AsmError> {
853 use crate::encoder::encode_instruction;
854
855 let tokens = crate::lexer::tokenize_with_syntax(source, self.syntax)?;
856 let stmts = crate::parser::parse_with_syntax(&tokens, self.arch, self.syntax)?;
857 if stmts.is_empty() {
858 return Ok(Vec::new());
859 }
860 match &stmts[0] {
861 crate::ir::Statement::Instruction(instr) => {
862 let mut instr = instr.clone();
864 self.resolve_constants_in_instruction(&mut instr);
865 let encoded = encode_instruction(&instr, self.arch)?;
866 Ok(encoded.bytes.to_vec())
867 }
868 _ => Err(AsmError::Syntax {
869 msg: String::from("expected an instruction"),
870 span: crate::error::Span::new(0, 0, 0, 0),
871 }),
872 }
873 }
874
875 pub fn reset(&mut self) -> &mut Self {
895 let base = self.linker.base_address();
896 self.linker = Linker::new();
897 self.linker.set_base_address(base);
898 self.preprocessor = Preprocessor::new();
899 self.limits(self.resource_limits);
901 self.errors.clear();
902 self.fragment_annotations.clear();
903 self.statement_count = 0;
905 self.label_count = 0;
906 self.literal_pool.clear();
907 self.literal_pool_index.clear();
908 self.literal_pool_counter = 0;
909 self.thumb_func_pending = false;
910 self.thumb_labels.clear();
911 self.estimated_output_bytes = 0;
912 self
915 }
916
917 fn check_output_limit(&mut self, additional: usize) -> Result<(), AsmError> {
922 self.estimated_output_bytes = self.estimated_output_bytes.saturating_add(additional);
926 if self.estimated_output_bytes > self.resource_limits.max_output_bytes {
927 return Err(AsmError::ResourceLimitExceeded {
928 resource: String::from("output bytes"),
929 limit: self.resource_limits.max_output_bytes,
930 });
931 }
932 Ok(())
933 }
934
935 pub fn finish(mut self) -> Result<AssemblyResult, AsmError> {
942 if !self.errors.is_empty() {
943 if self.errors.len() == 1 {
944 return Err(self.errors.remove(0));
945 }
946 return Err(AsmError::Multiple {
947 errors: self.errors,
948 });
949 }
950
951 let base = self.linker.base_address();
952
953 let flush_span = crate::error::Span::new(0, 0, 0, 0);
955 self.flush_literal_pool(flush_span)?;
956
957 let (bytes, mut labels, relocations, offsets) = self.linker.resolve()?;
958
959 for (name, addr) in labels.iter_mut() {
961 if self.thumb_labels.iter().any(|t| t == name) {
962 *addr |= 1;
963 }
964 }
965
966 labels.sort_by(|(a, _), (b, _)| a.cmp(b));
972
973 if bytes.len() > self.resource_limits.max_output_bytes {
975 return Err(AsmError::ResourceLimitExceeded {
976 resource: String::from("output bytes"),
977 limit: self.resource_limits.max_output_bytes,
978 });
979 }
980
981 let source_annotations = self.build_source_annotations(&offsets);
984
985 Ok(AssemblyResult {
986 bytes,
987 labels,
988 relocations,
989 base_address: base,
990 source_annotations,
991 })
992 }
993
994 fn build_source_annotations(&self, offsets: &[u64]) -> Vec<(u64, String)> {
997 let mut annotations = Vec::new();
998 for &(frag_idx, ref text) in &self.fragment_annotations {
999 if frag_idx < offsets.len() {
1000 annotations.push((offsets[frag_idx], text.clone()));
1001 }
1002 }
1003 annotations
1004 }
1005
1006 fn process_statement(&mut self, stmt: &mut Statement, source: &str) -> Result<(), AsmError> {
1011 self.statement_count += 1;
1012 if self.statement_count > self.resource_limits.max_statements {
1013 return Err(AsmError::ResourceLimitExceeded {
1014 resource: String::from("statements"),
1015 limit: self.resource_limits.max_statements,
1016 });
1017 }
1018
1019 {
1020 match stmt {
1021 Statement::Label(name, span) => {
1022 self.label_count += 1;
1023 if self.label_count > self.resource_limits.max_labels {
1024 return Err(AsmError::ResourceLimitExceeded {
1025 resource: String::from("labels"),
1026 limit: self.resource_limits.max_labels,
1027 });
1028 }
1029 self.linker.add_label(name, *span)?;
1030 if self.thumb_func_pending {
1032 self.thumb_labels.push(name.clone());
1033 self.thumb_func_pending = false;
1034 }
1035 }
1036
1037 Statement::Instruction(instr) => {
1038 let frag_idx = self.linker.fragment_count();
1039 self.resolve_constants_in_instruction(instr);
1041 self.transform_literal_pool_operands(instr);
1043 crate::optimize::optimize_instruction(instr, self.arch, self.opt_level);
1044 let encode_result = if self.x86_mode == crate::ir::X86Mode::Mode16 {
1045 #[cfg(feature = "x86")]
1046 {
1047 encoder::encode_instruction_16(instr)
1048 }
1049 #[cfg(not(feature = "x86"))]
1050 {
1051 encoder::encode_instruction(instr, self.arch)
1052 }
1053 } else {
1054 encoder::encode_instruction(instr, self.arch)
1055 };
1056 #[cfg(feature = "riscv")]
1060 let encode_result = if self.rvc_enabled
1061 && matches!(self.arch, Arch::Rv32 | Arch::Rv64)
1062 && !instr.mnemonic.starts_with("c.")
1063 {
1064 match encode_result {
1065 Ok(ref enc) if enc.bytes.len() == 4 && enc.relocation.is_none() => {
1066 let is_rv64 = self.arch == Arch::Rv64;
1067 if let Some(hw) = crate::riscv::try_compress(
1068 &instr.mnemonic,
1069 &instr.operands,
1070 is_rv64,
1071 instr.span,
1072 ) {
1073 Ok(crate::riscv::rvc_instr(hw))
1074 } else {
1075 encode_result
1076 }
1077 }
1078 _ => encode_result,
1079 }
1080 } else {
1081 encode_result
1082 };
1083 match encode_result {
1084 Ok(encoded) => {
1085 self.check_output_limit(encoded.bytes.len())?;
1086 self.linker.add_encoded(
1087 encoded.bytes,
1088 encoded.relocation,
1089 encoded.relax,
1090 instr.span,
1091 )?;
1092 self.annotate(frag_idx, source, instr.span);
1093 }
1094 Err(e) => {
1095 self.errors.push(e);
1096 if self.errors.len() >= self.resource_limits.max_errors {
1097 return Err(AsmError::ResourceLimitExceeded {
1098 resource: String::from("errors"),
1099 limit: self.resource_limits.max_errors,
1100 });
1101 }
1102 }
1103 }
1104 }
1105
1106 Statement::Data(data) => {
1107 let frag_idx = self.linker.fragment_count();
1108 let span = data.span;
1109 self.emit_data(data)?;
1110 self.annotate(frag_idx, source, span);
1111 }
1112
1113 Statement::Align(align) => {
1114 let frag_idx = self.linker.fragment_count();
1115 let span = align.span;
1116 let use_nop =
1120 align.fill.is_none() && matches!(self.arch, Arch::X86 | Arch::X86_64);
1121 self.linker.add_alignment(
1122 align.alignment,
1123 align.fill.unwrap_or(0x00),
1124 align.max_skip,
1125 use_nop,
1126 align.span,
1127 );
1128 self.annotate(frag_idx, source, span);
1129 }
1130
1131 Statement::Const(c) => {
1132 self.linker.define_constant(&c.name, c.value);
1133 }
1134
1135 Statement::Fill(fill) => {
1136 let frag_idx = self.linker.fragment_count();
1137 let span = fill.span;
1138 let total = (fill.count as usize).saturating_mul(fill.size as usize);
1139 self.check_output_limit(total)?;
1140 let mut bytes = Vec::with_capacity(total);
1141 let val_bytes = fill.value.to_le_bytes();
1144 for _ in 0..fill.count {
1145 for &b in val_bytes.iter().take(fill.size as usize) {
1146 bytes.push(b);
1147 }
1148 if (fill.size as usize) > 8 {
1150 bytes.resize(bytes.len() + fill.size as usize - 8, 0);
1151 }
1152 }
1153 self.linker.add_bytes(bytes, fill.span);
1154 self.annotate(frag_idx, source, span);
1155 }
1156
1157 Statement::Space(space) => {
1158 let frag_idx = self.linker.fragment_count();
1159 let span = space.span;
1160 self.check_output_limit(space.size as usize)?;
1161 let bytes = alloc::vec![space.fill; space.size as usize];
1162 self.linker.add_bytes(bytes, space.span);
1163 self.annotate(frag_idx, source, span);
1164 }
1165
1166 Statement::Org(org) => {
1167 let frag_idx = self.linker.fragment_count();
1168 let span = org.span;
1169 self.linker.add_org(org.offset, org.fill, org.span);
1173 self.annotate(frag_idx, source, span);
1174 }
1175
1176 Statement::CodeMode(mode, span) => {
1177 if !matches!(self.arch, Arch::X86 | Arch::X86_64) {
1179 return Err(AsmError::Syntax {
1180 msg: String::from(".code16/.code32/.code64 only valid for x86/x86-64"),
1181 span: *span,
1182 });
1183 }
1184 self.x86_mode = *mode;
1185 match mode {
1187 crate::ir::X86Mode::Mode16 | crate::ir::X86Mode::Mode32 => {
1188 self.arch = Arch::X86;
1189 }
1190 crate::ir::X86Mode::Mode64 => {
1191 self.arch = Arch::X86_64;
1192 }
1193 }
1194 }
1195
1196 Statement::Ltorg(span) => {
1197 let span = *span;
1199 self.flush_literal_pool(span)?;
1200 }
1201
1202 Statement::OptionRvc(enable, span) => {
1203 if !matches!(self.arch, Arch::Rv32 | Arch::Rv64) {
1205 return Err(AsmError::Syntax {
1206 msg: String::from(".option rvc/norvc is only valid for RISC-V"),
1207 span: *span,
1208 });
1209 }
1210 self.rvc_enabled = *enable;
1211 }
1212
1213 Statement::ThumbMode(is_thumb, span) => {
1214 if !matches!(self.arch, Arch::Arm | Arch::Thumb) {
1216 return Err(AsmError::Syntax {
1217 msg: String::from(".thumb/.arm only valid for ARM"),
1218 span: *span,
1219 });
1220 }
1221 self.arch = if *is_thumb { Arch::Thumb } else { Arch::Arm };
1222 }
1223
1224 Statement::ThumbFunc(span) => {
1225 if !matches!(self.arch, Arch::Arm | Arch::Thumb) {
1227 return Err(AsmError::Syntax {
1228 msg: String::from(".thumb_func only valid for ARM/Thumb"),
1229 span: *span,
1230 });
1231 }
1232 self.arch = Arch::Thumb;
1234 self.thumb_func_pending = true;
1235 }
1236 }
1237 }
1238 Ok(())
1239 }
1240
1241 #[inline]
1243 fn annotate(&mut self, frag_idx: usize, source: &str, span: Span) {
1244 if self.listing_enabled {
1245 let src_text = extract_source_line(source, span);
1246 if !src_text.is_empty() {
1247 self.fragment_annotations
1248 .push((frag_idx, src_text.to_string()));
1249 }
1250 }
1251 }
1252
1253 fn transform_literal_pool_operands(&mut self, instr: &mut Instruction) {
1263 let size: u8 = instr
1268 .operands
1269 .iter()
1270 .find_map(|op| {
1271 if let Operand::Register(r) = op {
1272 if r.is_arm() {
1273 return Some(4u8); }
1275 if r.is_aarch64() {
1276 return Some(if r.is_a64_64bit() { 8u8 } else { 4u8 });
1277 }
1278 }
1279 None
1280 })
1281 .unwrap_or(8);
1282
1283 for op in &mut instr.operands {
1284 if let Operand::LiteralPoolValue(val) = op {
1285 let val = *val;
1286
1287 let label = match self.literal_pool_index.get(&(val, size)) {
1289 Some(&idx) => self.literal_pool[idx].label.clone(),
1290 None => {
1291 let label = alloc::format!(".Lpool_{}", self.literal_pool_counter);
1292 self.literal_pool_counter += 1;
1293 self.literal_pool_index
1294 .insert((val, size), self.literal_pool.len());
1295 self.literal_pool.push(LiteralPoolEntry {
1296 value: val,
1297 size,
1298 label: label.clone(),
1299 });
1300 label
1301 }
1302 };
1303
1304 *op = Operand::Label(label);
1305 }
1306 }
1307 }
1308
1309 fn flush_literal_pool(&mut self, span: Span) -> Result<(), AsmError> {
1314 if self.literal_pool.is_empty() {
1315 return Ok(());
1316 }
1317
1318 let max_align = self
1320 .literal_pool
1321 .iter()
1322 .map(|e| e.size as u32)
1323 .max()
1324 .unwrap_or(4);
1325 self.linker
1326 .add_alignment(max_align, 0x00, None, false, span);
1327
1328 let entries: Vec<LiteralPoolEntry> = core::mem::take(&mut self.literal_pool);
1330 self.literal_pool_index.clear();
1331 for entry in &entries {
1332 self.linker.add_label(&entry.label, span)?;
1333 let bytes = match entry.size {
1334 4 => (entry.value as u32).to_le_bytes().to_vec(),
1335 8 => (entry.value as u64).to_le_bytes().to_vec(),
1336 _ => (entry.value as u64).to_le_bytes().to_vec(),
1337 };
1338 self.linker.add_bytes(bytes, span);
1339 }
1340
1341 Ok(())
1342 }
1343
1344 fn resolve_constants_in_instruction(&self, instr: &mut Instruction) {
1349 for op in &mut instr.operands {
1350 match op {
1351 Operand::Label(name) => {
1352 if let Some(&value) = self.linker.get_constant(name) {
1353 *op = Operand::Immediate(value);
1354 }
1355 }
1356 Operand::Expression(expr) => {
1357 expr.resolve_constants(|name| self.linker.get_constant(name).copied());
1359 if let Some(val) = expr.eval() {
1361 *op = Operand::Immediate(val);
1362 }
1363 }
1364 Operand::Memory(mem) => {
1365 if let Some(ref label) = mem.disp_label {
1367 if let Some(&value) = self.linker.get_constant(label) {
1368 mem.disp = mem.disp.wrapping_add(value as i64);
1369 mem.disp_label = None;
1370 }
1371 }
1372 }
1373 _ => {}
1374 }
1375 }
1376 }
1377
1378 fn emit_data(&mut self, data: &DataDecl) -> Result<(), AsmError> {
1380 use crate::encoder::Relocation;
1381
1382 let data_item_size: usize = match data.size {
1383 DataSize::Byte => 1,
1384 DataSize::Word => 2,
1385 DataSize::Long => 4,
1386 DataSize::Quad => 8,
1387 };
1388
1389 let mut pending: Vec<u8> = Vec::new();
1391
1392 for value in &data.values {
1393 match value {
1394 DataValue::Integer(n) => match data.size {
1395 DataSize::Byte => pending.push(*n as u8),
1396 DataSize::Word => pending.extend_from_slice(&(*n as u16).to_le_bytes()),
1397 DataSize::Long => pending.extend_from_slice(&(*n as u32).to_le_bytes()),
1398 DataSize::Quad => pending.extend_from_slice(&(*n as u64).to_le_bytes()),
1399 },
1400 DataValue::Bytes(b) => {
1401 pending.extend_from_slice(b);
1402 }
1403 DataValue::Label(name, addend) => {
1404 if let Some(&const_val) = self.linker.get_constant(name) {
1406 let val = const_val.wrapping_add(*addend as i128);
1407 match data.size {
1408 DataSize::Byte => pending.push(val as u8),
1409 DataSize::Word => {
1410 pending.extend_from_slice(&(val as u16).to_le_bytes())
1411 }
1412 DataSize::Long => {
1413 pending.extend_from_slice(&(val as u32).to_le_bytes())
1414 }
1415 DataSize::Quad => {
1416 pending.extend_from_slice(&(val as u64).to_le_bytes())
1417 }
1418 }
1419 continue;
1420 }
1421
1422 if !pending.is_empty() {
1424 self.linker
1425 .add_bytes(core::mem::take(&mut pending), data.span);
1426 }
1427 let mut slot = encoder::InstrBytes::new();
1429 for _ in 0..data_item_size {
1430 slot.push(0);
1431 }
1432 let reloc = Relocation {
1433 offset: 0,
1434 size: data_item_size as u8,
1435 label: alloc::rc::Rc::from(name.as_str()),
1436 kind: encoder::RelocKind::Absolute,
1437 addend: *addend,
1438 trailing_bytes: 0,
1439 };
1440 self.linker
1442 .add_encoded(slot, Some(reloc), None, data.span)?;
1443 }
1444 }
1445 }
1446
1447 if !pending.is_empty() {
1449 self.linker.add_bytes(pending, data.span);
1450 }
1451
1452 Ok(())
1453 }
1454}
1455
1456fn extract_source_line(source: &str, span: Span) -> &str {
1461 let offset = span.offset;
1462 if offset >= source.len() {
1463 return "";
1464 }
1465 let line_start = source[..offset].rfind('\n').map_or(0, |p| p + 1);
1467 let line_end = source[offset..]
1469 .find('\n')
1470 .map_or(source.len(), |p| offset + p);
1471 source[line_start..line_end].trim()
1472}
1473
1474#[cfg(test)]
1475mod tests {
1476 use super::*;
1477
1478 #[test]
1481 fn assemble_nop() {
1482 let mut asm = Assembler::new(Arch::X86_64);
1483 asm.emit("nop").unwrap();
1484 let result = asm.finish().unwrap();
1485 assert_eq!(result.bytes(), &[0x90]);
1486 }
1487
1488 #[test]
1489 fn assemble_ret() {
1490 let mut asm = Assembler::new(Arch::X86_64);
1491 asm.emit("ret").unwrap();
1492 let result = asm.finish().unwrap();
1493 assert_eq!(result.bytes(), &[0xC3]);
1494 }
1495
1496 #[test]
1497 fn assemble_multiple_instructions() {
1498 let mut asm = Assembler::new(Arch::X86_64);
1499 asm.emit("nop\nret").unwrap();
1500 let result = asm.finish().unwrap();
1501 assert_eq!(result.bytes(), &[0x90, 0xC3]);
1502 }
1503
1504 #[test]
1505 fn assemble_push_pop() {
1506 let mut asm = Assembler::new(Arch::X86_64);
1507 asm.emit("push rbp").unwrap();
1508 asm.emit("mov rbp, rsp").unwrap();
1509 asm.emit("pop rbp").unwrap();
1510 asm.emit("ret").unwrap();
1511 let result = asm.finish().unwrap();
1512 let bytes = result.bytes();
1513 assert_eq!(bytes[0], 0x55); assert_eq!(*bytes.last().unwrap(), 0xC3); }
1516
1517 #[test]
1518 fn assemble_with_label() {
1519 let mut asm = Assembler::new(Arch::X86_64);
1520 asm.emit("jmp target\ntarget:\nnop").unwrap();
1521 let result = asm.finish().unwrap();
1522 let bytes = result.bytes();
1523 assert_eq!(bytes[0], 0xEB); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0x90); }
1528
1529 #[test]
1530 fn assemble_backward_jump() {
1531 let mut asm = Assembler::new(Arch::X86_64);
1532 asm.emit("loop_start:\nnop\njmp loop_start").unwrap();
1533 let result = asm.finish().unwrap();
1534 let bytes = result.bytes();
1535 assert_eq!(bytes[0], 0x90); assert_eq!(bytes[1], 0xEB); assert_eq!(bytes[2], 0xFD);
1540 }
1541
1542 #[test]
1543 fn assemble_conditional_jump() {
1544 let mut asm = Assembler::new(Arch::X86_64);
1545 asm.emit("cmp rax, 0\nje done\nnop\ndone:\nret").unwrap();
1546 let result = asm.finish().unwrap();
1547 let bytes = result.bytes();
1548 assert!(!bytes.is_empty());
1550 assert_eq!(*bytes.last().unwrap(), 0xC3);
1552 }
1553
1554 #[test]
1555 fn assemble_xor_self() {
1556 let mut asm = Assembler::new(Arch::X86_64);
1557 asm.emit("xor eax, eax").unwrap();
1558 let result = asm.finish().unwrap();
1559 assert_eq!(result.bytes(), &[0x31, 0xC0]);
1560 }
1561
1562 #[test]
1563 fn assemble_syscall_stub() {
1564 let mut asm = Assembler::new(Arch::X86_64);
1565 asm.emit("mov eax, 60\nxor edi, edi\nsyscall").unwrap();
1566 let result = asm.finish().unwrap();
1567 let bytes = result.bytes();
1568 assert_eq!(&bytes[0..5], &[0xB8, 0x3C, 0x00, 0x00, 0x00]);
1570 assert_eq!(&bytes[bytes.len() - 2..], &[0x0F, 0x05]);
1572 }
1573
1574 #[test]
1577 fn builder_api() {
1578 let mut asm = Assembler::new(Arch::X86_64);
1579 asm.emit("push rbp").unwrap();
1580 asm.db(&[0xCC]).unwrap(); asm.emit("pop rbp").unwrap();
1582 asm.emit("ret").unwrap();
1583 let result = asm.finish().unwrap();
1584 let bytes = result.bytes();
1585 assert_eq!(bytes[0], 0x55); assert_eq!(bytes[1], 0xCC); }
1588
1589 #[test]
1590 fn builder_label() {
1591 let mut asm = Assembler::new(Arch::X86_64);
1592 asm.emit("jmp target").unwrap();
1593 asm.label("target").unwrap();
1594 asm.emit("ret").unwrap();
1595 let result = asm.finish().unwrap();
1596 let bytes = result.bytes();
1597 assert_eq!(bytes[0], 0xEB);
1599 assert_eq!(*bytes.last().unwrap(), 0xC3);
1600 }
1601
1602 #[test]
1603 fn builder_data_words() {
1604 let mut asm = Assembler::new(Arch::X86_64);
1605 asm.dw(0x1234).unwrap();
1606 asm.dd(0xDEADBEEF).unwrap();
1607 let result = asm.finish().unwrap();
1608 let bytes = result.bytes();
1609 assert_eq!(&bytes[0..2], &[0x34, 0x12]);
1610 assert_eq!(&bytes[2..6], &[0xEF, 0xBE, 0xAD, 0xDE]);
1611 }
1612
1613 #[test]
1616 fn assemble_byte_directive() {
1617 let mut asm = Assembler::new(Arch::X86_64);
1618 asm.emit(".byte 0x90, 0xCC, 0xC3").unwrap();
1619 let result = asm.finish().unwrap();
1620 assert_eq!(result.bytes(), &[0x90, 0xCC, 0xC3]);
1621 }
1622
1623 #[test]
1624 fn assemble_word_directive() {
1625 let mut asm = Assembler::new(Arch::X86_64);
1626 asm.emit(".word 0x1234").unwrap();
1627 let result = asm.finish().unwrap();
1628 assert_eq!(result.bytes(), &[0x34, 0x12]);
1629 }
1630
1631 #[test]
1632 fn assemble_asciz_directive() {
1633 let mut asm = Assembler::new(Arch::X86_64);
1634 asm.emit(".asciz \"hello\"").unwrap();
1635 let result = asm.finish().unwrap();
1636 assert_eq!(result.bytes(), b"hello\0");
1637 }
1638
1639 #[test]
1640 fn assemble_equ_constant() {
1641 let mut asm = Assembler::new(Arch::X86_64);
1642 asm.emit(".equ EXIT, 60\nmov eax, EXIT").unwrap();
1643 let _result = asm.finish();
1645 }
1647
1648 #[test]
1649 fn assemble_fill_directive() {
1650 let mut asm = Assembler::new(Arch::X86_64);
1651 asm.emit(".fill 3, 1, 0x90").unwrap();
1652 let result = asm.finish().unwrap();
1653 assert_eq!(result.bytes(), &[0x90, 0x90, 0x90]);
1654 }
1655
1656 #[test]
1657 fn assemble_space_directive() {
1658 let mut asm = Assembler::new(Arch::X86_64);
1659 asm.emit(".space 4").unwrap();
1660 let result = asm.finish().unwrap();
1661 assert_eq!(result.bytes(), &[0, 0, 0, 0]);
1662 }
1663
1664 #[test]
1667 fn unknown_mnemonic_error() {
1668 let mut asm = Assembler::new(Arch::X86_64);
1669 asm.emit("foobar").unwrap(); let err = asm.finish().unwrap_err();
1671 assert!(matches!(err, AsmError::UnknownMnemonic { .. }));
1672 }
1673
1674 #[test]
1675 fn duplicate_label_error() {
1676 let mut asm = Assembler::new(Arch::X86_64);
1677 let err = asm.emit("foo:\nfoo:").unwrap_err();
1678 assert!(matches!(err, AsmError::DuplicateLabel { .. }));
1679 }
1680
1681 #[test]
1682 fn undefined_label_error() {
1683 let mut asm = Assembler::new(Arch::X86_64);
1684 asm.emit("jmp nowhere").unwrap();
1685 let err = asm.finish().unwrap_err();
1686 assert!(matches!(err, AsmError::UndefinedLabel { .. }));
1687 }
1688
1689 #[test]
1692 fn assemble_with_external() {
1693 let mut asm = Assembler::new(Arch::X86_64);
1694 asm.define_external("printf", 0x400000);
1695 asm.emit("mov rax, printf").unwrap();
1696 let result = asm.finish().unwrap();
1697 let bytes = result.bytes();
1698 assert_eq!(&bytes[bytes.len() - 8..], &0x400000u64.to_le_bytes());
1700 }
1701
1702 #[test]
1705 fn assemble_with_base_address() {
1706 let mut asm = Assembler::new(Arch::X86_64);
1707 asm.base_address(0x1000);
1708 asm.emit("nop").unwrap();
1709 let result = asm.finish().unwrap();
1710 assert_eq!(result.bytes(), &[0x90]);
1711 }
1712
1713 #[test]
1716 fn assemble_loop() {
1717 let mut asm = Assembler::new(Arch::X86_64);
1718 asm.emit(
1719 r#"
1720 mov ecx, 10
1721 loop_start:
1722 dec ecx
1723 jnz loop_start
1724 ret
1725 "#,
1726 )
1727 .unwrap();
1728 let result = asm.finish().unwrap();
1729 assert!(!result.is_empty());
1730 assert_eq!(*result.bytes().last().unwrap(), 0xC3);
1731 }
1732
1733 #[test]
1734 fn assemble_function_prologue_epilogue() {
1735 let mut asm = Assembler::new(Arch::X86_64);
1736 asm.emit(
1737 r#"
1738 push rbp
1739 mov rbp, rsp
1740 sub rsp, 0x20
1741 add rsp, 0x20
1742 pop rbp
1743 ret
1744 "#,
1745 )
1746 .unwrap();
1747 let result = asm.finish().unwrap();
1748 let bytes = result.bytes();
1749 assert_eq!(bytes[0], 0x55); assert_eq!(*bytes.last().unwrap(), 0xC3); }
1752
1753 #[test]
1754 fn result_length() {
1755 let mut asm = Assembler::new(Arch::X86_64);
1756 asm.emit("nop\nnop\nnop").unwrap();
1757 let result = asm.finish().unwrap();
1758 assert_eq!(result.len(), 3);
1759 }
1760
1761 #[test]
1762 fn result_into_bytes() {
1763 let mut asm = Assembler::new(Arch::X86_64);
1764 asm.emit("ret").unwrap();
1765 let result = asm.finish().unwrap();
1766 let bytes = result.into_bytes();
1767 assert_eq!(bytes, vec![0xC3]);
1768 }
1769
1770 #[test]
1773 fn semicolon_separated_instructions() {
1774 let mut asm = Assembler::new(Arch::X86_64);
1775 asm.emit("nop; nop; ret").unwrap();
1776 let result = asm.finish().unwrap();
1777 assert_eq!(result.bytes(), &[0x90, 0x90, 0xC3]);
1778 }
1779
1780 #[test]
1783 fn labels_returned() {
1784 let mut asm = Assembler::new(Arch::X86_64);
1785 asm.emit("start:\nnop\nnop\nend:\nret").unwrap();
1786 let result = asm.finish().unwrap();
1787 assert_eq!(result.label_address("start"), Some(0));
1788 assert_eq!(result.label_address("end"), Some(2));
1790 }
1791
1792 #[test]
1793 fn labels_with_base_address() {
1794 let mut asm = Assembler::new(Arch::X86_64);
1795 asm.base_address(0x400000);
1796 asm.emit("entry:\nnop").unwrap();
1797 let result = asm.finish().unwrap();
1798 assert_eq!(result.label_address("entry"), Some(0x400000));
1799 }
1800
1801 #[test]
1802 fn builder_label_address() {
1803 let mut asm = Assembler::new(Arch::X86_64);
1804 asm.label("before").unwrap();
1805 asm.emit("nop; nop; nop").unwrap();
1806 asm.label("after").unwrap();
1807 asm.emit("ret").unwrap();
1808 let result = asm.finish().unwrap();
1809 assert_eq!(result.label_address("before"), Some(0));
1810 assert_eq!(result.label_address("after"), Some(3));
1811 }
1812
1813 #[test]
1816 fn builder_syntax_and_optimize() {
1817 let mut asm = Assembler::new(Arch::X86_64);
1818 asm.syntax(Syntax::Intel);
1819 asm.optimize(OptLevel::Size);
1820 asm.emit("nop").unwrap();
1821 let result = asm.finish().unwrap();
1822 assert_eq!(result.bytes(), &[0x90]);
1823 }
1824
1825 #[test]
1828 fn builder_define_constant() {
1829 let mut asm = Assembler::new(Arch::X86_64);
1830 asm.define_constant("EXIT", 60);
1831 asm.emit("mov eax, EXIT").unwrap();
1832 let result = asm.finish().unwrap();
1833 assert_eq!(result.bytes(), &[0xB8, 0x3C, 0x00, 0x00, 0x00]);
1835 }
1836
1837 #[test]
1840 fn short_branch_uses_rel8() {
1841 let mut asm = Assembler::new(Arch::X86_64);
1842 asm.emit("je done\ndone:\nret").unwrap();
1843 let result = asm.finish().unwrap();
1844 assert_eq!(result.bytes(), &[0x74, 0x00, 0xC3]);
1846 }
1847
1848 #[test]
1851 fn quad_label_reference() {
1852 let mut asm = Assembler::new(Arch::X86_64);
1853 asm.base_address(0x1000);
1854 asm.emit("func:\nnop\nret\njump_table:\n.quad func")
1855 .unwrap();
1856 let result = asm.finish().unwrap();
1857 let bytes = result.bytes();
1858 let qw = u64::from_le_bytes(bytes[2..10].try_into().unwrap());
1861 assert_eq!(qw, 0x1000);
1862 }
1863
1864 #[test]
1865 fn long_label_reference() {
1866 let mut asm = Assembler::new(Arch::X86_64);
1867 asm.base_address(0x2000);
1868 asm.emit("entry:\nnop\n.long entry").unwrap();
1869 let result = asm.finish().unwrap();
1870 let bytes = result.bytes();
1871 let dw = u32::from_le_bytes(bytes[1..5].try_into().unwrap());
1873 assert_eq!(dw, 0x2000);
1874 }
1875
1876 #[test]
1877 fn name_equals_constant_in_instruction() {
1878 let mut asm = Assembler::new(Arch::X86_64);
1879 asm.emit("ANSWER = 42\nmov eax, ANSWER").unwrap();
1880 let result = asm.finish().unwrap();
1881 assert_eq!(result.bytes(), &[0xB8, 0x2A, 0x00, 0x00, 0x00]);
1883 }
1884
1885 #[test]
1888 fn listing_simple() {
1889 let mut asm = Assembler::new(Arch::X86_64);
1890 asm.enable_listing();
1891 asm.emit("nop\nret").unwrap();
1892 let result = asm.finish().unwrap();
1893 let listing = result.listing();
1894 assert!(listing.contains("00000000"));
1895 assert!(listing.contains("90")); assert!(listing.contains("C3")); assert!(listing.contains("nop"));
1899 assert!(listing.contains("ret"));
1900 }
1901
1902 #[test]
1903 fn listing_with_labels() {
1904 let mut asm = Assembler::new(Arch::X86_64);
1905 asm.enable_listing();
1906 asm.emit("start:\nnop\nend:\nret").unwrap();
1907 let result = asm.finish().unwrap();
1908 let listing = result.listing();
1909 assert!(listing.contains("start:"));
1910 assert!(listing.contains("end:"));
1911 assert!(listing.contains("nop"));
1913 assert!(listing.contains("ret"));
1914 }
1915
1916 #[test]
1917 fn listing_with_base_address() {
1918 let mut asm = Assembler::new(Arch::X86_64);
1919 asm.enable_listing();
1920 asm.base_address(0x401000);
1921 asm.emit("nop").unwrap();
1922 let result = asm.finish().unwrap();
1923 let listing = result.listing();
1924 assert!(listing.contains("00401000"));
1925 assert!(listing.contains("nop"));
1926 }
1927
1928 #[test]
1929 fn listing_base_address_accessor() {
1930 let mut asm = Assembler::new(Arch::X86_64);
1931 asm.base_address(0x1000);
1932 asm.emit("nop").unwrap();
1933 let result = asm.finish().unwrap();
1934 assert_eq!(result.base_address(), 0x1000);
1935 }
1936
1937 #[test]
1938 fn listing_hex_format() {
1939 let mut asm = Assembler::new(Arch::X86_64);
1940 asm.enable_listing();
1941 asm.emit("push rbp\nmov rbp, rsp").unwrap();
1942 let result = asm.finish().unwrap();
1943 let listing = result.listing();
1944 assert!(listing.contains("55"));
1946 assert!(listing.contains("4889E5"));
1948 assert!(listing.contains("push rbp"));
1950 assert!(listing.contains("mov rbp, rsp"));
1951 }
1952
1953 #[test]
1954 fn listing_source_annotations() {
1955 let mut asm = Assembler::new(Arch::X86_64);
1956 asm.enable_listing();
1957 asm.emit("mov eax, 1\nadd eax, 2\nret").unwrap();
1958 let result = asm.finish().unwrap();
1959 let listing = result.listing();
1960 assert!(listing.contains("mov eax, 1"));
1962 assert!(listing.contains("add eax, 2"));
1963 assert!(listing.contains("ret"));
1964 }
1965
1966 #[test]
1967 fn listing_data_annotation() {
1968 let mut asm = Assembler::new(Arch::X86_64);
1969 asm.enable_listing();
1970 asm.emit(".byte 0x90, 0xCC").unwrap();
1971 let result = asm.finish().unwrap();
1972 let listing = result.listing();
1973 assert!(listing.contains(".byte 0x90, 0xCC"));
1974 }
1975
1976 #[test]
1979 fn relocations_returned() {
1980 let mut asm = Assembler::new(Arch::X86_64);
1981 asm.emit("jmp target\nnop\ntarget:\nret").unwrap();
1982 let result = asm.finish().unwrap();
1983 assert!(!result.relocations().is_empty());
1984 assert_eq!(result.relocations()[0].label, "target");
1985 }
1986
1987 #[test]
1988 fn relocations_for_call() {
1989 let mut asm = Assembler::new(Arch::X86_64);
1990 asm.emit("call func\nfunc:\nret").unwrap();
1991 let result = asm.finish().unwrap();
1992 let relocs = result.relocations();
1993 assert!(!relocs.is_empty());
1994 assert_eq!(relocs[0].label, "func");
1995 assert_eq!(relocs[0].kind, crate::encoder::RelocKind::X86Relative);
1996 }
1997
1998 #[test]
2001 fn builder_ascii() {
2002 let mut asm = Assembler::new(Arch::X86_64);
2003 asm.ascii("AB").unwrap();
2004 let result = asm.finish().unwrap();
2005 assert_eq!(result.bytes(), &[0x41, 0x42]);
2006 }
2007
2008 #[test]
2009 fn builder_asciz() {
2010 let mut asm = Assembler::new(Arch::X86_64);
2011 asm.asciz("Hi").unwrap();
2012 let result = asm.finish().unwrap();
2013 assert_eq!(result.bytes(), &[0x48, 0x69, 0x00]);
2014 }
2015
2016 #[test]
2017 fn builder_align() {
2018 let mut asm = Assembler::new(Arch::X86_64);
2019 asm.db(&[0x90]).unwrap(); asm.align(4); asm.db(&[0xCC]).unwrap();
2022 let result = asm.finish().unwrap();
2023 assert_eq!(result.bytes().len(), 5); assert_eq!(result.bytes()[4], 0xCC);
2025 }
2026
2027 #[test]
2028 fn builder_align_with_fill() {
2029 let mut asm = Assembler::new(Arch::X86_64);
2030 asm.db(&[0x90]).unwrap();
2031 asm.align_with_fill(4, 0xAA);
2032 asm.db(&[0xCC]).unwrap();
2033 let result = asm.finish().unwrap();
2034 assert_eq!(result.bytes()[1], 0xAA);
2035 assert_eq!(result.bytes()[2], 0xAA);
2036 assert_eq!(result.bytes()[3], 0xAA);
2037 }
2038
2039 #[test]
2040 fn builder_org() {
2041 let mut asm = Assembler::new(Arch::X86_64);
2042 asm.db(&[0x90]).unwrap();
2043 asm.org(4);
2044 asm.db(&[0xCC]).unwrap();
2045 let result = asm.finish().unwrap();
2046 assert_eq!(result.bytes(), &[0x90, 0x00, 0x00, 0x00, 0xCC]);
2047 }
2048
2049 #[test]
2050 fn builder_org_with_fill() {
2051 let mut asm = Assembler::new(Arch::X86_64);
2052 asm.db(&[0x90]).unwrap();
2053 asm.org_with_fill(4, 0xFF);
2054 asm.db(&[0xCC]).unwrap();
2055 let result = asm.finish().unwrap();
2056 assert_eq!(result.bytes(), &[0x90, 0xFF, 0xFF, 0xFF, 0xCC]);
2057 }
2058
2059 #[test]
2060 fn builder_fill() {
2061 let mut asm = Assembler::new(Arch::X86_64);
2064 asm.fill(3, 2, 0xAB).unwrap();
2065 let result = asm.finish().unwrap();
2066 assert_eq!(result.bytes(), &[0xAB, 0x00, 0xAB, 0x00, 0xAB, 0x00]);
2067 }
2068
2069 #[test]
2070 fn builder_fill_size_1() {
2071 let mut asm = Assembler::new(Arch::X86_64);
2073 asm.fill(4, 1, 0xCC).unwrap();
2074 let result = asm.finish().unwrap();
2075 assert_eq!(result.bytes(), &[0xCC, 0xCC, 0xCC, 0xCC]);
2076 }
2077
2078 #[test]
2079 fn builder_fill_multi_byte_value() {
2080 let mut asm = Assembler::new(Arch::X86_64);
2082 asm.fill(1, 4, 0xDEADBEEFu32 as i64).unwrap();
2083 let result = asm.finish().unwrap();
2084 assert_eq!(result.bytes(), &[0xEF, 0xBE, 0xAD, 0xDE]);
2085 }
2086
2087 #[test]
2088 fn builder_fill_16bit_value() {
2089 let mut asm = Assembler::new(Arch::X86_64);
2091 asm.fill(2, 2, 0x1234).unwrap();
2092 let result = asm.finish().unwrap();
2093 assert_eq!(result.bytes(), &[0x34, 0x12, 0x34, 0x12]);
2094 }
2095
2096 #[test]
2097 fn builder_space() {
2098 let mut asm = Assembler::new(Arch::X86_64);
2099 asm.space(4).unwrap();
2100 let result = asm.finish().unwrap();
2101 assert_eq!(result.bytes(), &[0x00, 0x00, 0x00, 0x00]);
2102 }
2103
2104 #[test]
2107 fn listing_fill_annotation() {
2108 let mut asm = Assembler::new(Arch::X86_64);
2109 asm.enable_listing();
2110 asm.emit(".fill 2, 1, 0x90").unwrap();
2111 let result = asm.finish().unwrap();
2112 let listing = result.listing();
2113 assert!(listing.contains(".fill 2, 1, 0x90"));
2114 }
2115
2116 #[test]
2117 fn listing_space_annotation() {
2118 let mut asm = Assembler::new(Arch::X86_64);
2119 asm.enable_listing();
2120 asm.emit(".space 4").unwrap();
2121 let result = asm.finish().unwrap();
2122 let listing = result.listing();
2123 assert!(listing.contains(".space 4"));
2124 }
2125
2126 #[test]
2127 fn listing_align_annotation() {
2128 let mut asm = Assembler::new(Arch::X86_64);
2129 asm.enable_listing();
2130 asm.emit("nop\n.align 4\nnop").unwrap();
2131 let result = asm.finish().unwrap();
2132 let listing = result.listing();
2133 assert!(listing.contains(".align 4"));
2134 }
2135
2136 #[test]
2137 fn listing_org_annotation() {
2138 let mut asm = Assembler::new(Arch::X86_64);
2139 asm.enable_listing();
2140 asm.emit("nop\n.org 0x10\nnop").unwrap();
2141 let result = asm.finish().unwrap();
2142 let listing = result.listing();
2143 assert!(listing.contains(".org 0x10"));
2144 }
2145
2146 #[test]
2149 fn org_with_fill_byte() {
2150 let mut asm = Assembler::new(Arch::X86_64);
2151 asm.emit("nop\n.org 0x04, 0xFF\nnop").unwrap();
2152 let result = asm.finish().unwrap();
2153 assert_eq!(result.bytes(), &[0x90, 0xFF, 0xFF, 0xFF, 0x90]);
2155 }
2156
2157 #[test]
2160 fn att_syntax_basic() {
2161 let mut asm = Assembler::new(Arch::X86_64);
2162 asm.syntax(Syntax::Att);
2163 asm.emit("movq $1, %rax").unwrap();
2164 let result = asm.finish().unwrap();
2165 assert_eq!(result.bytes(), &[0xB8, 0x01, 0x00, 0x00, 0x00]);
2167 }
2168
2169 #[test]
2172 fn resource_limit_max_statements() {
2173 let mut asm = Assembler::new(Arch::X86_64);
2174 asm.limits(ResourceLimits {
2175 max_statements: 3,
2176 ..ResourceLimits::default()
2177 });
2178 asm.emit("nop; nop; nop").unwrap();
2180 let err = asm.emit("nop; nop").unwrap_err();
2182 match err {
2183 AsmError::ResourceLimitExceeded { resource, limit } => {
2184 assert_eq!(resource, "statements");
2185 assert_eq!(limit, 3);
2186 }
2187 other => panic!("expected ResourceLimitExceeded, got: {other:?}"),
2188 }
2189 }
2190
2191 #[test]
2192 fn resource_limit_max_labels() {
2193 let mut asm = Assembler::new(Arch::X86_64);
2194 asm.limits(ResourceLimits {
2195 max_labels: 2,
2196 ..ResourceLimits::default()
2197 });
2198 asm.label("a").unwrap();
2199 asm.label("b").unwrap();
2200 let err = asm.label("c").unwrap_err();
2201 match err {
2202 AsmError::ResourceLimitExceeded { resource, limit } => {
2203 assert_eq!(resource, "labels");
2204 assert_eq!(limit, 2);
2205 }
2206 other => panic!("expected ResourceLimitExceeded, got: {other:?}"),
2207 }
2208 }
2209
2210 #[test]
2211 fn resource_limit_max_labels_via_emit() {
2212 let mut asm = Assembler::new(Arch::X86_64);
2213 asm.limits(ResourceLimits {
2214 max_labels: 1,
2215 ..ResourceLimits::default()
2216 });
2217 asm.emit("a: nop").unwrap();
2218 let err = asm.emit("b: nop").unwrap_err();
2219 match err {
2220 AsmError::ResourceLimitExceeded { resource, limit } => {
2221 assert_eq!(resource, "labels");
2222 assert_eq!(limit, 1);
2223 }
2224 other => panic!("expected ResourceLimitExceeded, got: {other:?}"),
2225 }
2226 }
2227
2228 #[test]
2229 fn resource_limit_max_output_bytes() {
2230 let mut asm = Assembler::new(Arch::X86_64);
2231 asm.limits(ResourceLimits {
2232 max_output_bytes: 4,
2233 ..ResourceLimits::default()
2234 });
2235 asm.emit("nop; nop; nop; nop").unwrap(); let result = asm.finish();
2237 assert!(result.is_ok());
2238
2239 let mut asm2 = Assembler::new(Arch::X86_64);
2241 asm2.limits(ResourceLimits {
2242 max_output_bytes: 3,
2243 ..ResourceLimits::default()
2244 });
2245 let err = asm2.emit("nop; nop; nop; nop").unwrap_err(); match err {
2247 AsmError::ResourceLimitExceeded { resource, limit } => {
2248 assert_eq!(resource, "output bytes");
2249 assert_eq!(limit, 3);
2250 }
2251 other => panic!("expected ResourceLimitExceeded, got: {other:?}"),
2252 }
2253 }
2254
2255 #[test]
2256 fn resource_limits_default_does_not_interfere() {
2257 let mut asm = Assembler::new(Arch::X86_64);
2259 let source: String = (0..1000).map(|_| "nop; ").collect();
2261 asm.emit(&source).unwrap();
2262 let result = asm.finish().unwrap();
2263 assert_eq!(result.len(), 1000);
2264 }
2265
2266 #[test]
2267 fn resource_limit_max_recursion_depth() {
2268 let mut asm = Assembler::new(Arch::X86_64);
2269 asm.limits(ResourceLimits {
2270 max_recursion_depth: 3,
2271 ..ResourceLimits::default()
2272 });
2273 let result = asm.emit(".macro boom\nboom\n.endm\nboom");
2275 assert!(result.is_err());
2276 let err = result.unwrap_err();
2277 match err {
2278 AsmError::ResourceLimitExceeded { resource, limit } => {
2279 assert_eq!(resource, "macro recursion depth");
2280 assert_eq!(limit, 3);
2281 }
2282 _ => panic!("expected ResourceLimitExceeded, got {:?}", err),
2283 }
2284 }
2285
2286 #[test]
2289 fn encode_one_nop() {
2290 let asm = Assembler::new(Arch::X86_64);
2291 let bytes = asm.encode_one("nop").unwrap();
2292 assert_eq!(bytes, alloc::vec![0x90]);
2293 }
2294
2295 #[test]
2296 fn encode_one_ret() {
2297 let asm = Assembler::new(Arch::X86_64);
2298 let bytes = asm.encode_one("ret").unwrap();
2299 assert_eq!(bytes, alloc::vec![0xC3]);
2300 }
2301
2302 #[test]
2303 fn encode_one_empty_input() {
2304 let asm = Assembler::new(Arch::X86_64);
2305 let bytes = asm.encode_one("").unwrap();
2306 assert!(bytes.is_empty());
2307 }
2308
2309 #[test]
2310 fn encode_one_rejects_label() {
2311 let asm = Assembler::new(Arch::X86_64);
2312 assert!(asm.encode_one("foo:").is_err());
2313 }
2314
2315 #[test]
2316 fn encode_one_does_not_affect_state() {
2317 let asm = Assembler::new(Arch::X86_64);
2318 let _ = asm.encode_one("nop").unwrap();
2319 let result = asm.finish().unwrap();
2322 assert!(result.is_empty());
2323 }
2324
2325 #[test]
2328 fn define_preprocessor_symbol_ifdef() {
2329 let mut asm = Assembler::new(Arch::X86_64);
2330 asm.define_preprocessor_symbol("DEBUG", 1);
2331 asm.emit(".ifdef DEBUG\nnop\n.endif").unwrap();
2332 let result = asm.finish().unwrap();
2333 assert_eq!(result.bytes(), &[0x90]);
2334 }
2335
2336 #[test]
2337 fn define_preprocessor_symbol_skipped_when_missing() {
2338 let mut asm = Assembler::new(Arch::X86_64);
2339 asm.emit(".ifdef DEBUG\nnop\n.endif\nret").unwrap();
2341 let result = asm.finish().unwrap();
2342 assert_eq!(result.bytes(), &[0xC3]); }
2344
2345 #[test]
2348 fn builder_dq() {
2349 let mut asm = Assembler::new(Arch::X86_64);
2350 asm.dq(0xDEAD_BEEF_CAFE_BABE).unwrap();
2351 let result = asm.finish().unwrap();
2352 assert_eq!(result.bytes(), &0xDEAD_BEEF_CAFE_BABEu64.to_le_bytes());
2353 }
2354
2355 #[test]
2358 fn reset_clears_state_keeps_config() {
2359 let mut asm = Assembler::new(Arch::X86_64);
2360 asm.emit("nop").unwrap();
2361 asm.reset();
2362 asm.emit("ret").unwrap();
2363 let result = asm.finish().unwrap();
2364 assert_eq!(result.bytes(), &[0xC3]);
2366 }
2367
2368 #[test]
2369 fn reset_allows_reuse() {
2370 let mut asm = Assembler::new(Arch::X86_64);
2371 asm.emit("nop").unwrap();
2372 asm.reset();
2374 asm.emit("ret").unwrap();
2375 let result = asm.finish().unwrap();
2376 assert_eq!(result.bytes(), &[0xC3]);
2377 }
2378
2379 #[test]
2382 fn current_fragment_count_tracks_emissions() {
2383 let mut asm = Assembler::new(Arch::X86_64);
2384 assert_eq!(asm.current_fragment_count(), 0);
2385 asm.emit("nop").unwrap();
2386 assert!(asm.current_fragment_count() > 0);
2387 }
2388
2389 #[test]
2392 fn empty_assembly_result() {
2393 let asm = Assembler::new(Arch::X86_64);
2394 let result = asm.finish().unwrap();
2395 assert!(result.is_empty());
2396 assert_eq!(result.len(), 0);
2397 assert!(result.bytes().is_empty());
2398 }
2399
2400 #[test]
2403 fn labels_slice_access() {
2404 let mut asm = Assembler::new(Arch::X86_64);
2405 asm.emit("start:\nnop\nend:\nret").unwrap();
2406 let result = asm.finish().unwrap();
2407 let labels = result.labels();
2408 assert_eq!(labels.len(), 2);
2410 assert!(labels.iter().any(|(name, _)| name == "start"));
2412 assert!(labels.iter().any(|(name, _)| name == "end"));
2413 }
2414
2415 #[test]
2418 fn assemble_with_external_labels() {
2419 use crate::assemble_with;
2420 let bytes =
2421 assemble_with("call target", Arch::X86_64, 0x1000, &[("target", 0x2000)]).unwrap();
2422 assert_eq!(bytes[0], 0xE8);
2425 let rel = i32::from_le_bytes(bytes[1..5].try_into().unwrap());
2426 assert_eq!(rel, 0x0FFB);
2427 }
2428
2429 #[test]
2432 fn multiple_errors_collected() {
2433 let mut asm = Assembler::new(Arch::X86_64);
2434 asm.emit("badmnem1\nbadmnem2").unwrap();
2436 let err = asm.finish().unwrap_err();
2437 match err {
2438 AsmError::Multiple { errors } => assert_eq!(errors.len(), 2),
2439 _ => panic!("expected Multiple error, got: {err}"),
2440 }
2441 }
2442
2443 #[cfg(feature = "arm")]
2446 #[test]
2447 fn optimizer_noop_for_arm() {
2448 let mut asm = Assembler::new(Arch::Arm);
2449 asm.emit("mov r0, 0").unwrap();
2451 let result = asm.finish().unwrap();
2452 assert_eq!(result.len(), 4);
2454 assert_eq!(result.bytes(), &[0x00, 0x00, 0xA0, 0xE3]);
2455 }
2456
2457 #[test]
2460 fn org_directive_via_emit() {
2461 let mut asm = Assembler::new(Arch::X86_64);
2462 asm.emit("nop\n.org 0x10\nnop").unwrap();
2463 let result = asm.finish().unwrap();
2464 assert_eq!(result.len(), 17);
2466 assert_eq!(result.bytes()[0], 0x90); assert_eq!(result.bytes()[0x10], 0x90); for &b in &result.bytes()[1..0x10] {
2470 assert_eq!(b, 0x00);
2471 }
2472 }
2473
2474 #[test]
2477 fn listing_includes_label_and_hex() {
2478 let mut asm = Assembler::new(Arch::X86_64);
2479 asm.emit("start:\nnop\nret").unwrap();
2480 let result = asm.finish().unwrap();
2481 let listing = result.listing();
2482 assert!(
2484 listing.contains("start"),
2485 "listing should contain label 'start'"
2486 );
2487 assert!(
2489 listing.contains("90"),
2490 "listing should contain '90' for nop"
2491 );
2492 assert!(
2493 listing.contains("C3") || listing.contains("c3"),
2494 "listing should contain 'C3' for ret"
2495 );
2496 }
2497
2498 #[test]
2499 fn listing_with_base_address_format() {
2500 let mut asm = Assembler::new(Arch::X86_64);
2501 asm.base_address(0x401000);
2502 asm.emit("nop\nret").unwrap();
2503 let result = asm.finish().unwrap();
2504 let listing = result.listing();
2505 assert!(
2507 listing.contains("00401000") || listing.contains("401000"),
2508 "listing should contain base address"
2509 );
2510 }
2511
2512 #[test]
2515 fn org_builder_method() {
2516 let mut asm = Assembler::new(Arch::X86_64);
2517 asm.emit("nop").unwrap();
2518 asm.org(0x10);
2519 asm.emit("nop").unwrap();
2520 let result = asm.finish().unwrap();
2521 assert_eq!(result.len(), 17); }
2523
2524 #[test]
2527 fn jecxz_relaxes_to_long_form() {
2528 let mut asm = Assembler::new(Arch::X86_64);
2531 asm.emit("jecxz target").unwrap();
2532 asm.space(200).unwrap(); asm.emit("target:\nnop").unwrap();
2534 let result = asm.finish().unwrap();
2535 assert_eq!(result.bytes[0], 0x67);
2537 assert_eq!(result.bytes[1], 0xE3);
2538 assert_eq!(result.bytes[2], 0x02);
2539 assert_eq!(result.bytes[3], 0xEB);
2540 assert_eq!(result.bytes[4], 0x05);
2541 assert_eq!(result.bytes[5], 0xE9);
2542 assert_eq!(result.bytes[6], 0xC8);
2545 assert_eq!(result.bytes[7], 0x00);
2546 assert_eq!(result.bytes[8], 0x00);
2547 assert_eq!(result.bytes[9], 0x00);
2548 }
2549
2550 #[test]
2551 fn jecxz_relaxes_to_short_form_when_near() {
2552 let mut asm = Assembler::new(Arch::X86_64);
2554 asm.emit("jecxz target").unwrap();
2555 asm.emit("target:\nnop").unwrap();
2556 let result = asm.finish().unwrap();
2557 assert_eq!(result.bytes[0], 0x67);
2559 assert_eq!(result.bytes[1], 0xE3);
2560 assert_eq!(result.bytes[2], 0x00);
2562 assert_eq!(result.bytes[3], 0x90); }
2564
2565 #[test]
2568 fn single_error_not_wrapped_in_multiple() {
2569 let mut asm = Assembler::new(Arch::X86_64);
2570 asm.emit("badmnem").unwrap();
2571 let err = asm.finish().unwrap_err();
2572 assert!(matches!(err, AsmError::UnknownMnemonic { .. }));
2574 }
2575
2576 #[test]
2579 fn errors_collected_with_valid_instructions() {
2580 let mut asm = Assembler::new(Arch::X86_64);
2581 asm.emit("nop\nbadmnem\nret").unwrap();
2583 let err = asm.finish().unwrap_err();
2584 assert!(matches!(err, AsmError::UnknownMnemonic { .. }));
2586 }
2587
2588 #[test]
2591 fn errors_collected_across_emit_calls() {
2592 let mut asm = Assembler::new(Arch::X86_64);
2593 asm.emit("bad1").unwrap();
2594 asm.emit("bad2").unwrap();
2595 asm.emit("bad3").unwrap();
2596 let err = asm.finish().unwrap_err();
2597 match err {
2598 AsmError::Multiple { errors } => assert_eq!(errors.len(), 3),
2599 _ => panic!("expected Multiple error with 3 errors, got: {err}"),
2600 }
2601 }
2602
2603 #[test]
2606 fn reset_clears_errors() {
2607 let mut asm = Assembler::new(Arch::X86_64);
2608 asm.emit("badmnem").unwrap();
2609 asm.reset();
2610 asm.emit("nop").unwrap();
2611 let result = asm.finish().unwrap();
2612 assert_eq!(result.bytes(), &[0x90]);
2613 }
2614
2615 #[test]
2618 fn max_errors_limit_enforced() {
2619 let mut asm = Assembler::new(Arch::X86_64);
2620 asm.limits(ResourceLimits {
2621 max_errors: 2,
2622 ..ResourceLimits::default()
2623 });
2624 let result = asm.emit("bad1\nbad2\nbad3");
2626 assert!(result.is_err());
2627 let err = result.unwrap_err();
2628 assert!(matches!(err, AsmError::ResourceLimitExceeded { .. }));
2629 }
2630
2631 #[test]
2634 fn literal_pool_basic_x_reg() {
2635 let mut asm = Assembler::new(Arch::Aarch64);
2637 asm.emit("ldr x0, =0x12345678").unwrap();
2638 let result = asm.finish().unwrap();
2639 let bytes = result.bytes();
2640 assert!(
2642 bytes.len() >= 8,
2643 "expected at least 8 bytes, got {}",
2644 bytes.len()
2645 );
2646 let pool_start = bytes.len() - 8;
2648 let pool_val = u64::from_le_bytes(bytes[pool_start..pool_start + 8].try_into().unwrap());
2649 assert_eq!(pool_val, 0x12345678, "pool should contain the constant");
2650 }
2651
2652 #[test]
2653 fn literal_pool_basic_w_reg() {
2654 let mut asm = Assembler::new(Arch::Aarch64);
2656 asm.emit("ldr w0, =0x42").unwrap();
2657 let result = asm.finish().unwrap();
2658 let bytes = result.bytes();
2659 let pool_start = bytes.len() - 4;
2661 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2662 assert_eq!(pool_val, 0x42, "pool should contain the constant");
2663 }
2664
2665 #[test]
2666 fn literal_pool_with_ltorg() {
2667 let mut asm = Assembler::new(Arch::Aarch64);
2669 asm.emit("ldr x0, =0xCAFE\n.ltorg").unwrap();
2670 let result = asm.finish().unwrap();
2671 let bytes = result.bytes();
2672 assert!(bytes.len() >= 12);
2674 let pool_start = bytes.len() - 8;
2676 let pool_val = u64::from_le_bytes(bytes[pool_start..pool_start + 8].try_into().unwrap());
2677 assert_eq!(pool_val, 0xCAFE);
2678 }
2679
2680 #[test]
2681 fn literal_pool_deduplication() {
2682 let mut asm = Assembler::new(Arch::Aarch64);
2684 asm.emit("ldr x0, =0x1234\nldr x1, =0x1234").unwrap();
2685 let result = asm.finish().unwrap();
2686 let bytes = result.bytes();
2687 assert!(
2692 bytes.len() <= 24,
2693 "expected <= 24 bytes with dedup, got {}",
2694 bytes.len()
2695 );
2696 }
2697
2698 #[test]
2699 fn literal_pool_multiple_values() {
2700 let mut asm = Assembler::new(Arch::Aarch64);
2702 asm.emit("ldr x0, =0xAAAA\nldr x1, =0xBBBB").unwrap();
2703 let result = asm.finish().unwrap();
2704 let bytes = result.bytes();
2705 let pool_end = bytes.len();
2707 let val2 = u64::from_le_bytes(bytes[pool_end - 8..pool_end].try_into().unwrap());
2708 let val1 = u64::from_le_bytes(bytes[pool_end - 16..pool_end - 8].try_into().unwrap());
2709 assert!(
2710 (val1 == 0xAAAA && val2 == 0xBBBB) || (val1 == 0xBBBB && val2 == 0xAAAA),
2711 "pool should contain both values, got {:#x} and {:#x}",
2712 val1,
2713 val2
2714 );
2715 }
2716
2717 #[test]
2718 fn literal_pool_ldr_encodes_pc_relative() {
2719 let mut asm = Assembler::new(Arch::Aarch64);
2721 asm.emit("ldr x0, =0xFF").unwrap();
2722 let result = asm.finish().unwrap();
2723 let bytes = result.bytes();
2724 let word = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
2726 assert_eq!((word >> 30) & 0b11, 0b01, "opc should be 01 for 64-bit LDR");
2728 assert_eq!(
2730 (word >> 24) & 0b111111,
2731 0b011000,
2732 "should be LDR literal encoding"
2733 );
2734 assert_eq!(word & 0x1F, 0, "Rt should be X0");
2736 let imm19 = ((word >> 5) & 0x7FFFF) as i32;
2738 assert!(imm19 > 0, "imm19 should be positive (pool is after instr)");
2739 }
2740
2741 #[test]
2742 fn literal_pool_large_64bit_value() {
2743 let mut asm = Assembler::new(Arch::Aarch64);
2744 asm.emit("ldr x0, =0xDEADBEEFCAFEBABE").unwrap();
2745 let result = asm.finish().unwrap();
2746 let bytes = result.bytes();
2747 let pool_start = bytes.len() - 8;
2748 let pool_val = u64::from_le_bytes(bytes[pool_start..pool_start + 8].try_into().unwrap());
2749 assert_eq!(pool_val, 0xDEADBEEFCAFEBABE);
2750 }
2751
2752 #[test]
2753 fn literal_pool_negative_value() {
2754 let mut asm = Assembler::new(Arch::Aarch64);
2755 asm.emit("ldr x0, =-1").unwrap();
2756 let result = asm.finish().unwrap();
2757 let bytes = result.bytes();
2758 let pool_start = bytes.len() - 8;
2759 let pool_val = u64::from_le_bytes(bytes[pool_start..pool_start + 8].try_into().unwrap());
2760 assert_eq!(pool_val, 0xFFFFFFFFFFFFFFFF);
2762 }
2763
2764 #[test]
2765 fn literal_pool_pool_directive() {
2766 let mut asm = Assembler::new(Arch::Aarch64);
2768 asm.emit("ldr x0, =0xBEEF\n.pool").unwrap();
2769 let result = asm.finish().unwrap();
2770 let bytes = result.bytes();
2771 let pool_start = bytes.len() - 8;
2772 let pool_val = u64::from_le_bytes(bytes[pool_start..pool_start + 8].try_into().unwrap());
2773 assert_eq!(pool_val, 0xBEEF);
2774 }
2775
2776 #[test]
2777 fn literal_pool_reset_clears_pool() {
2778 let mut asm = Assembler::new(Arch::Aarch64);
2779 asm.emit("ldr x0, =0x1234").unwrap();
2780 asm.reset();
2781 asm.emit("nop").unwrap();
2783 let result = asm.finish().unwrap();
2784 assert_eq!(result.bytes(), &[0x1F, 0x20, 0x03, 0xD5]); }
2786
2787 #[test]
2790 fn arm_literal_pool_basic() {
2791 let mut asm = Assembler::new(Arch::Arm);
2793 asm.emit("ldr r0, =0x12345678").unwrap();
2794 let result = asm.finish().unwrap();
2795 let bytes = result.bytes();
2796 assert!(
2798 bytes.len() >= 8,
2799 "expected at least 8 bytes, got {}",
2800 bytes.len()
2801 );
2802 let pool_start = bytes.len() - 4;
2804 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2805 assert_eq!(pool_val, 0x12345678, "pool should contain the constant");
2806 }
2807
2808 #[test]
2809 fn arm_literal_pool_small_value() {
2810 let mut asm = Assembler::new(Arch::Arm);
2812 asm.emit("ldr r3, =42").unwrap();
2813 let result = asm.finish().unwrap();
2814 let bytes = result.bytes();
2815 let pool_start = bytes.len() - 4;
2816 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2817 assert_eq!(pool_val, 42);
2818 }
2819
2820 #[test]
2821 fn arm_literal_pool_negative_value() {
2822 let mut asm = Assembler::new(Arch::Arm);
2823 asm.emit("ldr r0, =-1").unwrap();
2824 let result = asm.finish().unwrap();
2825 let bytes = result.bytes();
2826 let pool_start = bytes.len() - 4;
2827 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2828 assert_eq!(pool_val, 0xFFFFFFFF);
2830 }
2831
2832 #[test]
2833 fn arm_literal_pool_deduplication() {
2834 let mut asm = Assembler::new(Arch::Arm);
2836 asm.emit("ldr r0, =0xAABB\nldr r1, =0xAABB").unwrap();
2837 let result = asm.finish().unwrap();
2838 let bytes = result.bytes();
2839 assert!(
2842 bytes.len() <= 16,
2843 "expected <=16 bytes with dedup, got {}",
2844 bytes.len()
2845 );
2846 }
2847
2848 #[test]
2849 fn arm_literal_pool_multiple_values() {
2850 let mut asm = Assembler::new(Arch::Arm);
2852 asm.emit("ldr r0, =0x1111\nldr r1, =0x2222").unwrap();
2853 let result = asm.finish().unwrap();
2854 let bytes = result.bytes();
2855 let pool_end = bytes.len();
2857 let val2 = u32::from_le_bytes(bytes[pool_end - 4..pool_end].try_into().unwrap());
2858 let val1 = u32::from_le_bytes(bytes[pool_end - 8..pool_end - 4].try_into().unwrap());
2859 assert!(
2860 (val1 == 0x1111 && val2 == 0x2222) || (val1 == 0x2222 && val2 == 0x1111),
2861 "pool should contain both values, got {:#x} and {:#x}",
2862 val1,
2863 val2
2864 );
2865 }
2866
2867 #[test]
2868 fn arm_literal_pool_with_ltorg() {
2869 let mut asm = Assembler::new(Arch::Arm);
2871 asm.emit("ldr r0, =0xCAFE\n.ltorg").unwrap();
2872 let result = asm.finish().unwrap();
2873 let bytes = result.bytes();
2874 assert!(bytes.len() >= 8);
2875 let pool_start = bytes.len() - 4;
2876 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2877 assert_eq!(pool_val, 0xCAFE);
2878 }
2879
2880 #[test]
2881 fn arm_literal_pool_pool_directive() {
2882 let mut asm = Assembler::new(Arch::Arm);
2884 asm.emit("ldr r0, =0xBEEF\n.pool").unwrap();
2885 let result = asm.finish().unwrap();
2886 let bytes = result.bytes();
2887 let pool_start = bytes.len() - 4;
2888 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2889 assert_eq!(pool_val, 0xBEEF);
2890 }
2891
2892 #[test]
2893 fn arm_literal_pool_ldr_encodes_pc_relative() {
2894 let mut asm = Assembler::new(Arch::Arm);
2896 asm.emit("ldr r0, =0xFF").unwrap();
2897 let result = asm.finish().unwrap();
2898 let bytes = result.bytes();
2899 let word = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
2901 assert_eq!(
2903 (word >> 26) & 0b11,
2904 0b01,
2905 "should be load/store word encoding"
2906 );
2907 assert_eq!((word >> 16) & 0xF, 15, "Rn should be PC (R15)");
2909 assert_eq!((word >> 12) & 0xF, 0, "Rd should be R0");
2911 assert_eq!((word >> 20) & 1, 1, "should be a load");
2913 }
2914
2915 #[test]
2916 fn arm_literal_pool_entry_always_4_bytes() {
2917 let mut asm = Assembler::new(Arch::Arm);
2919 asm.emit("ldr r0, =0x1\nldr r15, =0x2").unwrap();
2920 let result = asm.finish().unwrap();
2921 let bytes = result.bytes();
2922 assert!(
2925 bytes.len() <= 16,
2926 "ARM pool entries should be 4 bytes each, got {} total",
2927 bytes.len()
2928 );
2929 }
2930
2931 #[test]
2932 fn arm_literal_pool_hex_large() {
2933 let mut asm = Assembler::new(Arch::Arm);
2934 asm.emit("ldr r5, =0xDEADBEEF").unwrap();
2935 let result = asm.finish().unwrap();
2936 let bytes = result.bytes();
2937 let pool_start = bytes.len() - 4;
2938 let pool_val = u32::from_le_bytes(bytes[pool_start..pool_start + 4].try_into().unwrap());
2939 assert_eq!(pool_val, 0xDEADBEEF);
2940 }
2941}