1use std::collections::{BTreeMap, HashMap};
32
33use rucc_mir::CfiOp;
34use rucc_object::{
35 Array, Assembled, Binding, Extent, Held, Name, Part, Reference, Reloc, Shape, Sort, Visibility,
36};
37use rucc_target::aarch64::{self, AAPCS64};
38use rucc_target::x86_64::{SYSV, gpr_named, nops};
39use rucc_target::{CallRegs, ObjectFormat};
40use rucc_tuple::Arch;
41
42use crate::instruction::Sort as Reach;
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct Trouble {
49 pub line: usize,
52 pub why: String,
54}
55
56impl std::fmt::Display for Trouble {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(f, "{}: {}", self.line, self.why)
59 }
60}
61
62impl std::error::Error for Trouble {}
63
64pub fn read(text: &str, arch: Arch) -> Result<Assembled, Trouble> {
78 let mut long = std::collections::HashSet::new();
83 loop {
84 let aarch64 = arch == Arch::Aarch64;
85 let mut reader = Reader { long: long.clone(), aarch64, ..Reader::default() };
86 reader.run(text)?;
87 match reader.finish()? {
88 Ok(done) => return Ok(done),
89 Err(grow) => long.extend(grow),
90 }
91 }
92}
93
94#[derive(Debug, Clone)]
100struct Sym {
101 name: String,
102 at: Held,
103 size: u64,
104 sort: Sort,
105 binding: Binding,
106 visibility: Visibility,
107 numbered: bool,
110}
111
112#[derive(Debug, Clone)]
114struct Fixup {
115 part: usize,
116 at: u64,
117 width: u8,
118 sum: Sum,
119 reach: Reach,
123 slot: Reference,
126 branch: Option<usize>,
129 jump: bool,
132 field: Option<aarch64::Fixup>,
135 line: usize,
136}
137
138#[derive(Debug, Clone, Copy)]
140struct Aligned {
141 part: usize,
142 at: u64,
144 boundary: u64,
145 most: Option<u64>,
147 need: u64,
149}
150
151#[derive(Debug)]
153struct Frame {
154 part: usize,
155 start: u64,
156 len: u64,
157 sym: usize,
160 rows: crate::unwind::Rows,
161 cfa: i32,
164 remembered: Vec<i32>,
166}
167
168#[derive(Debug, Default)]
170struct Reader {
171 parts: Vec<Part>,
172 named: HashMap<String, usize>,
174 here: usize,
176 stack: Vec<usize>,
178 before: Option<usize>,
180 syms: Vec<Sym>,
181 known: HashMap<String, usize>,
182 counts: HashMap<String, usize>,
185 labelled: std::collections::HashSet<usize>,
188 fixups: Vec<Fixup>,
189 sets: Vec<(usize, Sum, usize)>,
191 sizes: Vec<(usize, Sum, usize)>,
193 current: HashMap<String, String>,
197 relocated: std::collections::HashSet<usize>,
201 said_local: std::collections::HashSet<usize>,
205 frame: Option<Frame>,
207 frames: Vec<Frame>,
209 no_unwind: bool,
212 files: Vec<String>,
216 long: std::collections::HashSet<usize>,
218 branches: usize,
220 aligns: Vec<Aligned>,
222 aarch64: bool,
224 line: usize,
225}
226
227impl Reader {
228 fn run(&mut self, text: &str) -> Result<(), Trouble> {
230 self.section(".text", Shape::of(".text"));
233 let mut commenting = false;
234 for (index, raw) in text.lines().enumerate() {
235 self.line = index + 1;
236 let line = self.strip(raw, &mut commenting)?;
237 for statement in split(&line, ';') {
238 self.statement(statement.trim())?;
239 }
240 }
241 if commenting {
242 return Err(self.bad("a block comment was opened and never closed"));
243 }
244 Ok(())
245 }
246
247 fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
254 let mut out = String::with_capacity(raw.len());
255 let bytes = raw.as_bytes();
256 let mut i = 0;
257 let mut quote = None;
258 while i < bytes.len() {
259 let rest = &raw[i..];
260 if *commenting {
261 if let Some(end) = rest.find("*/") {
262 *commenting = false;
263 out.push(' ');
266 i += end + 2;
267 } else {
268 return Ok(out);
269 }
270 continue;
271 }
272 let ch = bytes[i] as char;
273 if let Some(mark) = quote {
274 out.push(ch);
275 if ch == '\\' && i + 1 < bytes.len() {
276 out.push(bytes[i + 1] as char);
277 i += 2;
278 continue;
279 }
280 if ch == mark {
281 quote = None;
282 }
283 i += 1;
284 continue;
285 }
286 if ch == '"' {
287 quote = Some('"');
288 out.push(ch);
289 i += 1;
290 continue;
291 }
292 if rest.starts_with("/*") {
293 *commenting = true;
294 i += 2;
295 continue;
296 }
297 let marker = ch == '#' && (!self.aarch64 || out.trim().is_empty());
300 if rest.starts_with("//") || marker {
301 return Ok(out);
302 }
303 out.push(ch);
304 i += 1;
305 }
306 if quote.is_some() {
307 return Err(self.bad("a string was opened and the line ended before it closed"));
308 }
309 Ok(out)
310 }
311
312 fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
314 loop {
315 text = text.trim_start();
316 let Some(name) = labelled(text) else { break };
317 self.label(&name)?;
318 text = &text[name.len() + 1..];
319 }
320 let text = text.trim();
321 if text.is_empty() {
322 return Ok(());
323 }
324 let (word, rest) = match text.find(char::is_whitespace) {
325 Some(cut) => (&text[..cut], text[cut..].trim()),
326 None => (text, ""),
327 };
328 if let Some((name, what)) = assigned(text) {
329 return self.assign(name, what);
330 }
331 if let Some(directive) = word.strip_prefix('.') {
332 return self.directive(directive, rest);
333 }
334 if self.aarch64 {
335 return self.a64(text);
336 }
337 if let Some((word, rest)) = repeated(word, rest) {
338 return self.instruction(&word, rest);
339 }
340 self.instruction(word, rest)
341 }
342
343 fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
357 let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
358 let mut written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
359 let mut branch = None;
361 let short = crate::instruction::short(&written).filter(|_| written.holes[0].name != ".");
362 let jump = short.is_some();
363 if let Some(short) = short {
364 if !self.long.contains(&self.branches) {
365 branch = Some(self.branches);
366 written = short;
367 }
368 self.branches += 1;
369 }
370 let part = self.here;
371 let at = self.at();
372 self.put(&written.bytes)?;
373 let end = at + written.bytes.len() as u64;
374 let slot = crate::bytes::slot(&written.bytes);
375 for hole in written.holes {
376 let here = (part, at as i64);
379 let sum = if hole.sort == Reach::Value {
380 self.expression_at(&hole.name, here)?
382 } else {
383 let what = if hole.name == "." {
384 What::Here { part, at: here.1 }
385 } else {
386 let name = self.named(&hole.name)?;
389 self.sym(&name);
390 What::Symbol(name)
391 };
392 Sum {
393 constant: hole.addend,
394 terms: vec![
395 Term { coeff: 1, what },
396 Term { coeff: -1, what: What::Here { part, at: end as i64 } },
397 ],
398 }
399 };
400 self.fixups.push(Fixup {
401 part,
402 at: at + hole.at as u64,
403 width: hole.width,
404 sum,
405 reach: hole.sort,
406 slot,
407 branch,
408 jump,
409 field: None,
410 line: self.line,
411 });
412 }
413 Ok(())
414 }
415
416 fn a64(&mut self, text: &str) -> Result<(), Trouble> {
424 let line = aarch64::read(text).map_err(|why| self.bad(&why.to_string()))?;
425 let encoded = aarch64::encode(&line.mnemonic, &line.values)
426 .map_err(|why| self.bad(&why.to_string()))?;
427 let (part, at) = (self.here, self.at());
428 self.put(&encoded.word.to_le_bytes())?;
429 let (Some(field), Some(name)) = (encoded.fixup, line.symbol) else {
430 return Ok(());
431 };
432 let name = self.named(&name)?;
433 self.sym(&name);
434 let mut terms = vec![Term { coeff: 1, what: What::Symbol(name) }];
435 if relative(field) {
436 terms.push(Term { coeff: -1, what: What::Here { part, at: at as i64 } });
437 }
438 let jump = matches!(
439 field,
440 aarch64::Fixup::Jump26 | aarch64::Fixup::CondBr19 | aarch64::Fixup::TestBr14
441 );
442 self.fixups.push(Fixup {
443 part,
444 at,
445 width: 4,
446 sum: Sum { constant: line.addend, terms },
447 reach: Reach::Near,
448 slot: Reference::Data,
449 branch: None,
450 jump,
451 field: Some(field),
452 line: self.line,
453 });
454 Ok(())
455 }
456
457 fn label(&mut self, name: &str) -> Result<(), Trouble> {
459 let at = self.at();
460 let part = self.here;
461 let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
464 let held = if numbered {
465 let count = self.counts.entry(name.to_owned()).or_insert(0);
466 *count += 1;
467 counted(name, *count)
468 } else {
469 name.to_owned()
470 };
471 let sym = self.sym(&held);
472 if self.syms[sym].at != Held::Undefined {
473 let what = format!("'{name}' is defined twice");
474 return Err(self.bad(&what));
475 }
476 self.syms[sym].at = Held::In { part, offset: at };
477 self.labelled.insert(part);
478 Ok(())
479 }
480
481 fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
488 let Some(number) = word.strip_suffix(['b', 'f']) else {
489 return Ok(None);
490 };
491 if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
492 return Ok(None);
493 }
494 let count = self.counts.get(number).copied().unwrap_or(0);
495 if word.ends_with('b') {
496 if count == 0 {
497 let what =
498 format!("'{word}' goes back to a '{number}:' and there is none above it");
499 return Err(self.bad(&what));
500 }
501 return Ok(Some(counted(number, count)));
502 }
503 Ok(Some(counted(number, count + 1)))
504 }
505
506 fn named(&self, word: &str) -> Result<String, Trouble> {
511 if let Some(place) = self.numbered(word)? {
512 return Ok(place);
513 }
514 Ok(self.current.get(word).cloned().unwrap_or_else(|| word.to_owned()))
515 }
516
517 fn assign(&mut self, name: &str, what: &str) -> Result<(), Trouble> {
525 let sum = self.expression(what)?;
526 let held = match self.current.get(name) {
527 Some(_) => format!("{name}\u{1}={}", self.syms.len()),
528 None => name.to_owned(),
529 };
530 let sym = self.sym(&held);
531 if self.syms[sym].at != Held::Undefined {
532 let what = format!("'{name}' is defined twice");
533 return Err(self.bad(&what));
534 }
535 self.current.insert(name.to_owned(), held);
536 self.sets.push((sym, sum, self.line));
537 Ok(())
538 }
539
540 fn cfi(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
549 match word {
550 "cfi_startproc" => {
551 if self.frame.is_some() {
552 return Err(self.bad("a '.cfi_startproc' inside another one"));
553 }
554 let sym = self.sym(&format!("\u{1}frame{}", self.frames.len()));
555 let (part, start) = (self.here, self.at());
556 self.syms[sym].at = Held::In { part, offset: start };
557 let frame = Frame {
560 part,
561 start,
562 len: 0,
563 sym,
564 rows: Vec::new(),
565 cfa: if self.aarch64 { 0 } else { 8 },
566 remembered: Vec::new(),
567 };
568 self.frame = Some(frame);
569 return Ok(());
570 }
571 "cfi_sections" => {
572 self.no_unwind = !args.iter().any(|arg| arg.trim() == ".eh_frame");
573 return Ok(());
574 }
575 "cfi_endproc"
576 | "cfi_def_cfa"
577 | "cfi_def_cfa_offset"
578 | "cfi_adjust_cfa_offset"
579 | "cfi_def_cfa_register"
580 | "cfi_offset"
581 | "cfi_rel_offset"
582 | "cfi_restore"
583 | "cfi_remember_state"
584 | "cfi_restore_state" => {}
585 _ => return Ok(()),
586 }
587 let (here, at) = (self.here, self.at());
588 let line = self.line;
589 let bad = |why: &str| Trouble { line, why: why.to_owned() };
590 let Some(mut frame) = self.frame.take() else {
591 return Err(bad("a frame rule outside '.cfi_startproc' and '.cfi_endproc'"));
592 };
593 if frame.part != here {
594 return Err(bad("a frame rule in another section from the function it is about"));
595 }
596 let op = match word {
597 "cfi_endproc" => {
598 frame.len = at - frame.start;
599 self.frames.push(frame);
600 return Ok(());
601 }
602 "cfi_def_cfa" => {
603 let [reg, offset] = self.two(args, ".cfi_def_cfa")?;
604 frame.cfa = self.distance(&offset)?;
605 CfiOp::DefCfa { reg: self.dwarf(®)?, offset: frame.cfa }
606 }
607 "cfi_def_cfa_offset" | "cfi_adjust_cfa_offset" => {
608 let by = self.distance(args.first().map_or("", |arg| arg.as_str()))?;
609 frame.cfa = if word == "cfi_def_cfa_offset" { by } else { frame.cfa + by };
610 CfiOp::DefCfaOffset(frame.cfa)
611 }
612 "cfi_def_cfa_register" => {
613 CfiOp::DefCfaRegister(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
614 }
615 "cfi_offset" | "cfi_rel_offset" => {
616 let [reg, offset] = self.two(args, &format!(".{word}"))?;
617 let mut offset = self.distance(&offset)?;
618 if word == "cfi_rel_offset" {
621 offset -= frame.cfa;
622 }
623 if offset >= 0 || offset % 8 != 0 {
624 return Err(bad(
625 "a register saved somewhere that is not a whole slot below the end of the \
626 frame, which is the only place this writes a rule for",
627 ));
628 }
629 CfiOp::Offset { reg: self.dwarf(®)?, offset }
630 }
631 "cfi_restore" => {
632 CfiOp::Restore(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
633 }
634 "cfi_remember_state" => {
635 frame.remembered.push(frame.cfa);
636 CfiOp::RememberState
637 }
638 "cfi_restore_state" => {
639 frame.cfa = frame.remembered.pop().ok_or_else(|| {
640 bad("a '.cfi_restore_state' with nothing remembered to restore")
641 })?;
642 CfiOp::RestoreState
643 }
644 _ => unreachable!("every other word returned above"),
645 };
646 frame.rows.push(((at - frame.start) as usize, op));
647 self.frame = Some(frame);
648 Ok(())
649 }
650
651 fn distance(&mut self, text: &str) -> Result<i32, Trouble> {
653 let value = self.number(text)?;
654 i32::try_from(value).map_err(|_| self.bad(&format!("{value} is not a distance in a frame")))
655 }
656
657 fn dwarf(&self, text: &str) -> Result<u16, Trouble> {
659 let text = text.trim();
660 if let Ok(number) = text.parse::<u16>() {
661 return Ok(number);
662 }
663 if self.aarch64 {
664 return aarch64_dwarf(text).ok_or_else(|| {
665 self.bad(&format!("'{text}' is not a register a frame rule can name"))
666 });
667 }
668 let name = text.strip_prefix('%').unwrap_or(text);
669 if name == "rip" {
670 return Ok(SYSV.dwarf_return_address);
671 }
672 gpr_named(name)
673 .and_then(|(reg, _)| SYSV.dwarf(SYSV.int_class, reg))
674 .ok_or_else(|| self.bad(&format!("'{text}' is not a register a frame rule can name")))
675 }
676
677 #[allow(clippy::too_many_lines)]
679 fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
680 let args = split(rest, ',');
681 match word {
682 "text" | "data" | "bss" | "rodata" => {
683 self.plain(word, rest)?;
684 }
685 "section" => self.section_directive(&args)?,
686 "pushsection" => {
687 self.stack.push(self.here);
688 self.section_directive(&args)?;
689 }
690 "popsection" => {
691 let Some(back) = self.stack.pop() else {
692 return Err(self.bad(".popsection with nothing pushed"));
693 };
694 self.go(back);
695 }
696 "previous" => {
697 let Some(back) = self.before else {
698 return Err(self.bad(".previous with no section before this one"));
699 };
700 self.go(back);
701 }
702
703 "byte" => self.data(&args, 1)?,
704 "word" if self.aarch64 => self.data(&args, 4)?,
706 "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
707 "long" | "int" | "4byte" => self.data(&args, 4)?,
708 "quad" | "8byte" | "xword" | "dword" => self.data(&args, 8)?,
709
710 "ascii" => self.text_bytes(&args, false)?,
711 "asciz" | "string" => self.text_bytes(&args, true)?,
712
713 "space" | "skip" | "zero" => {
714 if args.is_empty() || args.len() > 2 {
715 return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
716 }
717 let size = self.number(&args[0])?;
718 let size = self.count(size)?;
719 let fill = match args.get(1) {
720 Some(arg) => self.byte(arg)?,
721 None => 0,
722 };
723 self.pad(size, fill)?;
724 }
725 "fill" => {
726 if args.is_empty() || args.len() > 3 {
730 return Err(self.bad(".fill wants a count and an optional width and value"));
731 }
732 let count = self.number(&args[0])?;
733 let count = self.count(count)?;
734 let width = match args.get(1) {
735 Some(arg) => {
736 let width = self.number(arg)?;
737 self.count(width)?
738 }
739 None => 1,
740 };
741 let value = match args.get(2) {
742 Some(arg) => self.number(arg)?,
743 None => 0,
744 };
745 if width > 8 {
746 return Err(self.bad(".fill of items wider than eight bytes is not written"));
747 }
748 let one = value.to_le_bytes();
749 for _ in 0..count {
750 self.put(&one[..width as usize])?;
751 }
752 }
753
754 "align" | "balign" | "p2align" => self.align(word, &args)?,
755 "org" => {
756 let Some(first) = args.first() else {
757 return Err(self.bad(".org with nothing after it"));
758 };
759 let to = self.number(first)?;
760 let to = self.count(to)?;
761 let fill = match args.get(1) {
762 Some(arg) => self.byte(arg)?,
763 None => 0,
764 };
765 let at = self.at();
766 if to < at {
767 let what = format!(".org back to {to} from {at}, which would overwrite bytes");
768 return Err(self.bad(&what));
769 }
770 self.pad(to - at, fill)?;
771 }
772
773 "globl" | "global" => self.bind(&args, Binding::Global)?,
774 "weak" => self.bind(&args, Binding::Weak)?,
775 "local" => {
776 self.bind(&args, Binding::Local)?;
777 for arg in &args {
778 let sym = self.sym(arg.trim());
779 self.said_local.insert(sym);
780 }
781 }
782 "hidden" => self.sight(&args, Visibility::Hidden)?,
783 "protected" => self.sight(&args, Visibility::Protected)?,
784 "internal" => self.sight(&args, Visibility::Hidden)?,
787
788 "type" => self.type_directive(&args)?,
789 "err" | "error" => {
790 let what = unquoted(args.first().map_or("", |arg| arg.trim()));
791 return Err(self.bad(&format!("the file says so itself: {what}")));
792 }
793 "size" => {
794 let [name, what] = self.two(&args, ".size")?;
795 let sum = self.expression(&what)?;
796 let sym = self.sym(&name);
797 self.sizes.push((sym, sum, self.line));
798 }
799 "set" | "equ" | "equiv" => {
800 let [name, what] = self.two(&args, &format!(".{word}"))?;
801 self.assign(&name, &what)?;
802 }
803 "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
804
805 "file" => {
809 let what = args.first().map_or("", |arg| arg.trim());
810 if what.starts_with('"') {
811 self.files.push(unquoted(what));
812 }
813 }
814
815 "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
819 | "intel_syntax" | "warning" => {}
820 _ if word.starts_with("cfi_") => self.cfi(word, &args)?,
821
822 _ => {
823 let what = format!(
824 "'.{word}' is a directive this compiler does not know, so nothing was written \
825 for it"
826 );
827 return Err(self.bad(&what));
828 }
829 }
830 Ok(())
831 }
832
833 fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
835 if !rest.trim().is_empty() && rest.trim() != "0" {
840 let what =
841 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
842 return Err(self.bad(&what));
843 }
844 let name = format!(".{word}");
845 let shape = Shape::of(&name);
846 self.section(&name, shape);
847 Ok(())
848 }
849
850 fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
852 let Some(name) = args.first() else {
853 return Err(self.bad(".section with no name"));
854 };
855 let name = unquoted(name.trim());
856 if name.is_empty() {
857 return Err(self.bad(".section with no name"));
858 }
859 let mut shape = Shape::of(&name);
862 let (mut merge, mut strings) = (false, false);
863 if let Some(flags) = args.get(1) {
864 let letters = unquoted(flags.trim());
865 shape = Shape { bits: true, ..Shape::default() };
866 for letter in letters.chars() {
867 match letter {
868 'a' => shape.alloc = true,
869 'w' => shape.write = true,
870 'x' => shape.exec = true,
871 'T' => shape.thread = true,
872 'M' => merge = true,
873 'S' => strings = true,
874 'G' | 'o' | 'e' | 'R' | 'd' => {}
878 _ => {
879 let what = format!("'{letter}' is not a section flag this compiler knows");
880 return Err(self.bad(&what));
881 }
882 }
883 }
884 }
885 if let Some(kind) = args.get(2) {
886 let kind = kind.trim().trim_start_matches(['@', '%']);
887 let kind = unquoted(kind);
888 match kind.as_str() {
889 "progbits" => shape.bits = true,
890 "nobits" => shape.bits = false,
891 "init_array" => shape.array = Some(Array::Init),
892 "fini_array" => shape.array = Some(Array::Fini),
893 "preinit_array" => shape.array = Some(Array::Preinit),
894 "note" => shape.bits = true,
895 _ => {
896 let what = format!("'{kind}' is not a section type this compiler writes");
897 return Err(self.bad(&what));
898 }
899 }
900 }
901 if merge {
904 shape.merge = args.get(3).and_then(|entry| entry.trim().parse().ok()).unwrap_or(0);
905 shape.strings = strings;
906 }
907 self.section(&name, shape);
908 Ok(())
909 }
910
911 fn section(&mut self, name: &str, shape: Shape) {
917 if let Some(&at) = self.named.get(name) {
918 self.go(at);
919 return;
920 }
921 let at = self.parts.len();
922 self.parts.push(Part {
923 name: name.to_owned(),
924 bytes: Vec::new(),
925 size: 0,
926 align: 1,
927 shape,
928 relocs: Vec::new(),
929 });
930 self.named.insert(name.to_owned(), at);
931 self.go(at);
932 }
933
934 fn go(&mut self, at: usize) {
936 if at != self.here {
937 self.before = Some(self.here);
938 self.here = at;
939 }
940 }
941
942 fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
944 if args.is_empty() {
945 return Err(self.bad("a data directive with nothing after it"));
946 }
947 for arg in args {
948 let sum = self.expression(arg)?;
949 let at = self.at();
950 if let Some(value) = sum.flat() {
951 self.put(&value.to_le_bytes()[..width as usize])?;
952 continue;
953 }
954 let part = self.here;
957 if !self.parts[part].shape.bits {
958 let what = format!(
959 "'{}' holds no bytes and this asks the linker to write some into it",
960 self.parts[part].name
961 );
962 return Err(self.bad(&what));
963 }
964 self.put(&vec![0u8; width as usize])?;
965 self.fixups.push(Fixup {
966 part,
967 at,
968 width,
969 sum,
970 reach: Reach::Near,
971 slot: Reference::Got,
972 branch: None,
973 jump: false,
974 field: None,
975 line: self.line,
976 });
977 }
978 Ok(())
979 }
980
981 fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
983 for arg in args {
984 let mut bytes = self.string(arg.trim())?;
985 if terminated {
986 bytes.push(0);
987 }
988 self.put(&bytes)?;
989 }
990 Ok(())
991 }
992
993 fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
999 let Some(head) = args.first() else {
1000 return Err(self.bad(&format!(".{word} with nothing after it")));
1001 };
1002 let first = self.number(head)?;
1003 let first = self.count(first)?;
1004 let boundary = if word == "p2align" {
1005 if first > 31 {
1006 return Err(self.bad(".p2align of more than two gigabytes"));
1007 }
1008 1u64 << first
1009 } else {
1010 first
1011 };
1012 if boundary == 0 || !boundary.is_power_of_two() {
1013 let what = format!("an alignment of {boundary}, which is not a power of two");
1014 return Err(self.bad(&what));
1015 }
1016 let exec = self.parts[self.here].shape.exec;
1020 let fill = match args.get(1) {
1021 Some(arg) if !arg.trim().is_empty() => Some(self.byte(arg)?),
1022 _ => None,
1023 };
1024 let at = self.at();
1025 let most = match args.get(2).filter(|arg| !arg.trim().is_empty()) {
1028 Some(most) => {
1029 let most = self.number(&most.clone())?;
1030 Some(self.count(most)?)
1031 }
1032 None => None,
1033 };
1034 let need = padding(at, boundary, most);
1035 self.aligns.push(Aligned { part: self.here, at, boundary, most, need });
1036 if need == 0 && most.is_some_and(|most| padding(at, boundary, None) > most) {
1037 return Ok(());
1038 }
1039 let part = &mut self.parts[self.here];
1040 part.align = part.align.max(boundary);
1041 match fill {
1042 Some(fill) => self.pad(need, fill),
1043 None if exec && self.aarch64 => {
1048 let mut bytes = vec![0u8; (need % 4) as usize];
1049 for _ in 0..need / 4 {
1050 bytes.extend_from_slice(&A64_NOP.to_le_bytes());
1051 }
1052 self.put(&bytes)
1053 }
1054 None if exec => {
1055 let mut bytes = Vec::new();
1056 nops(usize::try_from(need).unwrap_or(usize::MAX), &mut bytes);
1057 self.put(&bytes)
1058 }
1059 None => self.pad(need, 0),
1060 }
1061 }
1062
1063 fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
1065 for arg in args {
1066 let sym = self.sym(arg.trim());
1067 self.syms[sym].binding = binding;
1068 }
1069 Ok(())
1070 }
1071
1072 fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
1074 for arg in args {
1075 let sym = self.sym(arg.trim());
1076 self.syms[sym].visibility = visibility;
1077 }
1078 Ok(())
1079 }
1080
1081 fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
1083 let [name, what] = self.two(args, ".type")?;
1084 let what = unquoted(what.trim().trim_start_matches(['@', '%']));
1085 let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
1086 "func" | "function" => Sort::Func,
1087 "object" | "gnu_unique_object" => Sort::Object,
1088 "tls_object" | "tls" => Sort::Thread,
1089 "notype" | "" => Sort::Untyped,
1090 other => {
1091 let what = format!("'{other}' is not a symbol type this compiler writes");
1092 return Err(self.bad(&what));
1093 }
1094 };
1095 let sym = self.sym(name.trim());
1096 self.syms[sym].sort = sort;
1097 Ok(())
1098 }
1099
1100 fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
1107 if !(2..=3).contains(&args.len()) {
1108 return Err(
1109 self.bad("a common directive wants a name, a size and an optional alignment")
1110 );
1111 }
1112 let name = args[0].trim().to_owned();
1113 let size = self.number(&args[1])?;
1114 let size = self.count(size)?;
1115 let align = match args.get(2) {
1116 Some(arg) => {
1117 let align = self.number(&arg.clone())?;
1118 self.count(align)?.max(1)
1119 }
1120 None => size.next_power_of_two().clamp(1, 16),
1123 };
1124 if !align.is_power_of_two() {
1125 let what = format!("an alignment of {align}, which is not a power of two");
1126 return Err(self.bad(&what));
1127 }
1128 let sym = self.sym(&name);
1129 let local = local || self.said_local.contains(&sym);
1133 self.syms[sym].sort = Sort::Object;
1137 if local {
1138 let was = self.here;
1139 self.section(".bss", Shape::of(".bss"));
1140 let part = &mut self.parts[self.here];
1141 part.align = part.align.max(align);
1142 let over = part.size % align;
1143 if over != 0 {
1144 part.size += align - over;
1145 }
1146 let offset = self.parts[self.here].size;
1147 self.parts[self.here].size += size;
1148 let at = self.here;
1149 self.syms[sym].at = Held::In { part: at, offset };
1150 self.syms[sym].size = size;
1151 self.syms[sym].binding = Binding::Local;
1152 self.go(was);
1153 } else {
1154 self.syms[sym].at = Held::Common { size, align };
1155 self.syms[sym].size = size;
1156 self.syms[sym].binding = Binding::Global;
1157 }
1158 Ok(())
1159 }
1160
1161 fn at(&self) -> u64 {
1163 let part = &self.parts[self.here];
1164 if part.shape.bits { part.bytes.len() as u64 } else { part.size }
1165 }
1166
1167 fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
1169 let part = &mut self.parts[self.here];
1170 if !part.shape.bits {
1171 if bytes.iter().all(|byte| *byte == 0) {
1172 part.size += bytes.len() as u64;
1175 return Ok(());
1176 }
1177 let what = format!("'{}' holds no bytes and this puts some in it", part.name);
1178 return Err(Trouble { line: self.line, why: what });
1179 }
1180 part.bytes.extend_from_slice(bytes);
1181 part.size = part.bytes.len() as u64;
1182 Ok(())
1183 }
1184
1185 fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
1187 let part = &mut self.parts[self.here];
1188 if !part.shape.bits {
1189 part.size += count;
1190 return Ok(());
1191 }
1192 part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
1193 part.size = part.bytes.len() as u64;
1194 Ok(())
1195 }
1196
1197 fn sym(&mut self, name: &str) -> usize {
1199 if let Some(&at) = self.known.get(name) {
1200 return at;
1201 }
1202 let at = self.syms.len();
1203 self.syms.push(Sym {
1204 name: name.to_owned(),
1205 at: Held::Undefined,
1206 size: 0,
1207 sort: Sort::Untyped,
1208 binding: Binding::Local,
1212 visibility: Visibility::Default,
1213 numbered: name.contains('\u{1}'),
1216 });
1217 self.known.insert(name.to_owned(), at);
1218 at
1219 }
1220
1221 fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
1223 if args.len() != 2 {
1224 let why = format!("{what} wants two operands and was given {}", args.len());
1225 return Err(Trouble { line: self.line, why });
1226 }
1227 Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
1228 }
1229
1230 fn number(&mut self, text: &str) -> Result<i64, Trouble> {
1232 let sum = self.expression(text)?;
1233 sum.flat().ok_or_else(|| Trouble {
1234 line: self.line,
1235 why: format!("'{}' has to be a number here and it names something", text.trim()),
1236 })
1237 }
1238
1239 fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
1241 let value = self.number(text)?;
1242 u8::try_from(value & 0xff).map_err(|_| Trouble {
1243 line: self.line,
1244 why: format!("{value} does not fit in a byte"),
1245 })
1246 }
1247
1248 fn count(&self, value: i64) -> Result<u64, Trouble> {
1250 u64::try_from(value).map_err(|_| Trouble {
1251 line: self.line,
1252 why: format!("{value} is negative and this is a length"),
1253 })
1254 }
1255
1256 fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
1258 self.expression_at(text, (self.here, self.at() as i64))
1259 }
1260
1261 fn expression_at(&mut self, text: &str, here: (usize, i64)) -> Result<Sum, Trouble> {
1263 let mut parser = Parser { text: text.trim(), at: 0, here };
1264 let mut sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
1265 for term in &mut sum.terms {
1268 if let What::Symbol(name) = &term.what {
1269 let name = self.named(name)?;
1270 self.sym(&name);
1271 term.what = What::Symbol(name);
1272 }
1273 }
1274 Ok(sum)
1275 }
1276
1277 fn bad(&self, why: &str) -> Trouble {
1279 Trouble { line: self.line, why: why.to_owned() }
1280 }
1281
1282 fn finish(mut self) -> Result<Result<Assembled, Vec<usize>>, Trouble> {
1287 if self.frame.is_some() {
1288 return Err(self.bad("a '.cfi_startproc' that is never ended"));
1289 }
1290 self.unwind_table();
1291 self.resolve_sets()?;
1292 self.resolve_sizes()?;
1293 let grow = self.too_far()?;
1294 if !grow.is_empty() {
1295 return Ok(Err(grow));
1296 }
1297 self.resolve_fixups()?;
1298 let keep: Vec<bool> = self
1302 .parts
1303 .iter()
1304 .enumerate()
1305 .map(|(at, part)| {
1306 part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
1307 })
1308 .collect();
1309 let mut moved = vec![0usize; self.parts.len()];
1310 let mut parts = Vec::with_capacity(self.parts.len());
1311 for (at, part) in self.parts.into_iter().enumerate() {
1312 if keep[at] {
1313 moved[at] = parts.len();
1314 parts.push(part);
1315 }
1316 }
1317 let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
1318 for file in self.files {
1321 names.push(Name {
1322 name: file,
1323 at: Held::Absolute(0),
1324 size: 0,
1325 sort: Sort::File,
1326 binding: Binding::Local,
1327 visibility: Visibility::Default,
1328 });
1329 }
1330 for (index, sym) in self.syms.into_iter().enumerate() {
1331 if sym.numbered && !self.relocated.contains(&index) {
1336 continue;
1337 }
1338 let at = match sym.at {
1339 Held::In { part, offset } => Held::In { part: moved[part], offset },
1340 other => other,
1341 };
1342 let binding = match (at, sym.binding) {
1343 (Held::Undefined, Binding::Local) => Binding::Global,
1344 (_, binding) => binding,
1345 };
1346 names.push(Name {
1347 name: sym.name,
1348 at,
1349 size: sym.size,
1350 sort: sym.sort,
1351 binding,
1352 visibility: sym.visibility,
1353 });
1354 }
1355 Ok(Ok(Assembled { parts, names }))
1356 }
1357
1358 fn unwind_table(&mut self) {
1364 if self.frames.is_empty() || self.no_unwind {
1365 return;
1366 }
1367 let funcs: Vec<Extent> = self
1368 .frames
1369 .iter()
1370 .map(|frame| Extent {
1371 name: self.syms[frame.sym].name.clone(),
1372 start: frame.start as usize,
1373 len: frame.len as usize,
1374 align: 1,
1375 binding: Binding::Local,
1376 visibility: Visibility::Default,
1377 patch: None,
1378 })
1379 .collect();
1380 let rows: Vec<_> = self.frames.iter().map(|frame| frame.rows.clone()).collect();
1381 let conv: &CallRegs = if self.aarch64 { &AAPCS64 } else { &SYSV };
1382 let Ok(table) = crate::unwind::table(&funcs, &rows, conv, ObjectFormat::Elf) else {
1383 return;
1384 };
1385 for frame in &self.frames {
1386 self.relocated.insert(frame.sym);
1387 }
1388 let size = table.bytes.len() as u64;
1389 self.parts.push(Part {
1390 name: ".eh_frame".to_owned(),
1391 bytes: table.bytes,
1392 size,
1393 align: 8,
1394 shape: Shape { alloc: true, bits: true, ..Shape::default() },
1395 relocs: table.relocs,
1396 });
1397 }
1398
1399 fn resolve_sets(&mut self) -> Result<(), Trouble> {
1402 while !self.sets.is_empty() {
1403 let mut done = Vec::new();
1404 for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
1405 if let Ok(residue) = self.reduce(sum) {
1406 done.push((at, *sym, self.settled(&residue, *line)?));
1407 }
1408 }
1409 if done.is_empty() {
1410 let (sym, _, line) = &self.sets[0];
1411 let why = format!(
1412 "'{}' is set to something that is set to it, so neither has a value",
1413 self.syms[*sym].name
1414 );
1415 return Err(Trouble { line: *line, why });
1416 }
1417 for (_, sym, held) in &done {
1418 self.syms[*sym].at = *held;
1419 }
1420 for (at, _, _) in done.iter().rev() {
1422 self.sets.remove(*at);
1423 }
1424 }
1425 Ok(())
1426 }
1427
1428 fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
1430 match residue.left.as_slice() {
1431 [] => Ok(Held::Absolute(residue.constant as u64)),
1432 [Left { coeff: 1, at: Some((part, offset)), .. }] => {
1435 Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
1436 }
1437 _ => Err(Trouble {
1438 line,
1439 why: "a set to something that is neither a number nor a place in this file"
1440 .to_owned(),
1441 }),
1442 }
1443 }
1444
1445 fn resolve_sizes(&mut self) -> Result<(), Trouble> {
1447 for (sym, sum, line) in std::mem::take(&mut self.sizes) {
1448 let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
1449 if !residue.left.is_empty() {
1450 let why = format!(
1451 "the size of '{}' is not a number, and a size has to be one",
1452 self.syms[sym].name
1453 );
1454 return Err(Trouble { line, why });
1455 }
1456 let size = self.count(residue.constant).map_err(|_| Trouble {
1457 line,
1458 why: format!("'{}' is given a negative size", self.syms[sym].name),
1459 })?;
1460 self.syms[sym].size = size;
1461 }
1462 Ok(())
1463 }
1464
1465 fn too_far(&self) -> Result<Vec<usize>, Trouble> {
1485 let mut away = Vec::new();
1486 let mut jumps: Vec<(usize, i64, i64, usize)> = Vec::new();
1488 for fixup in &self.fixups {
1489 let Some(nth) = fixup.branch else { continue };
1490 let residue = self
1491 .reduce_kept(&fixup.sum, true)
1492 .map_err(|why| Trouble { line: fixup.line, why })?;
1493 if !residue.left.is_empty() {
1494 away.push(nth);
1495 } else {
1496 jumps.push((fixup.part, fixup.at as i64 + 1, residue.constant, nth));
1497 }
1498 }
1499 if !away.is_empty() {
1500 return Ok(away);
1501 }
1502 jumps.sort_unstable();
1503 let mut far = Vec::new();
1504 let mut jumps = jumps.into_iter().peekable();
1505 while let Some(&(part, ..)) = jumps.peek() {
1506 let aligns: Vec<Aligned> =
1507 self.aligns.iter().filter(|align| align.part == part).copied().collect();
1508 let mut aligns_left = aligns.iter().peekable();
1509 let mut stretch = 0i64;
1510 let mut moved: Vec<(i64, i64)> = Vec::new();
1512 while let Some(&(_, end, distance, nth)) = jumps.peek().filter(|jump| jump.0 == part) {
1513 jumps.next();
1514 while let Some(align) = aligns_left.next_if(|align| align.at as i64 <= end - 2) {
1515 let now =
1516 padding((align.at as i64 + stretch) as u64, align.boundary, align.most);
1517 stretch += now as i64 - align.need as i64;
1518 moved.push(((align.at + align.need) as i64, stretch));
1519 }
1520 let target = end + distance;
1521 let judged = if distance < 0 {
1522 let there = moved.iter().rev().find(|(from, _)| *from <= target);
1523 distance + there.map_or(0, |(_, by)| *by) - stretch
1524 } else if stretch > 0
1525 && aligns.iter().any(|align| {
1526 end <= align.at as i64 && (align.at + align.need) as i64 <= target
1527 })
1528 {
1529 distance - stretch
1530 } else {
1531 distance
1532 };
1533 if distance >= 0 && judged < -2 {
1537 continue;
1538 }
1539 if i8::try_from(judged).is_err() {
1540 far.push(nth);
1541 stretch += if self.parts[part].bytes[end as usize - 2] == 0xEB { 3 } else { 4 };
1542 moved.push((end, stretch));
1543 }
1544 }
1545 }
1546 Ok(far)
1547 }
1548
1549 fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1550 for fixup in std::mem::take(&mut self.fixups) {
1551 if let Some(field) = fixup.field {
1552 self.field(&fixup, field)?;
1553 continue;
1554 }
1555 let line = fixup.line;
1556 let bad = |why: String| Trouble { line, why };
1557 if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1565 let [
1566 Term { coeff: 1, what: What::Symbol(name) },
1567 Term { coeff: -1, what: What::Here { at: end, .. } },
1568 ] = fixup.sum.terms.as_slice()
1569 else {
1570 return Err(bad(
1571 "a reach through the global offset table in something other than an \
1572 instruction, which is not an expression this compiler writes"
1573 .to_owned(),
1574 ));
1575 };
1576 let kind = if fixup.reach == Reach::Table { fixup.slot } else { Reference::Thread };
1577 self.parts[fixup.part].relocs.push(Reloc {
1578 at: fixup.at as usize,
1579 symbol: name.clone(),
1580 kind,
1581 addend: fixup.sum.constant + fixup.at as i64 - end,
1582 after: (end - fixup.at as i64 - 4).max(0) as u8,
1583 });
1584 continue;
1585 }
1586 let residue =
1587 self.reduce_kept(&fixup.sum, fixup.jump).map_err(|why| Trouble { line, why })?;
1588 if fixup.reach == Reach::Value && !residue.left.is_empty() {
1589 return Err(bad(
1590 "a number in an instruction that names something outside this section, \
1591 which wants a relocation this compiler does not write yet"
1592 .to_owned(),
1593 ));
1594 }
1595 let (symbol, kind, addend, after) = match residue.left.as_slice() {
1596 [] => {
1597 let width = fixup.width as usize;
1605 let room = 8 * width as u32;
1606 let low = -(1i64 << (room - 1));
1607 let high = if fixup.reach == Reach::Branch {
1608 (1i64 << (room - 1)) - 1
1609 } else {
1610 (1i64 << room) - 1
1611 };
1612 if width < 8 && (residue.constant < low || residue.constant > high) {
1613 return Err(bad(format!(
1614 "{} written into {width} bytes, which does not reach it",
1615 residue.constant
1616 )));
1617 }
1618 let bytes = residue.constant.to_le_bytes();
1619 let at = fixup.at as usize;
1620 let part = &mut self.parts[fixup.part];
1621 part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1622 continue;
1623 }
1624 [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1626 let kind = Reference::Address { bytes: fixup.width };
1627 (name.clone(), kind, residue.constant, 0)
1628 }
1629 [
1634 Left { coeff: 1, what: What::Symbol(name), .. },
1635 Left { coeff: -1, at: Some((part, offset)), .. },
1636 ]
1637 | [
1638 Left { coeff: -1, at: Some((part, offset)), .. },
1639 Left { coeff: 1, what: What::Symbol(name), .. },
1640 ] => {
1641 if *part != fixup.part {
1642 return Err(bad(
1643 "a distance that is subtracted from somewhere in another section"
1644 .to_owned(),
1645 ));
1646 }
1647 if fixup.width != 4 {
1648 return Err(bad(format!(
1649 "a distance written into {} bytes, and four is the only width a \
1650 relocation says one at",
1651 fixup.width
1652 )));
1653 }
1654 let addend = residue.constant + fixup.at as i64 - offset;
1661 let near = self.known.get(name).is_some_and(|&sym| {
1664 self.syms[sym].binding == Binding::Local
1665 && matches!(self.syms[sym].at, Held::In { .. })
1666 });
1667 let kind = if fixup.reach == Reach::Branch && !near {
1668 Reference::Call
1669 } else {
1670 Reference::Data
1671 };
1672 let after = (offset - fixup.at as i64 - 4).max(0);
1675 (name.clone(), kind, addend, after as u8)
1676 }
1677 [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1678 return Err(bad(
1679 "the address of these bytes themselves, which has no symbol to be \
1680 relocated against"
1681 .to_owned(),
1682 ));
1683 }
1684 _ => {
1685 return Err(bad(
1686 "an expression that does not come out as a number, an address, or a \
1687 distance, and those are what a relocation can say"
1688 .to_owned(),
1689 ));
1690 }
1691 };
1692 if let Some(&sym) = self.known.get(&symbol) {
1696 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1697 self.relocated.insert(sym);
1698 } else if self.syms[sym].numbered {
1699 let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1700 return Err(bad(format!(
1701 "'{number}f' goes on to a '{number}:' and there is none below it"
1702 )));
1703 }
1704 }
1705 if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1706 return Err(bad(format!(
1707 "the address of '{symbol}' written into {} bytes, and this machine relocates \
1708 an address at four or eight",
1709 fixup.width
1710 )));
1711 }
1712 self.parts[fixup.part].relocs.push(Reloc {
1713 at: fixup.at as usize,
1714 symbol,
1715 kind,
1716 addend,
1717 after,
1718 });
1719 }
1720 Ok(())
1721 }
1722
1723 fn field(&mut self, fixup: &Fixup, field: aarch64::Fixup) -> Result<(), Trouble> {
1733 let line = fixup.line;
1734 let bad = |why: String| Trouble { line, why };
1735 let residue = self.reduce_kept(&fixup.sum, fixup.jump).map_err(bad)?;
1736 let (name, addend) = match residue.left.as_slice() {
1737 [] if relative(field) => {
1738 let at = fixup.at as usize;
1739 let bytes = &mut self.parts[fixup.part].bytes[at..at + 4];
1740 let word = u32::from_le_bytes(bytes.try_into().expect("four bytes"));
1741 let word = field.apply(word, residue.constant).ok_or_else(|| {
1742 bad(format!("{} is out of the reach of {}", residue.constant, field.name()))
1743 })?;
1744 bytes.copy_from_slice(&word.to_le_bytes());
1745 return Ok(());
1746 }
1747 [Left { coeff: 1, what: What::Symbol(name), .. }] if !relative(field) => {
1748 (name.clone(), residue.constant)
1749 }
1750 [
1751 Left { coeff: 1, what: What::Symbol(name), .. },
1752 Left { coeff: -1, at: Some((part, offset)), .. },
1753 ]
1754 | [
1755 Left { coeff: -1, at: Some((part, offset)), .. },
1756 Left { coeff: 1, what: What::Symbol(name), .. },
1757 ] if relative(field) && *part == fixup.part => {
1758 (name.clone(), residue.constant + fixup.at as i64 - offset)
1759 }
1760 _ => {
1761 return Err(bad(format!(
1762 "an expression that {} cannot say, which is a name and a number added to it",
1763 field.name()
1764 )));
1765 }
1766 };
1767 if let Some(&sym) = self.known.get(&name) {
1768 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1769 self.relocated.insert(sym);
1770 } else if self.syms[sym].numbered {
1771 let number = name.split('\u{1}').next().unwrap_or(&name);
1772 return Err(bad(format!(
1773 "'{number}f' goes on to a '{number}:' and there is none below it"
1774 )));
1775 }
1776 }
1777 self.parts[fixup.part].relocs.push(Reloc {
1778 at: fixup.at as usize,
1779 symbol: name,
1780 kind: Reference::Field(field),
1781 addend,
1782 after: 0,
1783 });
1784 Ok(())
1785 }
1786
1787 fn reduce_kept(&self, sum: &Sum, jump: bool) -> Result<Residue, String> {
1798 let mut named =
1799 sum.terms.iter().enumerate().filter(|(_, term)| matches!(term.what, What::Symbol(_)));
1800 let (Some((nth, Term { coeff: 1, what: What::Symbol(name) })), None) =
1801 (named.next(), named.next())
1802 else {
1803 return self.reduce(sum);
1804 };
1805 let kept = self.known.get(name).is_some_and(|&sym| {
1806 (self.syms[sym].binding == Binding::Weak
1807 || !jump && self.syms[sym].binding == Binding::Global)
1808 && matches!(self.syms[sym].at, Held::In { .. })
1809 });
1810 if !kept {
1811 return self.reduce(sum);
1812 }
1813 let mut rest = sum.clone();
1814 rest.terms.remove(nth);
1815 let mut residue = self.reduce(&rest)?;
1816 residue.left.push(Left { coeff: 1, what: What::Symbol(name.clone()), at: None });
1817 Ok(residue)
1818 }
1819
1820 fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
1829 let mut constant = sum.constant;
1830 let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
1831 let mut outside: Vec<(i64, String)> = Vec::new();
1832 for term in &sum.terms {
1833 match &term.what {
1834 What::Here { part, at } => {
1835 placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
1836 }
1837 What::Symbol(name) => {
1838 let Some(&at) = self.known.get(name) else {
1839 return Err(format!("'{name}' is named and never said"));
1840 };
1841 match self.syms[at].at {
1842 Held::Absolute(value) => constant += term.coeff * value as i64,
1843 Held::In { part, offset } => placed.entry(part).or_default().push((
1844 term.coeff,
1845 term.what.clone(),
1846 offset as i64,
1847 )),
1848 Held::Undefined | Held::Common { .. } => {
1851 if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
1852 outside.push((term.coeff, name.clone()));
1853 } else {
1854 return Err(format!("'{name}' is not worked out yet"));
1855 }
1856 }
1857 }
1858 }
1859 }
1860 }
1861 let mut left: Vec<Left> = Vec::new();
1862 for (part, terms) in placed {
1863 let (_, chosen, base) = terms[0].clone();
1864 let mut net = 0;
1865 for (coeff, _, offset) in &terms {
1866 net += coeff;
1867 constant += coeff * (offset - base);
1868 }
1869 if net != 0 {
1870 left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
1871 }
1872 }
1873 let mut together: BTreeMap<String, i64> = BTreeMap::new();
1874 for (coeff, name) in outside {
1875 *together.entry(name).or_default() += coeff;
1876 }
1877 for (name, coeff) in together {
1878 if coeff != 0 {
1879 left.push(Left { coeff, what: What::Symbol(name), at: None });
1880 }
1881 }
1882 Ok(Residue { constant, left })
1883 }
1884}
1885
1886#[derive(Debug, Clone)]
1888struct Residue {
1889 constant: i64,
1890 left: Vec<Left>,
1891}
1892
1893#[derive(Debug, Clone)]
1895struct Left {
1896 coeff: i64,
1898 what: What,
1900 at: Option<(usize, i64)>,
1903}
1904
1905#[derive(Debug, Clone, Default, PartialEq, Eq)]
1907struct Sum {
1908 constant: i64,
1909 terms: Vec<Term>,
1910}
1911
1912#[derive(Debug, Clone, PartialEq, Eq)]
1914struct Term {
1915 coeff: i64,
1916 what: What,
1917}
1918
1919#[derive(Debug, Clone, PartialEq, Eq)]
1921enum What {
1922 Symbol(String),
1924 Here { part: usize, at: i64 },
1927}
1928
1929impl Sum {
1930 fn flat(&self) -> Option<i64> {
1932 self.terms.is_empty().then_some(self.constant)
1933 }
1934
1935 fn of(what: What) -> Sum {
1937 Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1938 }
1939
1940 fn just(value: i64) -> Sum {
1942 Sum { constant: value, terms: Vec::new() }
1943 }
1944
1945 fn plus(mut self, other: Sum) -> Sum {
1947 self.constant = self.constant.wrapping_add(other.constant);
1948 self.terms.extend(other.terms);
1949 self
1950 }
1951
1952 fn minus(self) -> Sum {
1954 Sum {
1955 constant: self.constant.wrapping_neg(),
1956 terms: self
1957 .terms
1958 .into_iter()
1959 .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
1960 .collect(),
1961 }
1962 }
1963
1964 fn times(self, factor: i64) -> Sum {
1966 Sum {
1967 constant: self.constant.wrapping_mul(factor),
1968 terms: self
1969 .terms
1970 .into_iter()
1971 .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
1972 .collect(),
1973 }
1974 }
1975}
1976
1977struct Parser<'a> {
1979 text: &'a str,
1980 at: usize,
1981 here: (usize, i64),
1982}
1983
1984impl Parser<'_> {
1985 fn whole(&mut self) -> Result<Sum, String> {
1987 let sum = self.bitwise()?;
1988 self.space();
1989 if self.at < self.text.len() {
1990 return Err(format!(
1991 "'{}' is left over at the end of an expression",
1992 &self.text[self.at..]
1993 ));
1994 }
1995 Ok(sum)
1996 }
1997
1998 fn bitwise(&mut self) -> Result<Sum, String> {
2000 let mut left = self.shift()?;
2001 loop {
2002 self.space();
2003 let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
2004 let right = self.shift()?;
2005 left = self.arithmetic(left, right, op)?;
2006 }
2007 }
2008
2009 fn shift(&mut self) -> Result<Sum, String> {
2011 let mut left = self.sum()?;
2012 loop {
2013 self.space();
2014 let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
2015 let right = self.sum()?;
2016 left = self.arithmetic(left, right, op)?;
2017 }
2018 }
2019
2020 fn sum(&mut self) -> Result<Sum, String> {
2022 let mut left = self.product()?;
2023 loop {
2024 self.space();
2025 let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
2027 let right = self.product()?;
2028 left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
2029 }
2030 }
2031
2032 fn product(&mut self) -> Result<Sum, String> {
2034 let mut left = self.unary()?;
2035 loop {
2036 self.space();
2037 let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
2038 let right = self.unary()?;
2039 left = match (op, left.flat(), right.flat()) {
2043 ("*", _, Some(factor)) => left.times(factor),
2044 ("*", Some(factor), _) => right.times(factor),
2045 (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
2046 _ => return Err(format!("'{op}' of something that names a symbol")),
2047 };
2048 }
2049 }
2050
2051 fn unary(&mut self) -> Result<Sum, String> {
2053 self.space();
2054 if self.eat("-") {
2055 return Ok(self.unary()?.minus());
2056 }
2057 if self.eat("+") {
2058 return self.unary();
2059 }
2060 if self.eat("~") {
2061 let inner = self.unary()?;
2062 let value = inner
2063 .flat()
2064 .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
2065 return Ok(Sum::just(!value));
2066 }
2067 if self.eat("!") {
2068 let inner = self.unary()?;
2069 let value = inner
2070 .flat()
2071 .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
2072 return Ok(Sum::just(i64::from(value == 0)));
2073 }
2074 self.primary()
2075 }
2076
2077 fn primary(&mut self) -> Result<Sum, String> {
2079 self.space();
2080 let rest = &self.text[self.at..];
2081 if rest.is_empty() {
2082 return Err("an expression that stops before it says anything".to_owned());
2083 }
2084 if self.eat("(") {
2085 let inner = self.bitwise()?;
2086 self.space();
2087 if !self.eat(")") {
2088 return Err("a bracket that was opened and never closed".to_owned());
2089 }
2090 return Ok(inner);
2091 }
2092 let first = rest.as_bytes()[0];
2093 if first == b'\'' {
2094 return self.character();
2095 }
2096 if first.is_ascii_digit() {
2097 let end = rest.find(|ch: char| !ch.is_ascii_digit()).unwrap_or(rest.len());
2101 let bytes = rest.as_bytes();
2102 if matches!(bytes.get(end), Some(b'b' | b'f'))
2103 && !bytes.get(end + 1).is_some_and(|byte| carries_on(*byte))
2104 {
2105 self.at += end + 1;
2106 return Ok(Sum::of(What::Symbol(rest[..=end].to_owned())));
2107 }
2108 return self.digits();
2109 }
2110 if starts(first) {
2111 let name = self.word();
2112 if name == "." {
2114 let (part, at) = self.here;
2115 return Ok(Sum::of(What::Here { part, at }));
2116 }
2117 if self.text[self.at..].starts_with('@') {
2121 return Err(format!(
2122 "'{name}@' asks for a relocation only an instruction can carry"
2123 ));
2124 }
2125 return Ok(Sum::of(What::Symbol(name)));
2126 }
2127 Err(format!("'{rest}' is not the start of an expression"))
2128 }
2129
2130 fn digits(&mut self) -> Result<Sum, String> {
2132 let rest = &self.text[self.at..];
2133 let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
2134 (16, 2)
2135 } else if rest.starts_with("0b") || rest.starts_with("0B") {
2136 (2, 2)
2137 } else if rest.len() > 1 && rest.starts_with('0') {
2138 (8, 1)
2139 } else {
2140 (10, 0)
2141 };
2142 let body = &rest[skip..];
2143 let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
2144 if end == 0 {
2145 return Err(format!("'{rest}' starts like a number and is not one"));
2146 }
2147 let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
2148 let value = u64::from_str_radix(&text, radix)
2151 .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
2152 self.at += skip + end;
2153 while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
2156 self.at += 1;
2157 }
2158 Ok(Sum::just(value as i64))
2159 }
2160
2161 fn character(&mut self) -> Result<Sum, String> {
2163 self.at += 1;
2164 let rest = &self.text[self.at..];
2165 let mut chars = rest.chars();
2166 let Some(first) = chars.next() else {
2167 return Err("a quote with no character after it".to_owned());
2168 };
2169 let (value, used) = if first == '\\' {
2170 let (value, used) = escape(&rest[1..])?;
2171 (value, used + 1)
2172 } else {
2173 (first as u8, first.len_utf8())
2174 };
2175 self.at += used;
2176 if self.text[self.at..].starts_with('\'') {
2179 self.at += 1;
2180 }
2181 Ok(Sum::just(i64::from(value)))
2182 }
2183
2184 fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
2186 let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
2187 return Err(format!("'{op}' of something that names a symbol"));
2188 };
2189 Ok(Sum::just(self.arithmetic_number(a, b, op)?))
2190 }
2191
2192 fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
2194 Ok(match op {
2195 "|" => a | b,
2196 "^" => a ^ b,
2197 "&" => a & b,
2198 "<<" => a.wrapping_shl(shift(b)?),
2199 ">>" => a.wrapping_shr(shift(b)?),
2200 "*" => a.wrapping_mul(b),
2201 "/" if b == 0 => return Err("a division by zero".to_owned()),
2202 "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
2203 "/" => a.wrapping_div(b),
2204 "%" => a.wrapping_rem(b),
2205 _ => return Err(format!("'{op}' is not an operator this compiler knows")),
2206 })
2207 }
2208
2209 fn word(&mut self) -> String {
2211 let body = &self.text[self.at..];
2212 let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
2213 let word = body[..end].to_owned();
2214 self.at += end;
2215 word
2216 }
2217
2218 fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
2223 for op in ops {
2224 if self.text[self.at..].starts_with(op) {
2225 self.at += op.len();
2226 return Some(op);
2227 }
2228 }
2229 None
2230 }
2231
2232 fn eat(&mut self, what: &str) -> bool {
2234 if self.text[self.at..].starts_with(what) {
2235 self.at += what.len();
2236 return true;
2237 }
2238 false
2239 }
2240
2241 fn space(&mut self) {
2243 while self.text[self.at..].starts_with([' ', '\t']) {
2244 self.at += 1;
2245 }
2246 }
2247}
2248
2249impl Reader {
2250 fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
2252 let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
2253 let body = text
2254 .strip_prefix('"')
2255 .and_then(|rest| rest.strip_suffix('"'))
2256 .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
2257 let mut out = Vec::with_capacity(body.len());
2258 let mut at = 0;
2259 while at < body.len() {
2260 let rest = &body[at..];
2261 let first = rest.as_bytes()[0];
2262 if first == b'\\' {
2263 let (value, used) =
2264 escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
2265 out.push(value);
2266 at += used + 1;
2267 continue;
2268 }
2269 let ch = rest.chars().next().unwrap_or('\0');
2270 let mut buffer = [0u8; 4];
2271 out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
2272 at += ch.len_utf8();
2273 }
2274 Ok(out)
2275 }
2276}
2277
2278const A64_NOP: u32 = 0xd503_201f;
2281
2282fn relative(field: aarch64::Fixup) -> bool {
2285 use aarch64::Fixup;
2286 matches!(
2287 field,
2288 Fixup::Jump26
2289 | Fixup::Call26
2290 | Fixup::CondBr19
2291 | Fixup::TestBr14
2292 | Fixup::Literal19
2293 | Fixup::AdrLo21
2294 )
2295}
2296
2297fn aarch64_dwarf(text: &str) -> Option<u16> {
2300 if let Ok(number) = text.parse::<u16>() {
2301 return Some(number);
2302 }
2303 let lower = text.to_ascii_lowercase();
2304 match lower.as_str() {
2305 "sp" => return Some(31),
2306 "fp" => return Some(29),
2307 "lr" => return Some(30),
2308 _ => {}
2309 }
2310 let (first, number) = lower.split_at(1);
2311 let number = number.parse::<u16>().ok().filter(|&number| number < 32)?;
2312 match first {
2313 "x" | "w" if number < 31 => Some(number),
2314 "v" | "q" | "d" | "s" => Some(64 + number),
2315 _ => None,
2316 }
2317}
2318
2319fn shift(by: i64) -> Result<u32, String> {
2320 u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
2321}
2322
2323fn escape(rest: &str) -> Result<(u8, usize), String> {
2327 let bytes = rest.as_bytes();
2328 let Some(&first) = bytes.first() else {
2329 return Err("a backslash with nothing after it".to_owned());
2330 };
2331 let simple = match first {
2332 b'n' => Some(b'\n'),
2333 b't' => Some(b'\t'),
2334 b'r' => Some(b'\r'),
2335 b'f' => Some(0x0c),
2336 b'b' => Some(0x08),
2337 b'v' => Some(0x0b),
2338 b'a' => Some(0x07),
2339 b'e' => Some(0x1b),
2340 b'\\' => Some(b'\\'),
2341 b'"' => Some(b'"'),
2342 b'\'' => Some(b'\''),
2343 _ => None,
2344 };
2345 if let Some(value) = simple {
2346 return Ok((value, 1));
2347 }
2348 if first == b'x' || first == b'X' {
2349 let end = bytes[1..]
2350 .iter()
2351 .position(|byte| !byte.is_ascii_hexdigit())
2352 .map_or(bytes.len(), |at| at + 1);
2353 if end == 1 {
2354 return Err("a hex escape with no digits in it".to_owned());
2355 }
2356 let text = &rest[1..end];
2359 let text = &text[text.len().saturating_sub(2)..];
2360 let value =
2361 u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
2362 return Ok((value, end));
2363 }
2364 if (b'0'..=b'7').contains(&first) {
2365 let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
2366 let value = u32::from_str_radix(&rest[..end], 8)
2367 .map_err(|_| "an octal escape that is not one".to_owned())?;
2368 return Ok(((value & 0xff) as u8, end));
2369 }
2370 Err(format!("'\\{}' is not an escape this compiler knows", first as char))
2374}
2375
2376fn labelled(text: &str) -> Option<String> {
2383 let bytes = text.as_bytes();
2384 if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
2385 return None;
2386 }
2387 let end = text.find(|ch: char| !carries_on(ch as u8))?;
2388 if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
2390 return None;
2391 }
2392 Some(text[..end].to_owned())
2393}
2394
2395fn assigned(text: &str) -> Option<(&str, &str)> {
2399 let bytes = text.as_bytes();
2400 if bytes.is_empty() || !starts(bytes[0]) {
2401 return None;
2402 }
2403 let end = text.find(|ch: char| !carries_on(ch as u8)).unwrap_or(text.len());
2404 let rest = text[end..].trim_start().strip_prefix('=')?;
2405 if rest.starts_with('=') {
2406 return None;
2407 }
2408 Some((&text[..end], rest.trim()))
2409}
2410
2411fn starts(byte: u8) -> bool {
2413 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
2414}
2415
2416fn carries_on(byte: u8) -> bool {
2418 starts(byte) || byte.is_ascii_digit()
2419}
2420
2421fn padding(at: u64, boundary: u64, most: Option<u64>) -> u64 {
2429 let over = at % boundary;
2430 let need = if over == 0 { 0 } else { boundary - over };
2431 if most.is_some_and(|most| need > most) { 0 } else { need }
2432}
2433
2434fn counted(number: &str, nth: usize) -> String {
2435 format!("{number}\u{1}{nth}")
2436}
2437
2438fn unquoted(text: &str) -> String {
2440 text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
2441}
2442
2443pub(crate) fn split(text: &str, on: char) -> Vec<String> {
2448 let mut out = Vec::new();
2449 let mut piece = String::new();
2450 let mut depth = 0i32;
2451 let mut quote = None;
2452 let mut chars = text.chars();
2453 while let Some(ch) = chars.next() {
2454 if let Some(mark) = quote {
2455 piece.push(ch);
2456 if ch == '\\' {
2457 if let Some(next) = chars.next() {
2458 piece.push(next);
2459 }
2460 continue;
2461 }
2462 if ch == mark {
2463 quote = None;
2464 }
2465 continue;
2466 }
2467 match ch {
2468 '"' => {
2469 quote = Some(ch);
2470 piece.push(ch);
2471 }
2472 '(' => {
2473 depth += 1;
2474 piece.push(ch);
2475 }
2476 ')' => {
2477 depth -= 1;
2478 piece.push(ch);
2479 }
2480 _ if ch == on && depth == 0 => {
2481 out.push(std::mem::take(&mut piece));
2482 }
2483 _ => piece.push(ch),
2484 }
2485 }
2486 if !piece.trim().is_empty() || !out.is_empty() {
2487 out.push(piece);
2488 }
2489 out.into_iter().map(|piece| piece.trim().to_owned()).collect()
2490}
2491
2492fn repeated<'a>(word: &str, rest: &'a str) -> Option<(String, &'a str)> {
2503 let (next, after) = match rest.find(char::is_whitespace) {
2504 Some(cut) => (&rest[..cut], rest[cut..].trim()),
2505 None => (rest, ""),
2506 };
2507 if word == "notrack" {
2508 return match next {
2509 "jmp" | "jmpq" => Some(("notrack jmp".to_owned(), after)),
2510 "call" | "callq" => Some(("notrack call".to_owned(), after)),
2511 _ => None,
2512 };
2513 }
2514 if matches!(word, "rep" | "repe" | "repz") {
2517 if let Some(width) = next.strip_prefix("bsf") {
2518 if matches!(width, "" | "w" | "l" | "q") {
2519 return Some((format!("tzcnt{width}"), after));
2520 }
2521 }
2522 }
2523 let unequal = match word {
2524 "rep" | "repe" | "repz" => false,
2525 "repne" | "repnz" => true,
2526 _ => return None,
2527 };
2528 let string = next.len() == 5 && next.ends_with(['b', 'w', 'l', 'q']);
2529 let which = if string { &next[..4] } else { "" };
2530 let prefix = match (unequal, which) {
2531 (false, "movs" | "stos") => "rep",
2532 (false, "scas" | "cmps") => "repe",
2533 (true, "scas" | "cmps") => "repne",
2534 _ => return None,
2535 };
2536 Some((format!("{prefix} {next}"), after))
2537}
2538
2539#[cfg(test)]
2540mod tests {
2541 use super::*;
2542
2543 use rucc_object::Reference;
2544
2545 fn assembled(text: &str) -> Assembled {
2547 match read(text, Arch::X86_64) {
2548 Ok(assembled) => assembled,
2549 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2550 }
2551 }
2552
2553 fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
2555 let part = assembled
2556 .parts
2557 .iter()
2558 .find(|part| part.name == name)
2559 .unwrap_or_else(|| panic!("there is no section called '{name}'"));
2560 part.bytes.clone()
2561 }
2562
2563 fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
2565 assembled
2566 .names
2567 .iter()
2568 .find(|name| name.name == want)
2569 .unwrap_or_else(|| panic!("there is no name called '{want}'"))
2570 }
2571
2572 fn refused(text: &str) -> Trouble {
2574 read(text, Arch::X86_64)
2575 .err()
2576 .unwrap_or_else(|| panic!("this was read and should not have been"))
2577 }
2578
2579 fn aarch64(text: &str) -> Assembled {
2581 match read(text, Arch::Aarch64) {
2582 Ok(assembled) => assembled,
2583 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2584 }
2585 }
2586
2587 fn words(assembled: &Assembled, name: &str) -> Vec<u32> {
2589 let bytes = bytes(assembled, name);
2590 bytes.chunks(4).map(|word| u32::from_le_bytes(word.try_into().unwrap())).collect()
2591 }
2592
2593 #[test]
2594 fn an_aarch64_branch_in_the_file_is_filled_in_and_a_name_is_left_to_the_linker() {
2595 let read = aarch64(concat!(
2596 "# 1 \"f.s\"\n",
2597 "f:\tcbz x0, 1f // to the return\n",
2598 "\tbl g\n",
2599 "\tadrp x1, table+8\n",
2600 "\tadd x1, x1, :lo12:table+8\n",
2601 "1:\tret\n",
2602 "\t.p2align 3\n",
2603 "\t.word 5\n",
2604 ));
2605 assert_eq!(
2607 words(&read, ".text"),
2608 [0xb400_0080, 0x9400_0000, 0x9000_0001, 0x9100_0021, 0xd65f_03c0, 0xd503_201f, 5]
2609 );
2610 let text = read.parts.iter().find(|part| part.name == ".text").unwrap();
2611 let relocs: Vec<_> =
2612 text.relocs.iter().map(|r| (r.at, r.symbol.as_str(), r.kind, r.addend)).collect();
2613 assert_eq!(
2614 relocs,
2615 [
2616 (4, "g", Reference::Field(aarch64::Fixup::Call26), 0),
2617 (8, "table", Reference::Field(aarch64::Fixup::AdrPage21), 8),
2618 (12, "table", Reference::Field(aarch64::Fixup::AddLo12), 8),
2619 ]
2620 );
2621 }
2622
2623 #[test]
2624 fn an_aarch64_frame_starts_at_the_stack_pointer_with_the_return_address_in_x30() {
2625 let read = aarch64(concat!(
2626 "f:\n\t.cfi_startproc\n\tstr x19, [sp, #-16]!\n\t.cfi_def_cfa_offset 16\n",
2627 "\t.cfi_offset x19, -16\n\tldr x19, [sp], #16\n\t.cfi_restore 19\n",
2628 "\t.cfi_def_cfa_offset 0\n\tret\n\t.cfi_endproc\n",
2629 ));
2630 let frame = bytes(&read, ".eh_frame");
2631 assert_eq!(&frame[12..20], &[1, 0x78, 30, 1, 0x1b, 0x0c, 31, 0]);
2635 assert_eq!(&frame[20..24], &[0; 4]);
2636 assert!(frame.windows(5).any(|w| w == [0x44, 0x0e, 16, 0x93, 2]), "{frame:x?}");
2638 }
2639
2640 #[test]
2641 fn an_aarch64_line_that_is_not_an_instruction_is_refused_with_its_line() {
2642 let trouble = read("f:\n\tadd x0, x1, #zz\n", Arch::Aarch64).unwrap_err();
2643 assert_eq!(trouble.line, 2);
2644 let trouble = read("\tb 1f\n", Arch::Aarch64).unwrap_err();
2645 assert!(trouble.why.contains("'1f'"), "{trouble}");
2646 let trouble = read("\tcbz x0, far\n\t.skip 2000000\nfar:\tret\n", Arch::Aarch64);
2647 assert!(trouble.unwrap_err().why.contains("R_AARCH64_CONDBR19"));
2648 }
2649
2650 #[test]
2651 fn a_repeat_prefix_is_read_with_the_string_instruction_behind_it() {
2652 let assembled =
2653 assembled("\t.text\n\trep movsl\n\trepnz scasb\n\trepz cmpsb\n\trep stosq\n");
2654 assert_eq!(
2655 bytes(&assembled, ".text"),
2656 [0xF3, 0xA5, 0xF2, 0xAE, 0xF3, 0xA6, 0xF3, 0x48, 0xAB]
2657 );
2658 }
2659
2660 #[test]
2661 fn notrack_is_read_with_the_jump_behind_it() {
2662 let assembled = assembled("\t.text\n\tnotrack jmp\t*%rax\n\tnotrack jmp *%r8\n\tleave\n");
2663 assert_eq!(bytes(&assembled, ".text"), [0x3E, 0xFF, 0xE0, 0x3E, 0x41, 0xFF, 0xE0, 0xC9]);
2664 }
2665
2666 #[test]
2667 fn rep_bsf_is_read_as_tzcnt() {
2668 let assembled = assembled("\t.text\n\trep bsfq\t-8(%rbp), %rax\n\trep bsfl %edi, %eax\n");
2669 assert_eq!(
2670 bytes(&assembled, ".text"),
2671 [0xF3, 0x48, 0x0F, 0xBC, 0x45, 0xF8, 0xF3, 0x0F, 0xBC, 0xC7]
2672 );
2673 }
2674
2675 #[test]
2681 fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
2682 let out =
2683 assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
2684 let text = bytes(&out, ".text");
2685 assert_eq!(text, vec![0x90, 0xeb, 0xfd, 0x90, 0xeb, 0x01, 0x90, 0xc3]);
2688 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2689 let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2691 assert_eq!(written, vec!["foo"]);
2692 }
2693
2694 #[test]
2695 fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
2696 let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
2697 assert!(back.why.contains("none above it"), "{}", back.why);
2698 let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
2699 assert!(forward.why.contains("none below it"), "{}", forward.why);
2700 }
2701
2702 #[test]
2709 fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
2710 let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
2711 assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
2712 let split = assembled("\t.text\n\trep\n\tmovsq\n");
2713 assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
2714 let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
2715 assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
2716 }
2717
2718 #[test]
2725 fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
2726 let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
2727 let relocs = &out.parts[0].relocs;
2728 assert_eq!(relocs.len(), 1);
2729 assert_eq!(relocs[0].symbol, "table");
2730 assert_eq!(relocs[0].kind, Reference::Got);
2731 assert_eq!(relocs[0].addend, -4);
2734 let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
2735 assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
2736 }
2737
2738 #[test]
2742 fn only_an_instruction_the_linker_can_rewrite_asks_it_to() {
2743 for (line, kind) in [
2744 ("movq f@GOTPCREL(%rip), %rax", Reference::Got),
2745 ("cmpq f@GOTPCREL(%rip), %rdx", Reference::Got),
2746 ("addq f@GOTPCREL(%rip), %rdx", Reference::Got),
2747 ("movl f@GOTPCREL(%rip), %eax", Reference::GotBare),
2748 ("call *f@GOTPCREL(%rip)", Reference::GotBare),
2749 ("jmp *f@GOTPCREL(%rip)", Reference::GotBare),
2750 ("movq f@GOTPCREL(%rip), %xmm0", Reference::GotKept),
2751 ("movhps f@GOTPCREL(%rip), %xmm0", Reference::GotKept),
2752 ("movq %rax, f@GOTPCREL(%rip)", Reference::GotKept),
2753 ("movw f@GOTPCREL(%rip), %ax", Reference::GotKept),
2754 ] {
2755 let out = assembled(&format!("\t.text\n\t{line}\n"));
2756 assert_eq!(out.parts[0].relocs[0].kind, kind, "{line}");
2757 }
2758 }
2759
2760 #[test]
2766 fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
2767 let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
2768 let relocs = &out.parts[0].relocs;
2769 assert_eq!(relocs.len(), 1);
2770 assert_eq!(relocs[0].symbol, "table");
2771 assert_eq!(relocs[0].addend, -516);
2772 let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2775 assert_eq!(named, ["table"]);
2776 }
2777
2778 #[test]
2779 fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
2780 refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
2783 }
2784
2785 #[test]
2786 fn the_probe_gmp_writes() {
2787 let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
2791 assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
2792 let foo = name(&out, "foo");
2793 assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
2794 assert_eq!(foo.binding, Binding::Global);
2795 }
2796
2797 #[test]
2798 fn every_width_of_number_is_the_bytes_it_says_it_is() {
2799 let out = assembled(
2800 "\t.data\n\t.byte 1\n\t.short 2\n\t.long 3\n\t.quad 4\n\t.byte 0x7f, 0377, 'a', '\\n'\n",
2801 );
2802 let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
2803 want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
2804 assert_eq!(bytes(&out, ".data"), want);
2805 }
2806
2807 #[test]
2808 fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
2809 let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
2812 assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
2813 }
2814
2815 #[test]
2816 fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
2817 let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
2818 assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
2819 }
2820
2821 #[test]
2822 fn space_and_fill_put_that_many_bytes_there() {
2823 let out = assembled("\t.data\n\t.byte 1\n\t.zero 3\n\t.space 2, 0x41\n\t.fill 2, 1, 7\n");
2824 assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
2825 }
2826
2827 #[test]
2828 fn aligning_moves_on_to_the_boundary_and_no_further() {
2829 let out = assembled("\t.data\n\t.byte 1\n\t.align 8\n\t.byte 2\n\t.p2align 4\n\t.byte 3\n");
2832 let data = bytes(&out, ".data");
2833 assert_eq!(data.len(), 17);
2834 assert_eq!(data[0], 1);
2835 assert_eq!(data[8], 2);
2836 assert_eq!(data[16], 3);
2837 assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
2838 }
2839
2840 #[test]
2841 fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
2842 let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
2843 let part = &out.parts[0];
2844 assert_eq!(part.name, ".bss");
2845 assert_eq!(part.size, 4096);
2846 assert!(part.bytes.is_empty(), "the zeroes were carried after all");
2847 assert!(!part.shape.bits);
2848 }
2849
2850 #[test]
2851 fn what_a_section_directive_said_about_a_section_is_what_it_is() {
2852 let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
2853 let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
2854 assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
2855 assert!(!part.shape.write, "nothing said it was writable");
2856 }
2857
2858 #[test]
2859 fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
2860 let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
2861 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2862 assert_eq!(bytes(&out, ".text"), vec![0x90]);
2863 }
2864
2865 #[test]
2866 fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
2867 let out = assembled(
2868 "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
2869 );
2870 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2871 assert_eq!(bytes(&out, ".rodata"), vec![9]);
2872 }
2873
2874 #[test]
2875 fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
2876 let out = assembled(
2879 "\t.text\n\t.globl f\n\t.type f, @function\nf:\n\t.byte 0,0,0,0,0\n\t.size f, .-f\n",
2880 );
2881 let f = name(&out, "f");
2882 assert_eq!(f.size, 5);
2883 assert_eq!(f.sort, Sort::Func);
2884 }
2885
2886 #[test]
2887 fn a_set_may_name_something_further_down_the_file() {
2888 let out = assembled(
2891 "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
2892 table_end - table\n",
2893 );
2894 assert_eq!(name(&out, "width").at, Held::Absolute(12));
2895 }
2896
2897 #[test]
2898 fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
2899 let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
2900 assert_eq!(name(&out, "a").at, Held::Absolute(11));
2901 assert_eq!(name(&out, "b").at, Held::Absolute(10));
2902 }
2903
2904 #[test]
2905 fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
2906 let why = refused("\t.set a, b\n\t.set b, a\n");
2907 assert!(why.why.contains("neither has a value"), "{why}");
2908 }
2909
2910 #[test]
2911 fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
2912 let out = assembled("\t.data\n\t.quad message\n");
2913 let reloc = &out.parts[0].relocs[0];
2914 assert_eq!(reloc.at, 0);
2915 assert_eq!(reloc.symbol, "message");
2916 assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
2917 assert_eq!(reloc.addend, 0);
2918 assert_eq!(name(&out, "message").at, Held::Undefined);
2919 }
2920
2921 #[test]
2922 fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
2923 let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
2926 let reloc = &out.parts[0].relocs[0];
2927 assert_eq!(reloc.at, 8);
2928 assert_eq!(reloc.symbol, "message");
2929 assert_eq!(reloc.kind, Reference::Data);
2930 assert_eq!(reloc.addend, 0);
2931 }
2932
2933 #[test]
2934 fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
2935 let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
2940 let reloc = &out.parts[0].relocs[0];
2941 assert_eq!(reloc.at, 8);
2942 assert_eq!(reloc.kind, Reference::Data);
2943 assert_eq!(reloc.addend, 8);
2944 }
2945
2946 #[test]
2947 fn a_number_added_to_a_name_rides_along_in_the_addend() {
2948 let out = assembled("\t.data\n\t.quad message + 16\n");
2949 assert_eq!(out.parts[0].relocs[0].addend, 16);
2950 }
2951
2952 #[test]
2953 fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
2954 let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
2955 assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
2956 assert_eq!(name(&out, "shared").binding, Binding::Global);
2957 assert_eq!(name(&out, "mine").binding, Binding::Local);
2960 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2961 }
2962
2963 #[test]
2964 fn comm_of_a_name_said_to_be_local_is_room_here_as_lcomm_is() {
2965 let out = assembled("\t.local mine\n\t.comm mine, 8, 8\n");
2966 assert_eq!(name(&out, "mine").binding, Binding::Local);
2967 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2968 }
2969
2970 #[test]
2971 fn what_a_file_says_about_who_can_see_a_name_is_kept() {
2972 let out = assembled(
2973 "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
2974 inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
2975 );
2976 assert_eq!(name(&out, "seen").binding, Binding::Global);
2977 assert_eq!(name(&out, "maybe").binding, Binding::Weak);
2978 assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
2979 }
2980
2981 #[test]
2982 fn the_name_of_the_file_is_a_symbol_of_its_own() {
2983 let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
2986 assert_eq!(out.names[0].name, "big.s");
2987 assert_eq!(out.names[0].sort, Sort::File);
2988 assert_eq!(out.names[0].binding, Binding::Local);
2989 assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
2990 }
2991
2992 #[test]
2993 fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
2994 let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
2997 assert!(out.names.is_empty(), "{:?}", out.names);
2998 }
2999
3000 #[test]
3001 fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
3002 let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\tpopcnt %rax, %rdx\n\tret\n");
3006 assert_eq!(why.line, 4);
3007 assert!(why.why.contains("popcnt"), "{why}");
3008 }
3009
3010 #[test]
3011 fn a_function_of_instructions_is_its_bytes_and_its_size() {
3012 let out = assembled(
3015 "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
3016 id, .-id\n",
3017 );
3018 assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
3019 assert_eq!(name(&out, "id").size, 4);
3020 assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
3021 }
3022
3023 #[test]
3024 fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
3025 let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
3029 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0, 0xc3]);
3030 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
3031 }
3032
3033 #[test]
3034 fn a_jump_backwards_is_the_negative_distance_to_it() {
3035 let out = assembled("\t.text\nagain:\n\tjmp again\n");
3036 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0xfe]);
3037 }
3038
3039 #[test]
3040 fn a_branch_is_as_short_as_the_distance_lets_it_be() {
3041 let out = assembled("\tjne far\n\t.zero 127\nfar:\n\tret\n");
3044 assert_eq!(bytes(&out, ".text")[..2], [0x75, 127]);
3045 let out = assembled("\tjne far\n\t.zero 128\nfar:\n\tret\n");
3046 assert_eq!(bytes(&out, ".text")[..6], [0x0f, 0x85, 128, 0, 0, 0]);
3047 let out = assembled("back:\n\t.zero 126\n\tjmp back\n");
3048 assert_eq!(bytes(&out, ".text")[126..], [0xeb, 0x80]);
3049 let out = assembled("back:\n\t.zero 127\n\tjmp back\n");
3050 assert_eq!(bytes(&out, ".text")[127..], [0xe9, 0x7c, 0xff, 0xff, 0xff]);
3051 }
3052
3053 #[test]
3054 fn a_branch_made_long_can_push_another_one_out_of_reach() {
3055 let out = assembled("\tjmp a\n\t.zero 125\n\tjmp b\na:\n\t.zero 128\nb:\n\tret\n");
3059 let text = bytes(&out, ".text");
3060 assert_eq!(text[..5], [0xe9, 130, 0, 0, 0]);
3061 assert_eq!(text[130..135], [0xe9, 128, 0, 0, 0]);
3062 }
3063
3064 #[test]
3065 fn a_jump_past_an_alignment_is_judged_the_way_gas_judges_it() {
3066 let out = assembled(
3073 "\tjmp far1\n\tje far1\n\tje far2\n\t.zero 123\n\t.p2align 3\nfar2:\n\tret\n\t.zero \
3074 200\nfar1:\n\tret\n",
3075 );
3076 let text = bytes(&out, ".text");
3077 assert_eq!(text[..13], [0xe9, 0x4c, 1, 0, 0, 0x0f, 0x84, 0x46, 1, 0, 0, 0x74, 123]);
3078 assert_eq!(text.len(), 0x152);
3079 }
3080
3081 #[test]
3082 fn a_branch_that_leaves_the_section_or_goes_to_a_weak_name_is_long() {
3083 let out = assembled("\tjmp elsewhere\n\tjz maybe\n\t.weak maybe\nmaybe:\n\tret\n");
3085 assert_eq!(bytes(&out, ".text")[..1], [0xe9]);
3086 assert_eq!(bytes(&out, ".text")[5..7], [0x0f, 0x84]);
3087 }
3088
3089 #[test]
3090 fn a_section_of_constants_says_how_long_each_one_is() {
3091 let out = assembled(
3092 "\t.section .rodata.str1.1,\"aMS\",@progbits,1\n\t.string \"hi\"\n\t\
3093 .section .rodata.cst8,\"aM\",@progbits,8\n\t.quad 1\n\t.section .rodata.x,\"aM\"\n\t.byte 1\n",
3094 );
3095 let shapes: Vec<_> =
3096 out.parts.iter().map(|part| (part.shape.merge, part.shape.strings)).collect();
3097 assert_eq!(shapes, [(1, true), (8, false), (0, false)]);
3098 }
3099
3100 #[test]
3101 fn a_global_name_defined_here_is_still_left_to_the_linker() {
3102 let out = assembled(
3106 "\t.globl f\nf:\n\tcall f\n\tjmp f\n\tleaq f(%rip), %rax\n\tcall g\n\t\
3107 .long f - g\ng:\n\tret\n",
3108 );
3109 let relocs = &out.parts[0].relocs;
3110 let kinds: Vec<_> = relocs.iter().map(|r| (r.at, r.symbol.as_str(), r.kind)).collect();
3111 assert_eq!(kinds, [(1, "f", Reference::Call), (10, "f", Reference::Data)]);
3112 assert!(relocs.iter().all(|r| r.addend == -4));
3113 let text = bytes(&out, ".text");
3114 assert_eq!(text[..7], [0xe8, 0, 0, 0, 0, 0xeb, 0xf9]);
3115 assert_eq!(text[14..19], [0xe8, 4, 0, 0, 0]);
3116 assert_eq!(text[19..23], (-23i32).to_le_bytes());
3117 }
3118
3119 #[test]
3120 fn a_call_to_a_static_name_in_another_section_needs_no_stub() {
3121 let out = assembled("\t.text\n\tcall cold\n\t.section .text.unlikely\ncold:\n\tret\n");
3122 let reloc = &out.parts[0].relocs[0];
3123 assert_eq!((reloc.symbol.as_str(), reloc.kind), ("cold", Reference::Data));
3124 }
3125
3126 #[test]
3127 fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
3128 let out = assembled("\t.text\n\tcall puts\n");
3134 let reloc = &out.parts[0].relocs[0];
3135 assert_eq!(reloc.at, 1);
3136 assert_eq!(reloc.symbol, "puts");
3137 assert_eq!(reloc.kind, Reference::Call);
3138 assert_eq!(reloc.addend, -4);
3139 }
3140
3141 #[test]
3142 fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
3143 let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
3144 let reloc = &out.parts[0].relocs[0];
3145 assert_eq!(reloc.symbol, "message");
3146 assert_eq!(reloc.kind, Reference::Data);
3147 assert_eq!(reloc.at, 3);
3149 assert_eq!(reloc.addend, -4);
3150 }
3151
3152 #[test]
3153 fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
3154 let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
3157 assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
3158 }
3159
3160 #[test]
3161 fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
3162 let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
3166 assert_eq!(why.line, 2);
3167 assert!(why.why.contains("does not reach"), "{why}");
3168 }
3169
3170 #[test]
3171 fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
3172 let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
3177 assert_eq!(bytes(&out, ".data")[200], 200);
3178 let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
3179 assert!(why.why.contains("does not reach"), "{why}");
3180 }
3181
3182 #[test]
3183 fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
3184 let why = refused("\t.bss\n\tret\n");
3185 assert!(why.why.contains("holds no bytes"), "{why}");
3186 }
3187
3188 #[test]
3189 fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
3190 let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
3191 assert_eq!(why.line, 3);
3192 assert!(why.why.contains(".reloc"), "{why}");
3193 }
3194
3195 #[test]
3196 fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
3197 let out = assembled(
3200 "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
3201 3\n",
3202 );
3203 assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
3204 }
3205
3206 #[test]
3207 fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
3208 let why = refused("\t.data\n\t/* and then nothing\n");
3209 assert!(why.why.contains("never closed"), "{why}");
3210 }
3211
3212 #[test]
3213 fn a_string_with_a_comment_character_in_it_is_a_string() {
3214 let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
3215 assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
3216 }
3217
3218 #[test]
3219 fn several_statements_on_one_line_are_several_statements() {
3220 let out = assembled("\t.data; .byte 1; .byte 2\n");
3221 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
3222 }
3223
3224 #[test]
3225 fn a_section_nothing_was_ever_put_in_is_dropped() {
3226 let out = assembled("\t.data\n\t.byte 1\n");
3229 assert_eq!(out.parts.len(), 1);
3230 assert_eq!(out.parts[0].name, ".data");
3231 }
3232
3233 #[test]
3234 fn a_section_with_nothing_in_it_but_a_name_is_kept() {
3235 let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
3238 assert_eq!(out.parts.len(), 1);
3239 assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
3240 }
3241
3242 #[test]
3243 fn an_error_directive_is_the_file_saying_it_refuses_itself() {
3244 let why = refused("\t.error \"this is not the machine for it\"\n");
3245 assert!(why.why.contains("not the machine for it"), "{why}");
3246 }
3247
3248 #[test]
3249 fn a_jump_counted_from_itself_is_the_short_one_gas_writes() {
3250 let out = assembled("\tjmp .+6\n\t.int 123\n\tmov .-4(%rip), %eax\n");
3253 assert_eq!(
3254 bytes(&out, ".text"),
3255 vec![0xeb, 0x04, 123, 0, 0, 0, 0x8b, 0x05, 0xf6, 0xff, 0xff, 0xff]
3256 );
3257 }
3258
3259 #[test]
3260 fn a_numbered_label_in_an_expression_is_a_place() {
3261 let out =
3262 assembled("2:\n\tjmp .+6\n1:\n\t.pushsection .data\n\t.long 1b - 2b\n\t.popsection\n");
3263 assert_eq!(bytes(&out, ".data"), vec![2, 0, 0, 0]);
3264 let out = assembled("\t.data\n\t.byte 0b101\n");
3266 assert_eq!(bytes(&out, ".data"), vec![5]);
3267 }
3268
3269 #[test]
3270 fn a_number_an_instruction_carries_may_be_an_expression_over_labels() {
3271 let out = assembled("3:\tmov $4f-3b, %eax\n4:\n");
3272 assert_eq!(bytes(&out, ".text"), vec![0xb8, 5, 0, 0, 0]);
3273 }
3274
3275 #[test]
3276 fn a_number_an_instruction_carries_may_not_name_something_elsewhere() {
3277 let why = refused("\tmov $elsewhere, %eax\n");
3278 assert!(why.why.contains("relocation"), "{why}");
3279 }
3280
3281 #[test]
3282 fn a_name_set_twice_means_what_it_was_where_it_is_used() {
3283 let out = assembled(
3284 "\t.data\n\t.byte early\n\tearly = 3\n\tx = 1\n\t.byte x\n\tx = x + 1\n\t.byte x\n",
3285 );
3286 assert_eq!(bytes(&out, ".data"), vec![3, 1, 2]);
3287 }
3288
3289 #[test]
3290 fn a_place_set_twice_and_reached_from_another_section_is_relocated_against() {
3291 let out = assembled(
3292 "\t.data\n\tx = .\n\t.int 1\n\tx = .\n\t.int 2\n\t.text\n\tmov x(%rip), %eax\n",
3293 );
3294 let reloc = &out.parts.iter().find(|part| part.name == ".text").unwrap().relocs[0];
3295 let target = name(&out, &reloc.symbol);
3296 let data = out.parts.iter().position(|part| part.name == ".data").unwrap();
3297 assert_eq!(target.at, Held::In { part: data, offset: 4 });
3298 }
3299
3300 #[test]
3301 fn frame_rules_are_an_unwind_table_pointing_at_the_function() {
3302 let out = assembled(
3303 "f:\n\t.cfi_startproc\n\tpush %rbp\n\t.cfi_def_cfa_offset 16\n\t.cfi_offset %rbp, \
3304 -16\n\tpop %rbp\n\t.cfi_def_cfa_offset 8\n\tret\n\t.cfi_endproc\n",
3305 );
3306 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3307 let rows = [0x41, 0x0e, 0x10, 0x86, 0x02, 0x41, 0x0e, 0x08];
3310 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3311 let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
3312 let text = out.parts.iter().position(|part| part.name == ".text").unwrap();
3313 assert_eq!(name(&out, &reloc.symbol).at, Held::In { part: text, offset: 0 });
3314 }
3315
3316 #[test]
3317 fn a_frame_rule_relative_to_the_register_is_the_same_slot() {
3318 let out = assembled(
3319 "\t.cfi_startproc\n\tpush %rbx\n\t.cfi_adjust_cfa_offset 8\n\t.cfi_rel_offset \
3320 %rbx, 0\n\t.cfi_endproc\n",
3321 );
3322 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3323 let rows = [0x41, 0x0e, 0x10, 0x83, 0x02];
3324 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3325 }
3326
3327 #[test]
3328 fn frame_rules_for_a_debugger_only_are_no_unwind_table() {
3329 let out =
3330 assembled("\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\tret\n\t.cfi_endproc\n");
3331 assert!(out.parts.iter().all(|part| part.name != ".eh_frame"));
3332 }
3333
3334 #[test]
3335 fn a_frame_rule_outside_a_function_or_a_function_never_ended_is_refused() {
3336 let why = refused("\t.cfi_def_cfa_offset 16\n");
3337 assert!(why.why.contains("outside"), "{why}");
3338 let why = refused("\t.cfi_startproc\n\tret\n");
3339 assert!(why.why.contains("never ended"), "{why}");
3340 }
3341}