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 read_as(text, arch, ObjectFormat::Elf)
79}
80
81pub fn read_as(text: &str, arch: Arch, format: ObjectFormat) -> Result<Assembled, Trouble> {
92 let mut long = std::collections::HashSet::new();
97 loop {
98 let aarch64 = arch == Arch::Aarch64;
99 let macho = format == ObjectFormat::MachO;
100 let mut reader = Reader { long: long.clone(), aarch64, macho, ..Reader::default() };
101 reader.run(text)?;
102 match reader.finish()? {
103 Ok(done) => return Ok(done),
104 Err(grow) => long.extend(grow),
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
115struct Sym {
116 name: String,
117 at: Held,
118 size: u64,
119 sort: Sort,
120 binding: Binding,
121 visibility: Visibility,
122 numbered: bool,
125}
126
127#[derive(Debug, Clone)]
129struct Fixup {
130 part: usize,
131 at: u64,
132 width: u8,
133 sum: Sum,
134 reach: Reach,
138 slot: Reference,
141 branch: Option<usize>,
144 jump: bool,
147 field: Option<aarch64::Fixup>,
150 line: usize,
151}
152
153#[derive(Debug, Clone, Copy)]
155struct Aligned {
156 part: usize,
157 at: u64,
159 boundary: u64,
160 most: Option<u64>,
162 need: u64,
164}
165
166#[derive(Debug)]
168struct Frame {
169 part: usize,
170 start: u64,
171 len: u64,
172 sym: usize,
175 rows: crate::unwind::Rows,
176 cfa: i32,
179 remembered: Vec<i32>,
181}
182
183#[derive(Debug, Default)]
185struct Reader {
186 parts: Vec<Part>,
187 named: HashMap<String, usize>,
189 here: usize,
191 stack: Vec<usize>,
193 before: Option<usize>,
195 syms: Vec<Sym>,
196 known: HashMap<String, usize>,
197 counts: HashMap<String, usize>,
200 labelled: std::collections::HashSet<usize>,
203 fixups: Vec<Fixup>,
204 sets: Vec<(usize, Sum, usize)>,
206 sizes: Vec<(usize, Sum, usize)>,
208 current: HashMap<String, String>,
212 relocated: std::collections::HashSet<usize>,
216 said_local: std::collections::HashSet<usize>,
220 frame: Option<Frame>,
222 frames: Vec<Frame>,
224 no_unwind: bool,
227 files: Vec<String>,
231 long: std::collections::HashSet<usize>,
233 branches: usize,
235 aligns: Vec<Aligned>,
237 aarch64: bool,
239 macho: bool,
241 subsections: bool,
244 line: usize,
245}
246
247impl Reader {
248 fn run(&mut self, text: &str) -> Result<(), Trouble> {
250 if self.macho {
253 self.apple_section("__TEXT", "__text", None, &[])?;
254 } else {
255 self.section(".text", Shape::of(".text"));
256 }
257 let mut commenting = false;
258 for (index, raw) in text.lines().enumerate() {
259 self.line = index + 1;
260 let line = self.strip(raw, &mut commenting)?;
261 for statement in split(&line, ';') {
262 self.statement(statement.trim())?;
263 }
264 }
265 if commenting {
266 return Err(self.bad("a block comment was opened and never closed"));
267 }
268 Ok(())
269 }
270
271 fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
278 let mut out = String::with_capacity(raw.len());
279 let bytes = raw.as_bytes();
280 let mut i = 0;
281 let mut quote = None;
282 while i < bytes.len() {
283 let rest = &raw[i..];
284 if *commenting {
285 if let Some(end) = rest.find("*/") {
286 *commenting = false;
287 out.push(' ');
290 i += end + 2;
291 } else {
292 return Ok(out);
293 }
294 continue;
295 }
296 let ch = bytes[i] as char;
297 if let Some(mark) = quote {
298 out.push(ch);
299 if ch == '\\' && i + 1 < bytes.len() {
300 out.push(bytes[i + 1] as char);
301 i += 2;
302 continue;
303 }
304 if ch == mark {
305 quote = None;
306 }
307 i += 1;
308 continue;
309 }
310 if ch == '"' {
311 quote = Some('"');
312 out.push(ch);
313 i += 1;
314 continue;
315 }
316 if rest.starts_with("/*") {
317 *commenting = true;
318 i += 2;
319 continue;
320 }
321 let marker = ch == '#' && (!self.aarch64 || out.trim().is_empty());
324 if rest.starts_with("//") || marker {
325 return Ok(out);
326 }
327 out.push(ch);
328 i += 1;
329 }
330 if quote.is_some() {
331 return Err(self.bad("a string was opened and the line ended before it closed"));
332 }
333 Ok(out)
334 }
335
336 fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
338 loop {
339 text = text.trim_start();
340 let Some(name) = labelled(text) else { break };
341 self.label(&name)?;
342 text = &text[name.len() + 1..];
343 }
344 let text = text.trim();
345 if text.is_empty() {
346 return Ok(());
347 }
348 let (word, rest) = match text.find(char::is_whitespace) {
349 Some(cut) => (&text[..cut], text[cut..].trim()),
350 None => (text, ""),
351 };
352 if let Some((name, what)) = assigned(text) {
353 return self.assign(name, what);
354 }
355 if let Some(directive) = word.strip_prefix('.') {
356 return self.directive(directive, rest);
357 }
358 if self.aarch64 {
359 return self.a64(text);
360 }
361 if let Some((word, rest)) = repeated(word, rest) {
362 return self.instruction(&word, rest);
363 }
364 self.instruction(word, rest)
365 }
366
367 fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
381 let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
382 let mut written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
383 let mut branch = None;
385 let short = crate::instruction::short(&written).filter(|_| written.holes[0].name != ".");
386 let jump = short.is_some();
387 if let Some(short) = short {
388 if !self.long.contains(&self.branches) {
389 branch = Some(self.branches);
390 written = short;
391 }
392 self.branches += 1;
393 }
394 let part = self.here;
395 let at = self.at();
396 self.put(&written.bytes)?;
397 let end = at + written.bytes.len() as u64;
398 let slot = crate::bytes::slot(&written.bytes);
399 for hole in written.holes {
400 let here = (part, at as i64);
403 let sum = if hole.sort == Reach::Value {
404 self.expression_at(&hole.name, here)?
406 } else {
407 let what = if hole.name == "." {
408 What::Here { part, at: here.1 }
409 } else {
410 let name = self.named(&hole.name)?;
413 self.sym(&name);
414 What::Symbol(name)
415 };
416 Sum {
417 constant: hole.addend,
418 terms: vec![
419 Term { coeff: 1, what },
420 Term { coeff: -1, what: What::Here { part, at: end as i64 } },
421 ],
422 }
423 };
424 self.fixups.push(Fixup {
425 part,
426 at: at + hole.at as u64,
427 width: hole.width,
428 sum,
429 reach: hole.sort,
430 slot,
431 branch,
432 jump,
433 field: None,
434 line: self.line,
435 });
436 }
437 Ok(())
438 }
439
440 fn a64(&mut self, text: &str) -> Result<(), Trouble> {
448 let line = aarch64::read(text).map_err(|why| self.bad(&why.to_string()))?;
449 let encoded = aarch64::encode(&line.mnemonic, &line.values)
450 .map_err(|why| self.bad(&why.to_string()))?;
451 let (part, at) = (self.here, self.at());
452 self.put(&encoded.word.to_le_bytes())?;
453 let (Some(field), Some(name)) = (encoded.fixup, line.symbol) else {
454 return Ok(());
455 };
456 let what = if name == "." {
459 What::Here { part, at: at as i64 }
460 } else {
461 let name = self.named(&name)?;
462 self.sym(&name);
463 What::Symbol(name)
464 };
465 let mut terms = vec![Term { coeff: 1, what }];
466 if relative(field) {
467 terms.push(Term { coeff: -1, what: What::Here { part, at: at as i64 } });
468 }
469 let jump = matches!(
470 field,
471 aarch64::Fixup::Jump26 | aarch64::Fixup::CondBr19 | aarch64::Fixup::TestBr14
472 );
473 self.fixups.push(Fixup {
474 part,
475 at,
476 width: 4,
477 sum: Sum { constant: line.addend, terms },
478 reach: Reach::Near,
479 slot: Reference::Data,
480 branch: None,
481 jump,
482 field: Some(field),
483 line: self.line,
484 });
485 Ok(())
486 }
487
488 fn label(&mut self, name: &str) -> Result<(), Trouble> {
490 let at = self.at();
491 let part = self.here;
492 let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
495 let held = if numbered {
496 let count = self.counts.entry(name.to_owned()).or_insert(0);
497 *count += 1;
498 counted(name, *count)
499 } else {
500 name.to_owned()
501 };
502 let sym = self.sym(&held);
503 if self.syms[sym].at != Held::Undefined {
504 let what = format!("'{name}' is defined twice");
505 return Err(self.bad(&what));
506 }
507 self.syms[sym].at = Held::In { part, offset: at };
508 self.labelled.insert(part);
509 Ok(())
510 }
511
512 fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
519 let Some(number) = word.strip_suffix(['b', 'f']) else {
520 return Ok(None);
521 };
522 if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
523 return Ok(None);
524 }
525 let count = self.counts.get(number).copied().unwrap_or(0);
526 if word.ends_with('b') {
527 if count == 0 {
528 let what =
529 format!("'{word}' goes back to a '{number}:' and there is none above it");
530 return Err(self.bad(&what));
531 }
532 return Ok(Some(counted(number, count)));
533 }
534 Ok(Some(counted(number, count + 1)))
535 }
536
537 fn named(&self, word: &str) -> Result<String, Trouble> {
542 if let Some(place) = self.numbered(word)? {
543 return Ok(place);
544 }
545 Ok(self.current.get(word).cloned().unwrap_or_else(|| word.to_owned()))
546 }
547
548 fn assign(&mut self, name: &str, what: &str) -> Result<(), Trouble> {
556 let sum = self.expression(what)?;
557 let held = match self.current.get(name) {
558 Some(_) => format!("{name}\u{1}={}", self.syms.len()),
559 None => name.to_owned(),
560 };
561 let sym = self.sym(&held);
562 if self.syms[sym].at != Held::Undefined {
563 let what = format!("'{name}' is defined twice");
564 return Err(self.bad(&what));
565 }
566 self.current.insert(name.to_owned(), held);
567 self.sets.push((sym, sum, self.line));
568 Ok(())
569 }
570
571 fn cfi(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
580 match word {
581 "cfi_startproc" => {
582 if self.frame.is_some() {
583 return Err(self.bad("a '.cfi_startproc' inside another one"));
584 }
585 let sym = self.sym(&format!("\u{1}frame{}", self.frames.len()));
586 let (part, start) = (self.here, self.at());
587 self.syms[sym].at = Held::In { part, offset: start };
588 let frame = Frame {
591 part,
592 start,
593 len: 0,
594 sym,
595 rows: Vec::new(),
596 cfa: if self.aarch64 { 0 } else { 8 },
597 remembered: Vec::new(),
598 };
599 self.frame = Some(frame);
600 return Ok(());
601 }
602 "cfi_sections" => {
603 self.no_unwind = !args.iter().any(|arg| arg.trim() == ".eh_frame");
604 return Ok(());
605 }
606 "cfi_endproc"
607 | "cfi_def_cfa"
608 | "cfi_def_cfa_offset"
609 | "cfi_adjust_cfa_offset"
610 | "cfi_def_cfa_register"
611 | "cfi_offset"
612 | "cfi_rel_offset"
613 | "cfi_restore"
614 | "cfi_remember_state"
615 | "cfi_restore_state" => {}
616 _ => return Ok(()),
617 }
618 let (here, at) = (self.here, self.at());
619 let line = self.line;
620 let bad = |why: &str| Trouble { line, why: why.to_owned() };
621 let Some(mut frame) = self.frame.take() else {
622 return Err(bad("a frame rule outside '.cfi_startproc' and '.cfi_endproc'"));
623 };
624 if frame.part != here {
625 return Err(bad("a frame rule in another section from the function it is about"));
626 }
627 let op = match word {
628 "cfi_endproc" => {
629 frame.len = at - frame.start;
630 self.frames.push(frame);
631 return Ok(());
632 }
633 "cfi_def_cfa" => {
634 let [reg, offset] = self.two(args, ".cfi_def_cfa")?;
635 frame.cfa = self.distance(&offset)?;
636 CfiOp::DefCfa { reg: self.dwarf(®)?, offset: frame.cfa }
637 }
638 "cfi_def_cfa_offset" | "cfi_adjust_cfa_offset" => {
639 let by = self.distance(args.first().map_or("", |arg| arg.as_str()))?;
640 frame.cfa = if word == "cfi_def_cfa_offset" { by } else { frame.cfa + by };
641 CfiOp::DefCfaOffset(frame.cfa)
642 }
643 "cfi_def_cfa_register" => {
644 CfiOp::DefCfaRegister(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
645 }
646 "cfi_offset" | "cfi_rel_offset" => {
647 let [reg, offset] = self.two(args, &format!(".{word}"))?;
648 let mut offset = self.distance(&offset)?;
649 if word == "cfi_rel_offset" {
652 offset -= frame.cfa;
653 }
654 if offset >= 0 || offset % 8 != 0 {
655 return Err(bad(
656 "a register saved somewhere that is not a whole slot below the end of the \
657 frame, which is the only place this writes a rule for",
658 ));
659 }
660 CfiOp::Offset { reg: self.dwarf(®)?, offset }
661 }
662 "cfi_restore" => {
663 CfiOp::Restore(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
664 }
665 "cfi_remember_state" => {
666 frame.remembered.push(frame.cfa);
667 CfiOp::RememberState
668 }
669 "cfi_restore_state" => {
670 frame.cfa = frame.remembered.pop().ok_or_else(|| {
671 bad("a '.cfi_restore_state' with nothing remembered to restore")
672 })?;
673 CfiOp::RestoreState
674 }
675 _ => unreachable!("every other word returned above"),
676 };
677 frame.rows.push(((at - frame.start) as usize, op));
678 self.frame = Some(frame);
679 Ok(())
680 }
681
682 fn distance(&mut self, text: &str) -> Result<i32, Trouble> {
684 let value = self.number(text)?;
685 i32::try_from(value).map_err(|_| self.bad(&format!("{value} is not a distance in a frame")))
686 }
687
688 fn dwarf(&self, text: &str) -> Result<u16, Trouble> {
690 let text = text.trim();
691 if let Ok(number) = text.parse::<u16>() {
692 return Ok(number);
693 }
694 if self.aarch64 {
695 return aarch64_dwarf(text).ok_or_else(|| {
696 self.bad(&format!("'{text}' is not a register a frame rule can name"))
697 });
698 }
699 let name = text.strip_prefix('%').unwrap_or(text);
700 if name == "rip" {
701 return Ok(SYSV.dwarf_return_address);
702 }
703 gpr_named(name)
704 .and_then(|(reg, _)| SYSV.dwarf(SYSV.int_class, reg))
705 .ok_or_else(|| self.bad(&format!("'{text}' is not a register a frame rule can name")))
706 }
707
708 #[allow(clippy::too_many_lines)]
710 fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
711 let args = split(rest, ',');
712 match word {
713 "text" | "data" | "bss" | "rodata" | "const" | "cstring" | "const_data"
714 if self.macho =>
715 {
716 self.apple_plain(word, rest)?;
717 }
718 "text" | "data" | "bss" | "rodata" => {
719 self.plain(word, rest)?;
720 }
721 "section" if self.macho => self.apple_section_directive(&args)?,
722 "section" => self.section_directive(&args)?,
723 "zerofill" if self.macho => self.zerofill(&args, false)?,
724 "tbss" if self.macho => self.zerofill(&args, true)?,
725 "subsections_via_symbols" if self.macho => self.subsections = true,
726 "private_extern" if self.macho => self.sight(&args, Visibility::Hidden)?,
727 "weak_definition" | "weak_reference" if self.macho => {
728 self.bind(&args, Binding::Weak)?;
729 }
730 "build_version"
733 | "macosx_version_min"
734 | "ios_version_min"
735 | "tvos_version_min"
736 | "watchos_version_min"
737 | "data_region"
738 | "end_data_region"
739 if self.macho => {}
740 "pushsection" => {
741 self.stack.push(self.here);
742 self.section_directive(&args)?;
743 }
744 "popsection" => {
745 let Some(back) = self.stack.pop() else {
746 return Err(self.bad(".popsection with nothing pushed"));
747 };
748 self.go(back);
749 }
750 "previous" => {
751 let Some(back) = self.before else {
752 return Err(self.bad(".previous with no section before this one"));
753 };
754 self.go(back);
755 }
756
757 "byte" => self.data(&args, 1)?,
758 "word" if self.aarch64 => self.data(&args, 4)?,
760 "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
761 "long" | "int" | "4byte" => self.data(&args, 4)?,
762 "quad" | "8byte" | "xword" | "dword" => self.data(&args, 8)?,
763 "octa" => self.octa(&args)?,
764
765 "ascii" => self.text_bytes(&args, false)?,
766 "asciz" | "string" => self.text_bytes(&args, true)?,
767
768 "space" | "skip" | "zero" => {
769 if args.is_empty() || args.len() > 2 {
770 return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
771 }
772 let size = self.number(&args[0])?;
773 let size = self.count(size)?;
774 let fill = match args.get(1) {
775 Some(arg) => self.byte(arg)?,
776 None => 0,
777 };
778 self.pad(size, fill)?;
779 }
780 "fill" => {
781 if args.is_empty() || args.len() > 3 {
785 return Err(self.bad(".fill wants a count and an optional width and value"));
786 }
787 let count = self.number(&args[0])?;
788 let count = self.count(count)?;
789 let width = match args.get(1) {
790 Some(arg) => {
791 let width = self.number(arg)?;
792 self.count(width)?
793 }
794 None => 1,
795 };
796 let value = match args.get(2) {
797 Some(arg) => self.number(arg)?,
798 None => 0,
799 };
800 if width > 8 {
801 return Err(self.bad(".fill of items wider than eight bytes is not written"));
802 }
803 let one = value.to_le_bytes();
804 for _ in 0..count {
805 self.put(&one[..width as usize])?;
806 }
807 }
808
809 "align" | "balign" | "p2align" => self.align(word, &args)?,
810 "org" => {
811 let Some(first) = args.first() else {
812 return Err(self.bad(".org with nothing after it"));
813 };
814 let to = self.number(first)?;
815 let to = self.count(to)?;
816 let fill = match args.get(1) {
817 Some(arg) => self.byte(arg)?,
818 None => 0,
819 };
820 let at = self.at();
821 if to < at {
822 let what = format!(".org back to {to} from {at}, which would overwrite bytes");
823 return Err(self.bad(&what));
824 }
825 self.pad(to - at, fill)?;
826 }
827
828 "globl" | "global" => self.bind(&args, Binding::Global)?,
829 "weak" => self.bind(&args, Binding::Weak)?,
830 "local" => {
831 self.bind(&args, Binding::Local)?;
832 for arg in &args {
833 let sym = self.sym(arg.trim());
834 self.said_local.insert(sym);
835 }
836 }
837 "hidden" => self.sight(&args, Visibility::Hidden)?,
838 "protected" => self.sight(&args, Visibility::Protected)?,
839 "internal" => self.sight(&args, Visibility::Hidden)?,
842
843 "type" => self.type_directive(&args)?,
844 "err" | "error" => {
845 let what = unquoted(args.first().map_or("", |arg| arg.trim()));
846 return Err(self.bad(&format!("the file says so itself: {what}")));
847 }
848 "size" => {
849 let [name, what] = self.two(&args, ".size")?;
850 let sum = self.expression(&what)?;
851 let sym = self.sym(&name);
852 self.sizes.push((sym, sum, self.line));
853 }
854 "set" | "equ" | "equiv" => {
855 let [name, what] = self.two(&args, &format!(".{word}"))?;
856 self.assign(&name, &what)?;
857 }
858 "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
859
860 "file" => {
864 let what = args.first().map_or("", |arg| arg.trim());
865 if what.starts_with('"') {
866 self.files.push(unquoted(what));
867 }
868 }
869
870 "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
874 | "intel_syntax" | "warning" => {}
875 _ if word.starts_with("cfi_") => self.cfi(word, &args)?,
876
877 _ => {
878 let what = format!(
879 "'.{word}' is a directive this compiler does not know, so nothing was written \
880 for it"
881 );
882 return Err(self.bad(&what));
883 }
884 }
885 Ok(())
886 }
887
888 fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
890 if !rest.trim().is_empty() && rest.trim() != "0" {
895 let what =
896 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
897 return Err(self.bad(&what));
898 }
899 let name = format!(".{word}");
900 let shape = Shape::of(&name);
901 self.section(&name, shape);
902 Ok(())
903 }
904
905 fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
907 let Some(name) = args.first() else {
908 return Err(self.bad(".section with no name"));
909 };
910 let name = unquoted(name.trim());
911 if name.is_empty() {
912 return Err(self.bad(".section with no name"));
913 }
914 let mut shape = Shape::of(&name);
917 let (mut merge, mut strings) = (false, false);
918 if let Some(flags) = args.get(1) {
919 let letters = unquoted(flags.trim());
920 shape = Shape { bits: true, ..Shape::default() };
921 for letter in letters.chars() {
922 match letter {
923 'a' => shape.alloc = true,
924 'w' => shape.write = true,
925 'x' => shape.exec = true,
926 'T' => shape.thread = true,
927 'M' => merge = true,
928 'S' => strings = true,
929 'G' | 'o' | 'e' | 'R' | 'd' => {}
933 _ => {
934 let what = format!("'{letter}' is not a section flag this compiler knows");
935 return Err(self.bad(&what));
936 }
937 }
938 }
939 let implied = Shape::implied(&name);
940 shape.alloc |= implied.alloc;
941 shape.write |= implied.write;
942 shape.exec |= implied.exec;
943 shape.thread |= implied.thread;
944 }
945 if let Some(kind) = args.get(2) {
946 let kind = kind.trim().trim_start_matches(['@', '%']);
947 let kind = unquoted(kind);
948 match kind.as_str() {
949 "progbits" => shape.bits = true,
950 "nobits" => shape.bits = false,
951 "init_array" => shape.array = Some(Array::Init),
952 "fini_array" => shape.array = Some(Array::Fini),
953 "preinit_array" => shape.array = Some(Array::Preinit),
954 "note" => shape.bits = true,
955 _ => {
956 let what = format!("'{kind}' is not a section type this compiler writes");
957 return Err(self.bad(&what));
958 }
959 }
960 }
961 if merge {
964 shape.merge = args.get(3).and_then(|entry| entry.trim().parse().ok()).unwrap_or(0);
965 shape.strings = strings;
966 }
967 self.section(&name, shape);
968 Ok(())
969 }
970
971 fn apple_plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
973 if !rest.trim().is_empty() && rest.trim() != "0" {
974 let what =
975 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
976 return Err(self.bad(&what));
977 }
978 let (segment, section) = match word {
979 "text" => ("__TEXT", "__text"),
980 "data" => ("__DATA", "__data"),
981 "bss" => ("__DATA", "__bss"),
982 "cstring" => ("__TEXT", "__cstring"),
983 "const_data" => ("__DATA", "__const"),
984 _ => ("__TEXT", "__const"),
985 };
986 self.apple_section(segment, section, None, &[])
987 }
988
989 fn apple_section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
991 let [segment, section, ..] = args else {
992 return Err(self.bad(".section on Mach-O wants a segment and a section"));
993 };
994 let kind = args.get(2).map(|kind| kind.trim()).filter(|kind| !kind.is_empty());
995 let attributes = args.get(3).map_or(String::new(), |list| list.trim().to_owned());
996 let attributes: Vec<&str> =
997 attributes.split('+').map(str::trim).filter(|word| !word.is_empty()).collect();
998 if args.len() > 4 {
1001 return Err(self.bad("a Mach-O section with a stub size is not written"));
1002 }
1003 self.apple_section(segment.trim(), section.trim(), kind, &attributes)
1004 }
1005
1006 fn apple_section(
1008 &mut self,
1009 segment: &str,
1010 section: &str,
1011 kind: Option<&str>,
1012 attributes: &[&str],
1013 ) -> Result<(), Trouble> {
1014 let shape =
1015 Shape::mach(segment, section, kind, attributes).map_err(|why| self.bad(&why))?;
1016 self.section(&format!("{segment},{section}"), shape);
1017 Ok(())
1018 }
1019
1020 fn zerofill(&mut self, args: &[String], thread: bool) -> Result<(), Trouble> {
1026 let (segment, section, rest) = if thread {
1027 ("__DATA".to_owned(), "__thread_bss".to_owned(), args)
1028 } else {
1029 let [segment, section, rest @ ..] = args else {
1030 return Err(self.bad(".zerofill wants a segment and a section"));
1031 };
1032 (segment.trim().to_owned(), section.trim().to_owned(), rest)
1033 };
1034 let (was, before) = (self.here, self.before);
1037 self.apple_section(&segment, §ion, None, &[])?;
1038 let at = self.here;
1039 (self.here, self.before) = (was, before);
1040 let Some(name) = rest.first() else {
1041 return Ok(());
1042 };
1043 if !(2..=3).contains(&rest.len()) || self.parts[at].shape.bits {
1044 let what = format!("'{segment},{section}' is not a section this can make room in");
1045 return Err(self.bad(&what));
1046 }
1047 let size = self.number(&rest[1])?;
1048 let size = self.count(size)?;
1049 let align = match rest.get(2) {
1050 Some(arg) => {
1051 let power = self.number(arg)?;
1052 let power = self.count(power)?;
1053 if power > 15 {
1054 return Err(self.bad(&format!("an alignment of 2^{power} is too large")));
1055 }
1056 1 << power
1057 }
1058 None => 1,
1059 };
1060 let sym = self.sym(name.trim());
1061 let part = &mut self.parts[at];
1062 part.align = part.align.max(align);
1063 part.size = part.size.next_multiple_of(align);
1064 let offset = part.size;
1065 part.size += size;
1066 self.labelled.insert(at);
1067 self.syms[sym].at = Held::In { part: at, offset };
1068 self.syms[sym].size = size;
1069 if self.syms[sym].sort == Sort::Untyped {
1070 self.syms[sym].sort = if thread { Sort::Thread } else { Sort::Object };
1071 }
1072 Ok(())
1073 }
1074
1075 fn section(&mut self, name: &str, shape: Shape) {
1081 if let Some(&at) = self.named.get(name) {
1082 self.go(at);
1083 return;
1084 }
1085 let at = self.parts.len();
1086 self.parts.push(Part {
1087 name: name.to_owned(),
1088 bytes: Vec::new(),
1089 size: 0,
1090 align: 1,
1091 shape,
1092 relocs: Vec::new(),
1093 });
1094 self.named.insert(name.to_owned(), at);
1095 self.go(at);
1096 }
1097
1098 fn go(&mut self, at: usize) {
1100 if at != self.here {
1101 self.before = Some(self.here);
1102 self.here = at;
1103 }
1104 }
1105
1106 fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
1108 if args.is_empty() {
1109 return Err(self.bad("a data directive with nothing after it"));
1110 }
1111 for arg in args {
1112 let sum = self.expression(arg)?;
1113 let at = self.at();
1114 if let Some(value) = sum.flat() {
1115 self.put(&value.to_le_bytes()[..width as usize])?;
1116 continue;
1117 }
1118 let part = self.here;
1121 if !self.parts[part].shape.bits {
1122 let what = format!(
1123 "'{}' holds no bytes and this asks the linker to write some into it",
1124 self.parts[part].name
1125 );
1126 return Err(self.bad(&what));
1127 }
1128 self.put(&vec![0u8; width as usize])?;
1129 self.fixups.push(Fixup {
1130 part,
1131 at,
1132 width,
1133 sum,
1134 reach: Reach::Near,
1135 slot: Reference::Got,
1136 branch: None,
1137 jump: false,
1138 field: None,
1139 line: self.line,
1140 });
1141 }
1142 Ok(())
1143 }
1144
1145 fn octa(&mut self, args: &[String]) -> Result<(), Trouble> {
1150 if args.is_empty() {
1151 return Err(self.bad("a data directive with nothing after it"));
1152 }
1153 for arg in args {
1154 let bytes = match wide(arg) {
1155 Some(value) => value.to_le_bytes(),
1156 None => i128::from(self.number(arg)?).to_le_bytes(),
1157 };
1158 self.put(&bytes)?;
1159 }
1160 Ok(())
1161 }
1162
1163 fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
1165 for arg in args {
1166 let mut bytes = self.string(arg.trim())?;
1167 if terminated {
1168 bytes.push(0);
1169 }
1170 self.put(&bytes)?;
1171 }
1172 Ok(())
1173 }
1174
1175 fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
1181 let Some(head) = args.first() else {
1182 return Err(self.bad(&format!(".{word} with nothing after it")));
1183 };
1184 let first = self.number(head)?;
1185 let first = self.count(first)?;
1186 let boundary = if word == "p2align" {
1187 if first > 31 {
1188 return Err(self.bad(".p2align of more than two gigabytes"));
1189 }
1190 1u64 << first
1191 } else {
1192 first
1193 };
1194 if boundary == 0 || !boundary.is_power_of_two() {
1195 let what = format!("an alignment of {boundary}, which is not a power of two");
1196 return Err(self.bad(&what));
1197 }
1198 let exec = self.parts[self.here].shape.exec;
1202 let fill = match args.get(1) {
1203 Some(arg) if !arg.trim().is_empty() => Some(self.byte(arg)?),
1204 _ => None,
1205 };
1206 let fill = fill.filter(|&fill| !(exec && !self.aarch64 && fill == 0x90));
1209 let at = self.at();
1210 let most = match args.get(2).filter(|arg| !arg.trim().is_empty()) {
1213 Some(most) => {
1214 let most = self.number(&most.clone())?;
1215 Some(self.count(most)?)
1216 }
1217 None => None,
1218 };
1219 let need = padding(at, boundary, most);
1220 self.aligns.push(Aligned { part: self.here, at, boundary, most, need });
1221 if need == 0 && most.is_some_and(|most| padding(at, boundary, None) > most) {
1222 return Ok(());
1223 }
1224 let part = &mut self.parts[self.here];
1225 part.align = part.align.max(boundary);
1226 match fill {
1227 Some(fill) => self.pad(need, fill),
1228 None if exec && self.aarch64 => {
1233 let mut bytes = vec![0u8; (need % 4) as usize];
1234 for _ in 0..need / 4 {
1235 bytes.extend_from_slice(&A64_NOP.to_le_bytes());
1236 }
1237 self.put(&bytes)
1238 }
1239 None if exec => {
1240 let mut bytes = Vec::new();
1241 nops(usize::try_from(need).unwrap_or(usize::MAX), &mut bytes);
1242 self.put(&bytes)
1243 }
1244 None => self.pad(need, 0),
1245 }
1246 }
1247
1248 fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
1250 for arg in args {
1251 let sym = self.sym(arg.trim());
1252 self.syms[sym].binding = binding;
1253 }
1254 Ok(())
1255 }
1256
1257 fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
1259 for arg in args {
1260 let sym = self.sym(arg.trim());
1261 self.syms[sym].visibility = visibility;
1262 }
1263 Ok(())
1264 }
1265
1266 fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
1268 let [name, what] = self.two(args, ".type")?;
1269 let what = unquoted(what.trim().trim_start_matches(['@', '%']));
1270 let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
1271 "func" | "function" => Sort::Func,
1272 "object" | "gnu_unique_object" => Sort::Object,
1273 "tls_object" | "tls" => Sort::Thread,
1274 "notype" | "" => Sort::Untyped,
1275 other => {
1276 let what = format!("'{other}' is not a symbol type this compiler writes");
1277 return Err(self.bad(&what));
1278 }
1279 };
1280 let sym = self.sym(name.trim());
1281 self.syms[sym].sort = sort;
1282 Ok(())
1283 }
1284
1285 fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
1292 if !(2..=3).contains(&args.len()) {
1293 return Err(
1294 self.bad("a common directive wants a name, a size and an optional alignment")
1295 );
1296 }
1297 let name = args[0].trim().to_owned();
1298 let size = self.number(&args[1])?;
1299 let size = self.count(size)?;
1300 let align = match args.get(2) {
1301 Some(arg) if self.macho => {
1303 let power = self.number(&arg.clone())?;
1304 let power = self.count(power)?;
1305 if power > 15 {
1306 return Err(self.bad(&format!("an alignment of 2^{power} is too large")));
1307 }
1308 1 << power
1309 }
1310 Some(arg) => {
1311 let align = self.number(&arg.clone())?;
1312 self.count(align)?.max(1)
1313 }
1314 None => size.next_power_of_two().clamp(1, 16),
1317 };
1318 if !align.is_power_of_two() {
1319 let what = format!("an alignment of {align}, which is not a power of two");
1320 return Err(self.bad(&what));
1321 }
1322 let sym = self.sym(&name);
1323 let local = local || self.said_local.contains(&sym);
1327 self.syms[sym].sort = Sort::Object;
1331 if local {
1332 let was = self.here;
1333 if self.macho {
1334 self.apple_section("__DATA", "__bss", None, &[])?;
1335 } else {
1336 self.section(".bss", Shape::of(".bss"));
1337 }
1338 let part = &mut self.parts[self.here];
1339 part.align = part.align.max(align);
1340 let over = part.size % align;
1341 if over != 0 {
1342 part.size += align - over;
1343 }
1344 let offset = self.parts[self.here].size;
1345 self.parts[self.here].size += size;
1346 let at = self.here;
1347 self.syms[sym].at = Held::In { part: at, offset };
1348 self.syms[sym].size = size;
1349 self.syms[sym].binding = Binding::Local;
1350 self.go(was);
1351 } else {
1352 self.syms[sym].at = Held::Common { size, align };
1353 self.syms[sym].size = size;
1354 self.syms[sym].binding = Binding::Global;
1355 }
1356 Ok(())
1357 }
1358
1359 fn at(&self) -> u64 {
1361 let part = &self.parts[self.here];
1362 if part.shape.bits { part.bytes.len() as u64 } else { part.size }
1363 }
1364
1365 fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
1367 let part = &mut self.parts[self.here];
1368 if !part.shape.bits {
1369 if bytes.iter().all(|byte| *byte == 0) {
1370 part.size += bytes.len() as u64;
1373 return Ok(());
1374 }
1375 let what = format!("'{}' holds no bytes and this puts some in it", part.name);
1376 return Err(Trouble { line: self.line, why: what });
1377 }
1378 part.bytes.extend_from_slice(bytes);
1379 part.size = part.bytes.len() as u64;
1380 Ok(())
1381 }
1382
1383 fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
1385 let part = &mut self.parts[self.here];
1386 if !part.shape.bits {
1387 part.size += count;
1388 return Ok(());
1389 }
1390 part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
1391 part.size = part.bytes.len() as u64;
1392 Ok(())
1393 }
1394
1395 fn sym(&mut self, name: &str) -> usize {
1397 if let Some(&at) = self.known.get(name) {
1398 return at;
1399 }
1400 let at = self.syms.len();
1401 self.syms.push(Sym {
1402 name: name.to_owned(),
1403 at: Held::Undefined,
1404 size: 0,
1405 sort: Sort::Untyped,
1406 binding: Binding::Local,
1410 visibility: Visibility::Default,
1411 numbered: name.contains('\u{1}'),
1414 });
1415 self.known.insert(name.to_owned(), at);
1416 at
1417 }
1418
1419 fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
1421 if args.len() != 2 {
1422 let why = format!("{what} wants two operands and was given {}", args.len());
1423 return Err(Trouble { line: self.line, why });
1424 }
1425 Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
1426 }
1427
1428 fn number(&mut self, text: &str) -> Result<i64, Trouble> {
1430 let sum = self.expression(text)?;
1431 sum.flat().ok_or_else(|| Trouble {
1432 line: self.line,
1433 why: format!("'{}' has to be a number here and it names something", text.trim()),
1434 })
1435 }
1436
1437 fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
1439 let value = self.number(text)?;
1440 u8::try_from(value & 0xff).map_err(|_| Trouble {
1441 line: self.line,
1442 why: format!("{value} does not fit in a byte"),
1443 })
1444 }
1445
1446 fn count(&self, value: i64) -> Result<u64, Trouble> {
1448 u64::try_from(value).map_err(|_| Trouble {
1449 line: self.line,
1450 why: format!("{value} is negative and this is a length"),
1451 })
1452 }
1453
1454 fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
1456 self.expression_at(text, (self.here, self.at() as i64))
1457 }
1458
1459 fn expression_at(&mut self, text: &str, here: (usize, i64)) -> Result<Sum, Trouble> {
1461 let mut parser = Parser { text: text.trim(), at: 0, here };
1462 let mut sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
1463 for term in &mut sum.terms {
1466 if let What::Symbol(name) = &term.what {
1467 let name = self.named(name)?;
1468 self.sym(&name);
1469 term.what = What::Symbol(name);
1470 }
1471 }
1472 Ok(sum)
1473 }
1474
1475 fn bad(&self, why: &str) -> Trouble {
1477 Trouble { line: self.line, why: why.to_owned() }
1478 }
1479
1480 fn finish(mut self) -> Result<Result<Assembled, Vec<usize>>, Trouble> {
1485 if self.frame.is_some() {
1486 return Err(self.bad("a '.cfi_startproc' that is never ended"));
1487 }
1488 self.unwind_table();
1489 self.resolve_sets()?;
1490 self.resolve_sizes()?;
1491 let grow = self.too_far()?;
1492 if !grow.is_empty() {
1493 return Ok(Err(grow));
1494 }
1495 self.resolve_fixups()?;
1496 let keep: Vec<bool> = self
1500 .parts
1501 .iter()
1502 .enumerate()
1503 .map(|(at, part)| {
1504 part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
1505 })
1506 .collect();
1507 let mut moved = vec![0usize; self.parts.len()];
1508 let mut parts = Vec::with_capacity(self.parts.len());
1509 for (at, part) in self.parts.into_iter().enumerate() {
1510 if keep[at] {
1511 moved[at] = parts.len();
1512 parts.push(part);
1513 }
1514 }
1515 let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
1516 for file in self.files {
1519 names.push(Name {
1520 name: file,
1521 at: Held::Absolute(0),
1522 size: 0,
1523 sort: Sort::File,
1524 binding: Binding::Local,
1525 visibility: Visibility::Default,
1526 });
1527 }
1528 for (index, sym) in self.syms.into_iter().enumerate() {
1529 if sym.numbered && !self.relocated.contains(&index) {
1534 continue;
1535 }
1536 let at = match sym.at {
1537 Held::In { part, offset } => Held::In { part: moved[part], offset },
1538 other => other,
1539 };
1540 let binding = match (at, sym.binding) {
1541 (Held::Undefined, Binding::Local) => Binding::Global,
1542 (_, binding) => binding,
1543 };
1544 names.push(Name {
1545 name: sym.name,
1546 at,
1547 size: sym.size,
1548 sort: sym.sort,
1549 binding,
1550 visibility: sym.visibility,
1551 });
1552 }
1553 Ok(Ok(Assembled { parts, names, subsections: self.subsections }))
1554 }
1555
1556 fn unwind_table(&mut self) {
1562 if self.frames.is_empty() || self.no_unwind {
1563 return;
1564 }
1565 let funcs: Vec<Extent> = self
1566 .frames
1567 .iter()
1568 .map(|frame| Extent {
1569 name: self.syms[frame.sym].name.clone(),
1570 start: frame.start as usize,
1571 len: frame.len as usize,
1572 align: 1,
1573 binding: Binding::Local,
1574 visibility: Visibility::Default,
1575 patch: None,
1576 landings: Vec::new(),
1577 })
1578 .collect();
1579 let rows: Vec<_> = self.frames.iter().map(|frame| frame.rows.clone()).collect();
1580 let conv: &CallRegs = if self.aarch64 { &AAPCS64 } else { &SYSV };
1581 let format = if self.macho { ObjectFormat::MachO } else { ObjectFormat::Elf };
1582 let Ok(table) = crate::unwind::table(&funcs, &rows, conv, format) else {
1583 return;
1584 };
1585 for frame in &self.frames {
1586 self.relocated.insert(frame.sym);
1587 }
1588 let (name, shape) = if self.macho {
1592 let attributes = ["no_toc", "strip_static_syms", "live_support"];
1593 let Ok(shape) = Shape::mach("__TEXT", "__eh_frame", Some("coalesced"), &attributes)
1594 else {
1595 return;
1596 };
1597 ("__TEXT,__eh_frame", shape)
1598 } else {
1599 (".eh_frame", Shape { alloc: true, bits: true, ..Shape::default() })
1600 };
1601 let size = table.bytes.len() as u64;
1602 self.parts.push(Part {
1603 name: name.to_owned(),
1604 bytes: table.bytes,
1605 size,
1606 align: 8,
1607 shape,
1608 relocs: table.relocs,
1609 });
1610 }
1611
1612 fn resolve_sets(&mut self) -> Result<(), Trouble> {
1615 while !self.sets.is_empty() {
1616 let mut done = Vec::new();
1617 for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
1618 if let Ok(residue) = self.reduce(sum) {
1619 done.push((at, *sym, self.settled(&residue, *line)?));
1620 }
1621 }
1622 if done.is_empty() {
1623 let (sym, _, line) = &self.sets[0];
1624 let why = format!(
1625 "'{}' is set to something that is set to it, so neither has a value",
1626 self.syms[*sym].name
1627 );
1628 return Err(Trouble { line: *line, why });
1629 }
1630 for (_, sym, held) in &done {
1631 self.syms[*sym].at = *held;
1632 }
1633 for (at, _, _) in done.iter().rev() {
1635 self.sets.remove(*at);
1636 }
1637 }
1638 Ok(())
1639 }
1640
1641 fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
1643 match residue.left.as_slice() {
1644 [] => Ok(Held::Absolute(residue.constant as u64)),
1645 [Left { coeff: 1, at: Some((part, offset)), .. }] => {
1648 Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
1649 }
1650 _ => Err(Trouble {
1651 line,
1652 why: "a set to something that is neither a number nor a place in this file"
1653 .to_owned(),
1654 }),
1655 }
1656 }
1657
1658 fn resolve_sizes(&mut self) -> Result<(), Trouble> {
1660 for (sym, sum, line) in std::mem::take(&mut self.sizes) {
1661 let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
1662 if !residue.left.is_empty() {
1663 let why = format!(
1664 "the size of '{}' is not a number, and a size has to be one",
1665 self.syms[sym].name
1666 );
1667 return Err(Trouble { line, why });
1668 }
1669 let size = self.count(residue.constant).map_err(|_| Trouble {
1670 line,
1671 why: format!("'{}' is given a negative size", self.syms[sym].name),
1672 })?;
1673 self.syms[sym].size = size;
1674 }
1675 Ok(())
1676 }
1677
1678 fn too_far(&self) -> Result<Vec<usize>, Trouble> {
1698 let mut away = Vec::new();
1699 let mut jumps: Vec<(usize, i64, i64, usize)> = Vec::new();
1701 for fixup in &self.fixups {
1702 let Some(nth) = fixup.branch else { continue };
1703 let residue = self
1704 .reduce_kept(&fixup.sum, true)
1705 .map_err(|why| Trouble { line: fixup.line, why })?;
1706 if !residue.left.is_empty() {
1707 away.push(nth);
1708 } else {
1709 jumps.push((fixup.part, fixup.at as i64 + 1, residue.constant, nth));
1710 }
1711 }
1712 if !away.is_empty() {
1713 return Ok(away);
1714 }
1715 jumps.sort_unstable();
1716 let mut far = Vec::new();
1717 let mut jumps = jumps.into_iter().peekable();
1718 while let Some(&(part, ..)) = jumps.peek() {
1719 let aligns: Vec<Aligned> =
1720 self.aligns.iter().filter(|align| align.part == part).copied().collect();
1721 let mut aligns_left = aligns.iter().peekable();
1722 let mut stretch = 0i64;
1723 let mut moved: Vec<(i64, i64)> = Vec::new();
1725 while let Some(&(_, end, distance, nth)) = jumps.peek().filter(|jump| jump.0 == part) {
1726 jumps.next();
1727 while let Some(align) = aligns_left.next_if(|align| align.at as i64 <= end - 2) {
1728 let now =
1729 padding((align.at as i64 + stretch) as u64, align.boundary, align.most);
1730 stretch += now as i64 - align.need as i64;
1731 moved.push(((align.at + align.need) as i64, stretch));
1732 }
1733 let target = end + distance;
1734 let judged = if distance < 0 {
1735 let there = moved.iter().rev().find(|(from, _)| *from <= target);
1736 distance + there.map_or(0, |(_, by)| *by) - stretch
1737 } else if stretch > 0
1738 && aligns.iter().any(|align| {
1739 end <= align.at as i64 && (align.at + align.need) as i64 <= target
1740 })
1741 {
1742 distance - stretch
1743 } else {
1744 distance
1745 };
1746 if distance >= 0 && judged < -2 {
1750 continue;
1751 }
1752 if i8::try_from(judged).is_err() {
1753 far.push(nth);
1754 stretch += if self.parts[part].bytes[end as usize - 2] == 0xEB { 3 } else { 4 };
1755 moved.push((end, stretch));
1756 }
1757 }
1758 }
1759 Ok(far)
1760 }
1761
1762 fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1763 for fixup in std::mem::take(&mut self.fixups) {
1764 if let Some(field) = fixup.field {
1765 self.field(&fixup, field)?;
1766 continue;
1767 }
1768 let line = fixup.line;
1769 let bad = |why: String| Trouble { line, why };
1770 if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1778 let [
1779 Term { coeff: 1, what: What::Symbol(name) },
1780 Term { coeff: -1, what: What::Here { at: end, .. } },
1781 ] = fixup.sum.terms.as_slice()
1782 else {
1783 return Err(bad(
1784 "a reach through the global offset table in something other than an \
1785 instruction, which is not an expression this compiler writes"
1786 .to_owned(),
1787 ));
1788 };
1789 let kind = if fixup.reach == Reach::Table { fixup.slot } else { Reference::Thread };
1790 self.parts[fixup.part].relocs.push(Reloc {
1791 at: fixup.at as usize,
1792 symbol: name.clone(),
1793 kind,
1794 addend: fixup.sum.constant + fixup.at as i64 - end,
1795 after: (end - fixup.at as i64 - 4).max(0) as u8,
1796 });
1797 continue;
1798 }
1799 let residue =
1800 self.reduce_kept(&fixup.sum, fixup.jump).map_err(|why| Trouble { line, why })?;
1801 if fixup.reach == Reach::Value && !residue.left.is_empty() {
1802 return Err(bad(
1803 "a number in an instruction that names something outside this section, \
1804 which wants a relocation this compiler does not write yet"
1805 .to_owned(),
1806 ));
1807 }
1808 let (symbol, kind, addend, after) = match residue.left.as_slice() {
1809 [] => {
1810 let width = fixup.width as usize;
1818 let room = 8 * width as u32;
1819 let low = -(1i64 << (room - 1));
1820 let high = if fixup.reach == Reach::Branch {
1821 (1i64 << (room - 1)) - 1
1822 } else {
1823 (1i64 << room) - 1
1824 };
1825 if width < 8 && (residue.constant < low || residue.constant > high) {
1826 return Err(bad(format!(
1827 "{} written into {width} bytes, which does not reach it",
1828 residue.constant
1829 )));
1830 }
1831 let bytes = residue.constant.to_le_bytes();
1832 let at = fixup.at as usize;
1833 let part = &mut self.parts[fixup.part];
1834 part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1835 continue;
1836 }
1837 [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1839 let kind = Reference::Address { bytes: fixup.width };
1840 (name.clone(), kind, residue.constant, 0)
1841 }
1842 [
1847 Left { coeff: 1, what: What::Symbol(name), .. },
1848 Left { coeff: -1, at: Some((part, offset)), .. },
1849 ]
1850 | [
1851 Left { coeff: -1, at: Some((part, offset)), .. },
1852 Left { coeff: 1, what: What::Symbol(name), .. },
1853 ] => {
1854 if *part != fixup.part {
1855 return Err(bad(
1856 "a distance that is subtracted from somewhere in another section"
1857 .to_owned(),
1858 ));
1859 }
1860 if fixup.width != 4 {
1861 return Err(bad(format!(
1862 "a distance written into {} bytes, and four is the only width a \
1863 relocation says one at",
1864 fixup.width
1865 )));
1866 }
1867 let addend = residue.constant + fixup.at as i64 - offset;
1874 let near = self.known.get(name).is_some_and(|&sym| {
1877 self.syms[sym].binding == Binding::Local
1878 && matches!(self.syms[sym].at, Held::In { .. })
1879 });
1880 let kind = if fixup.reach == Reach::Branch && !near {
1881 Reference::Call
1882 } else {
1883 Reference::Data
1884 };
1885 let after = (offset - fixup.at as i64 - 4).max(0);
1888 (name.clone(), kind, addend, after as u8)
1889 }
1890 [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1891 return Err(bad(
1892 "the address of these bytes themselves, which has no symbol to be \
1893 relocated against"
1894 .to_owned(),
1895 ));
1896 }
1897 _ => {
1898 return Err(bad(
1899 "an expression that does not come out as a number, an address, or a \
1900 distance, and those are what a relocation can say"
1901 .to_owned(),
1902 ));
1903 }
1904 };
1905 if let Some(&sym) = self.known.get(&symbol) {
1909 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1910 self.relocated.insert(sym);
1911 } else if self.syms[sym].numbered {
1912 let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1913 return Err(bad(format!(
1914 "'{number}f' goes on to a '{number}:' and there is none below it"
1915 )));
1916 }
1917 }
1918 if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1919 return Err(bad(format!(
1920 "the address of '{symbol}' written into {} bytes, and this machine relocates \
1921 an address at four or eight",
1922 fixup.width
1923 )));
1924 }
1925 self.parts[fixup.part].relocs.push(Reloc {
1926 at: fixup.at as usize,
1927 symbol,
1928 kind,
1929 addend,
1930 after,
1931 });
1932 }
1933 Ok(())
1934 }
1935
1936 fn field(&mut self, fixup: &Fixup, field: aarch64::Fixup) -> Result<(), Trouble> {
1946 let line = fixup.line;
1947 let bad = |why: String| Trouble { line, why };
1948 let residue = self.reduce_kept(&fixup.sum, fixup.jump).map_err(bad)?;
1949 let (name, addend) = match residue.left.as_slice() {
1950 [] if relative(field) => {
1951 let at = fixup.at as usize;
1952 let bytes = &mut self.parts[fixup.part].bytes[at..at + 4];
1953 let word = u32::from_le_bytes(bytes.try_into().expect("four bytes"));
1954 let word = field.apply(word, residue.constant).ok_or_else(|| {
1955 bad(format!("{} is out of the reach of {}", residue.constant, field.name()))
1956 })?;
1957 bytes.copy_from_slice(&word.to_le_bytes());
1958 return Ok(());
1959 }
1960 [Left { coeff: 1, what: What::Symbol(name), .. }] if !relative(field) => {
1961 (name.clone(), residue.constant)
1962 }
1963 [
1964 Left { coeff: 1, what: What::Symbol(name), .. },
1965 Left { coeff: -1, at: Some((part, offset)), .. },
1966 ]
1967 | [
1968 Left { coeff: -1, at: Some((part, offset)), .. },
1969 Left { coeff: 1, what: What::Symbol(name), .. },
1970 ] if relative(field) && *part == fixup.part => {
1971 (name.clone(), residue.constant + fixup.at as i64 - offset)
1972 }
1973 _ => {
1974 return Err(bad(format!(
1975 "an expression that {} cannot say, which is a name and a number added to it",
1976 field.name()
1977 )));
1978 }
1979 };
1980 if let Some(&sym) = self.known.get(&name) {
1981 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1982 self.relocated.insert(sym);
1983 } else if self.syms[sym].numbered {
1984 let number = name.split('\u{1}').next().unwrap_or(&name);
1985 return Err(bad(format!(
1986 "'{number}f' goes on to a '{number}:' and there is none below it"
1987 )));
1988 }
1989 }
1990 self.parts[fixup.part].relocs.push(Reloc {
1991 at: fixup.at as usize,
1992 symbol: name,
1993 kind: Reference::Field(field),
1994 addend,
1995 after: 0,
1996 });
1997 Ok(())
1998 }
1999
2000 fn reduce_kept(&self, sum: &Sum, jump: bool) -> Result<Residue, String> {
2011 let mut named =
2012 sum.terms.iter().enumerate().filter(|(_, term)| matches!(term.what, What::Symbol(_)));
2013 let (Some((nth, Term { coeff: 1, what: What::Symbol(name) })), None) =
2014 (named.next(), named.next())
2015 else {
2016 return self.reduce(sum);
2017 };
2018 let kept = self.known.get(name).is_some_and(|&sym| {
2019 (self.syms[sym].binding == Binding::Weak
2020 || !jump && self.syms[sym].binding == Binding::Global)
2021 && matches!(self.syms[sym].at, Held::In { .. })
2022 });
2023 if !kept {
2024 return self.reduce(sum);
2025 }
2026 let mut rest = sum.clone();
2027 rest.terms.remove(nth);
2028 let mut residue = self.reduce(&rest)?;
2029 residue.left.push(Left { coeff: 1, what: What::Symbol(name.clone()), at: None });
2030 Ok(residue)
2031 }
2032
2033 fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
2042 let mut constant = sum.constant;
2043 let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
2044 let mut outside: Vec<(i64, String)> = Vec::new();
2045 for term in &sum.terms {
2046 match &term.what {
2047 What::Here { part, at } => {
2048 placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
2049 }
2050 What::Symbol(name) => {
2051 let Some(&at) = self.known.get(name) else {
2052 return Err(format!("'{name}' is named and never said"));
2053 };
2054 match self.syms[at].at {
2055 Held::Absolute(value) => constant += term.coeff * value as i64,
2056 Held::In { part, offset } => placed.entry(part).or_default().push((
2057 term.coeff,
2058 term.what.clone(),
2059 offset as i64,
2060 )),
2061 Held::Undefined | Held::Common { .. } => {
2064 if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
2065 outside.push((term.coeff, name.clone()));
2066 } else {
2067 return Err(format!("'{name}' is not worked out yet"));
2068 }
2069 }
2070 }
2071 }
2072 }
2073 }
2074 let mut left: Vec<Left> = Vec::new();
2075 for (part, terms) in placed {
2076 let (_, chosen, base) = terms[0].clone();
2077 let mut net = 0;
2078 for (coeff, _, offset) in &terms {
2079 net += coeff;
2080 constant += coeff * (offset - base);
2081 }
2082 if net != 0 {
2083 left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
2084 }
2085 }
2086 let mut together: BTreeMap<String, i64> = BTreeMap::new();
2087 for (coeff, name) in outside {
2088 *together.entry(name).or_default() += coeff;
2089 }
2090 for (name, coeff) in together {
2091 if coeff != 0 {
2092 left.push(Left { coeff, what: What::Symbol(name), at: None });
2093 }
2094 }
2095 Ok(Residue { constant, left })
2096 }
2097}
2098
2099#[derive(Debug, Clone)]
2101struct Residue {
2102 constant: i64,
2103 left: Vec<Left>,
2104}
2105
2106#[derive(Debug, Clone)]
2108struct Left {
2109 coeff: i64,
2111 what: What,
2113 at: Option<(usize, i64)>,
2116}
2117
2118#[derive(Debug, Clone, Default, PartialEq, Eq)]
2120struct Sum {
2121 constant: i64,
2122 terms: Vec<Term>,
2123}
2124
2125#[derive(Debug, Clone, PartialEq, Eq)]
2127struct Term {
2128 coeff: i64,
2129 what: What,
2130}
2131
2132#[derive(Debug, Clone, PartialEq, Eq)]
2134enum What {
2135 Symbol(String),
2137 Here { part: usize, at: i64 },
2140}
2141
2142impl Sum {
2143 fn flat(&self) -> Option<i64> {
2145 self.terms.is_empty().then_some(self.constant)
2146 }
2147
2148 fn of(what: What) -> Sum {
2150 Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
2151 }
2152
2153 fn just(value: i64) -> Sum {
2155 Sum { constant: value, terms: Vec::new() }
2156 }
2157
2158 fn plus(mut self, other: Sum) -> Sum {
2160 self.constant = self.constant.wrapping_add(other.constant);
2161 self.terms.extend(other.terms);
2162 self
2163 }
2164
2165 fn minus(self) -> Sum {
2167 Sum {
2168 constant: self.constant.wrapping_neg(),
2169 terms: self
2170 .terms
2171 .into_iter()
2172 .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
2173 .collect(),
2174 }
2175 }
2176
2177 fn times(self, factor: i64) -> Sum {
2179 Sum {
2180 constant: self.constant.wrapping_mul(factor),
2181 terms: self
2182 .terms
2183 .into_iter()
2184 .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
2185 .collect(),
2186 }
2187 }
2188}
2189
2190pub(crate) fn constant(text: &str) -> Option<i64> {
2196 let mut parser = Parser { text: text.trim(), at: 0, here: (0, 0) };
2197 parser.whole().ok()?.flat()
2198}
2199
2200struct Parser<'a> {
2202 text: &'a str,
2203 at: usize,
2204 here: (usize, i64),
2205}
2206
2207impl Parser<'_> {
2208 fn whole(&mut self) -> Result<Sum, String> {
2210 let sum = self.bitwise()?;
2211 self.space();
2212 if self.at < self.text.len() {
2213 return Err(format!(
2214 "'{}' is left over at the end of an expression",
2215 &self.text[self.at..]
2216 ));
2217 }
2218 Ok(sum)
2219 }
2220
2221 fn bitwise(&mut self) -> Result<Sum, String> {
2223 let mut left = self.shift()?;
2224 loop {
2225 self.space();
2226 let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
2227 let right = self.shift()?;
2228 left = self.arithmetic(left, right, op)?;
2229 }
2230 }
2231
2232 fn shift(&mut self) -> Result<Sum, String> {
2234 let mut left = self.sum()?;
2235 loop {
2236 self.space();
2237 let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
2238 let right = self.sum()?;
2239 left = self.arithmetic(left, right, op)?;
2240 }
2241 }
2242
2243 fn sum(&mut self) -> Result<Sum, String> {
2245 let mut left = self.product()?;
2246 loop {
2247 self.space();
2248 let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
2250 let right = self.product()?;
2251 left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
2252 }
2253 }
2254
2255 fn product(&mut self) -> Result<Sum, String> {
2257 let mut left = self.unary()?;
2258 loop {
2259 self.space();
2260 let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
2261 let right = self.unary()?;
2262 left = match (op, left.flat(), right.flat()) {
2266 ("*", _, Some(factor)) => left.times(factor),
2267 ("*", Some(factor), _) => right.times(factor),
2268 (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
2269 _ => return Err(format!("'{op}' of something that names a symbol")),
2270 };
2271 }
2272 }
2273
2274 fn unary(&mut self) -> Result<Sum, String> {
2276 self.space();
2277 if self.eat("-") {
2278 return Ok(self.unary()?.minus());
2279 }
2280 if self.eat("+") {
2281 return self.unary();
2282 }
2283 if self.eat("~") {
2284 let inner = self.unary()?;
2285 let value = inner
2286 .flat()
2287 .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
2288 return Ok(Sum::just(!value));
2289 }
2290 if self.eat("!") {
2291 let inner = self.unary()?;
2292 let value = inner
2293 .flat()
2294 .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
2295 return Ok(Sum::just(i64::from(value == 0)));
2296 }
2297 self.primary()
2298 }
2299
2300 fn primary(&mut self) -> Result<Sum, String> {
2302 self.space();
2303 let rest = &self.text[self.at..];
2304 if rest.is_empty() {
2305 return Err("an expression that stops before it says anything".to_owned());
2306 }
2307 if self.eat("(") {
2308 let inner = self.bitwise()?;
2309 self.space();
2310 if !self.eat(")") {
2311 return Err("a bracket that was opened and never closed".to_owned());
2312 }
2313 return Ok(inner);
2314 }
2315 let first = rest.as_bytes()[0];
2316 if first == b'\'' {
2317 return self.character();
2318 }
2319 if first.is_ascii_digit() {
2320 let end = rest.find(|ch: char| !ch.is_ascii_digit()).unwrap_or(rest.len());
2324 let bytes = rest.as_bytes();
2325 if matches!(bytes.get(end), Some(b'b' | b'f'))
2326 && !bytes.get(end + 1).is_some_and(|byte| carries_on(*byte))
2327 {
2328 self.at += end + 1;
2329 return Ok(Sum::of(What::Symbol(rest[..=end].to_owned())));
2330 }
2331 return self.digits();
2332 }
2333 if starts(first) {
2334 let name = self.word();
2335 if name == "." {
2337 let (part, at) = self.here;
2338 return Ok(Sum::of(What::Here { part, at }));
2339 }
2340 if self.text[self.at..].starts_with('@') {
2344 return Err(format!(
2345 "'{name}@' asks for a relocation only an instruction can carry"
2346 ));
2347 }
2348 return Ok(Sum::of(What::Symbol(name)));
2349 }
2350 Err(format!("'{rest}' is not the start of an expression"))
2351 }
2352
2353 fn digits(&mut self) -> Result<Sum, String> {
2355 let rest = &self.text[self.at..];
2356 let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
2357 (16, 2)
2358 } else if rest.starts_with("0b") || rest.starts_with("0B") {
2359 (2, 2)
2360 } else if rest.starts_with('0') && rest.as_bytes().get(1).is_some_and(u8::is_ascii_digit) {
2361 (8, 1)
2364 } else {
2365 (10, 0)
2366 };
2367 let body = &rest[skip..];
2368 let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
2369 if end == 0 {
2370 return Err(format!("'{rest}' starts like a number and is not one"));
2371 }
2372 let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
2373 let value = u64::from_str_radix(&text, radix)
2376 .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
2377 self.at += skip + end;
2378 while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
2381 self.at += 1;
2382 }
2383 Ok(Sum::just(value as i64))
2384 }
2385
2386 fn character(&mut self) -> Result<Sum, String> {
2388 self.at += 1;
2389 let rest = &self.text[self.at..];
2390 let mut chars = rest.chars();
2391 let Some(first) = chars.next() else {
2392 return Err("a quote with no character after it".to_owned());
2393 };
2394 let (value, used) = if first == '\\' {
2395 let (value, used) = escape(&rest[1..])?;
2396 (value, used + 1)
2397 } else {
2398 (first as u8, first.len_utf8())
2399 };
2400 self.at += used;
2401 if self.text[self.at..].starts_with('\'') {
2404 self.at += 1;
2405 }
2406 Ok(Sum::just(i64::from(value)))
2407 }
2408
2409 fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
2411 let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
2412 return Err(format!("'{op}' of something that names a symbol"));
2413 };
2414 Ok(Sum::just(self.arithmetic_number(a, b, op)?))
2415 }
2416
2417 fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
2419 Ok(match op {
2420 "|" => a | b,
2421 "^" => a ^ b,
2422 "&" => a & b,
2423 "<<" => a.wrapping_shl(shift(b)?),
2424 ">>" => a.wrapping_shr(shift(b)?),
2425 "*" => a.wrapping_mul(b),
2426 "/" if b == 0 => return Err("a division by zero".to_owned()),
2427 "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
2428 "/" => a.wrapping_div(b),
2429 "%" => a.wrapping_rem(b),
2430 _ => return Err(format!("'{op}' is not an operator this compiler knows")),
2431 })
2432 }
2433
2434 fn word(&mut self) -> String {
2436 let body = &self.text[self.at..];
2437 let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
2438 let word = body[..end].to_owned();
2439 self.at += end;
2440 word
2441 }
2442
2443 fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
2448 for op in ops {
2449 if self.text[self.at..].starts_with(op) {
2450 self.at += op.len();
2451 return Some(op);
2452 }
2453 }
2454 None
2455 }
2456
2457 fn eat(&mut self, what: &str) -> bool {
2459 if self.text[self.at..].starts_with(what) {
2460 self.at += what.len();
2461 return true;
2462 }
2463 false
2464 }
2465
2466 fn space(&mut self) {
2468 while self.text[self.at..].starts_with([' ', '\t']) {
2469 self.at += 1;
2470 }
2471 }
2472}
2473
2474impl Reader {
2475 fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
2477 let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
2478 let body = text
2479 .strip_prefix('"')
2480 .and_then(|rest| rest.strip_suffix('"'))
2481 .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
2482 let mut out = Vec::with_capacity(body.len());
2483 let mut at = 0;
2484 while at < body.len() {
2485 let rest = &body[at..];
2486 let first = rest.as_bytes()[0];
2487 if first == b'\\' {
2488 let (value, used) =
2489 escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
2490 out.push(value);
2491 at += used + 1;
2492 continue;
2493 }
2494 let ch = rest.chars().next().unwrap_or('\0');
2495 let mut buffer = [0u8; 4];
2496 out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
2497 at += ch.len_utf8();
2498 }
2499 Ok(out)
2500 }
2501}
2502
2503const A64_NOP: u32 = 0xd503_201f;
2506
2507fn relative(field: aarch64::Fixup) -> bool {
2510 use aarch64::Fixup;
2511 matches!(
2512 field,
2513 Fixup::Jump26
2514 | Fixup::Call26
2515 | Fixup::CondBr19
2516 | Fixup::TestBr14
2517 | Fixup::Literal19
2518 | Fixup::AdrLo21
2519 )
2520}
2521
2522fn aarch64_dwarf(text: &str) -> Option<u16> {
2525 if let Ok(number) = text.parse::<u16>() {
2526 return Some(number);
2527 }
2528 let lower = text.to_ascii_lowercase();
2529 match lower.as_str() {
2530 "sp" => return Some(31),
2531 "fp" => return Some(29),
2532 "lr" => return Some(30),
2533 _ => {}
2534 }
2535 let (first, number) = lower.split_at(1);
2536 let number = number.parse::<u16>().ok().filter(|&number| number < 32)?;
2537 match first {
2538 "x" | "w" if number < 31 => Some(number),
2539 "v" | "q" | "d" | "s" => Some(64 + number),
2540 _ => None,
2541 }
2542}
2543
2544fn shift(by: i64) -> Result<u32, String> {
2545 u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
2546}
2547
2548fn escape(rest: &str) -> Result<(u8, usize), String> {
2552 let bytes = rest.as_bytes();
2553 let Some(&first) = bytes.first() else {
2554 return Err("a backslash with nothing after it".to_owned());
2555 };
2556 let simple = match first {
2557 b'n' => Some(b'\n'),
2558 b't' => Some(b'\t'),
2559 b'r' => Some(b'\r'),
2560 b'f' => Some(0x0c),
2561 b'b' => Some(0x08),
2562 b'v' => Some(0x0b),
2563 b'a' => Some(0x07),
2564 b'e' => Some(0x1b),
2565 b'\\' => Some(b'\\'),
2566 b'"' => Some(b'"'),
2567 b'\'' => Some(b'\''),
2568 _ => None,
2569 };
2570 if let Some(value) = simple {
2571 return Ok((value, 1));
2572 }
2573 if first == b'x' || first == b'X' {
2574 let end = bytes[1..]
2575 .iter()
2576 .position(|byte| !byte.is_ascii_hexdigit())
2577 .map_or(bytes.len(), |at| at + 1);
2578 if end == 1 {
2579 return Err("a hex escape with no digits in it".to_owned());
2580 }
2581 let text = &rest[1..end];
2584 let text = &text[text.len().saturating_sub(2)..];
2585 let value =
2586 u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
2587 return Ok((value, end));
2588 }
2589 if (b'0'..=b'7').contains(&first) {
2590 let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
2591 let value = u32::from_str_radix(&rest[..end], 8)
2592 .map_err(|_| "an octal escape that is not one".to_owned())?;
2593 return Ok(((value & 0xff) as u8, end));
2594 }
2595 Err(format!("'\\{}' is not an escape this compiler knows", first as char))
2599}
2600
2601fn labelled(text: &str) -> Option<String> {
2608 let bytes = text.as_bytes();
2609 if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
2610 return None;
2611 }
2612 let end = text.find(|ch: char| !carries_on(ch as u8))?;
2613 if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
2615 return None;
2616 }
2617 Some(text[..end].to_owned())
2618}
2619
2620fn assigned(text: &str) -> Option<(&str, &str)> {
2624 let bytes = text.as_bytes();
2625 if bytes.is_empty() || !starts(bytes[0]) {
2626 return None;
2627 }
2628 let end = text.find(|ch: char| !carries_on(ch as u8)).unwrap_or(text.len());
2629 let rest = text[end..].trim_start().strip_prefix('=')?;
2630 if rest.starts_with('=') {
2631 return None;
2632 }
2633 Some((&text[..end], rest.trim()))
2634}
2635
2636fn starts(byte: u8) -> bool {
2638 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
2639}
2640
2641fn carries_on(byte: u8) -> bool {
2643 starts(byte) || byte.is_ascii_digit()
2644}
2645
2646fn padding(at: u64, boundary: u64, most: Option<u64>) -> u64 {
2654 let over = at % boundary;
2655 let need = if over == 0 { 0 } else { boundary - over };
2656 if most.is_some_and(|most| need > most) { 0 } else { need }
2657}
2658
2659fn counted(number: &str, nth: usize) -> String {
2660 format!("{number}\u{1}{nth}")
2661}
2662
2663fn unquoted(text: &str) -> String {
2665 text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
2666}
2667
2668fn wide(text: &str) -> Option<u128> {
2673 let text = text.trim();
2674 let (negative, text) = match text.strip_prefix('-') {
2675 Some(rest) => (true, rest.trim_start()),
2676 None => (false, text),
2677 };
2678 let lower = text.to_ascii_lowercase();
2679 let (digits, radix) = if let Some(rest) = lower.strip_prefix("0x") {
2680 (rest, 16)
2681 } else if let Some(rest) = lower.strip_prefix("0b") {
2682 (rest, 2)
2683 } else if lower.len() > 1 && lower.starts_with('0') {
2684 (&lower[1..], 8)
2685 } else {
2686 (lower.as_str(), 10)
2687 };
2688 if digits.is_empty() {
2689 return None;
2690 }
2691 let value = u128::from_str_radix(digits, radix).ok()?;
2692 Some(if negative { value.wrapping_neg() } else { value })
2693}
2694
2695pub(crate) fn split(text: &str, on: char) -> Vec<String> {
2698 let mut out = Vec::new();
2699 let mut piece = String::new();
2700 let mut depth = 0i32;
2701 let mut quote = None;
2702 let mut chars = text.chars();
2703 while let Some(ch) = chars.next() {
2704 if let Some(mark) = quote {
2705 piece.push(ch);
2706 if ch == '\\' {
2707 if let Some(next) = chars.next() {
2708 piece.push(next);
2709 }
2710 continue;
2711 }
2712 if ch == mark {
2713 quote = None;
2714 }
2715 continue;
2716 }
2717 match ch {
2718 '"' => {
2719 quote = Some(ch);
2720 piece.push(ch);
2721 }
2722 '(' => {
2723 depth += 1;
2724 piece.push(ch);
2725 }
2726 ')' => {
2727 depth -= 1;
2728 piece.push(ch);
2729 }
2730 _ if ch == on && depth == 0 => {
2731 out.push(std::mem::take(&mut piece));
2732 }
2733 _ => piece.push(ch),
2734 }
2735 }
2736 if !piece.trim().is_empty() || !out.is_empty() {
2737 out.push(piece);
2738 }
2739 out.into_iter().map(|piece| piece.trim().to_owned()).collect()
2740}
2741
2742fn repeated<'a>(word: &str, rest: &'a str) -> Option<(String, &'a str)> {
2753 let (next, after) = match rest.find(char::is_whitespace) {
2754 Some(cut) => (&rest[..cut], rest[cut..].trim()),
2755 None => (rest, ""),
2756 };
2757 if word == "notrack" {
2758 return match next {
2759 "jmp" | "jmpq" => Some(("notrack jmp".to_owned(), after)),
2760 "call" | "callq" => Some(("notrack call".to_owned(), after)),
2761 _ => None,
2762 };
2763 }
2764 if matches!(word, "rep" | "repe" | "repz") {
2767 if let Some(width) = next.strip_prefix("bsf") {
2768 if matches!(width, "" | "w" | "l" | "q") {
2769 return Some((format!("tzcnt{width}"), after));
2770 }
2771 }
2772 }
2773 let unequal = match word {
2774 "rep" | "repe" | "repz" => false,
2775 "repne" | "repnz" => true,
2776 _ => return None,
2777 };
2778 let string = next.len() == 5 && next.ends_with(['b', 'w', 'l', 'q']);
2779 let which = if string { &next[..4] } else { "" };
2780 let prefix = match (unequal, which) {
2781 (false, "movs" | "stos") => "rep",
2782 (false, "scas" | "cmps") => "repe",
2783 (true, "scas" | "cmps") => "repne",
2784 _ => return None,
2785 };
2786 Some((format!("{prefix} {next}"), after))
2787}
2788
2789#[cfg(test)]
2790mod tests {
2791 use super::*;
2792
2793 use rucc_object::Reference;
2794
2795 fn assembled(text: &str) -> Assembled {
2797 match read(text, Arch::X86_64) {
2798 Ok(assembled) => assembled,
2799 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2800 }
2801 }
2802
2803 #[test]
2807 fn a_mac_listing_with_frame_rules_has_an_unwind_table() {
2808 let text = "\t.section\t__TEXT,__text,regular,pure_instructions
2809\t.globl\t_f
2810_f:
2811\t.cfi_startproc
2812\tstp x29, x30, [sp, #-16]!
2813\t.cfi_def_cfa_offset 16
2814\tldp x29, x30, [sp], #16
2815\tret
2816\t.cfi_endproc
2817\t.subsections_via_symbols
2818";
2819 let done = match read_as(text, Arch::Aarch64, ObjectFormat::MachO) {
2820 Ok(done) => done,
2821 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2822 };
2823 let table = done.parts.iter().find(|part| part.name == "__TEXT,__eh_frame").expect("one");
2824 assert_eq!(table.shape.mach & 0x0800_00ff, 0x0800_000b);
2826 let rows = [0x44, 0x0e, 0x10];
2827 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
2828 let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
2829 assert_eq!(reloc.kind, Reference::Data);
2830 let at = |wanted: &str| done.names.iter().find(|name| name.name == wanted).unwrap().at;
2831 assert_eq!(at(&reloc.symbol), at("_f"));
2832 }
2833
2834 #[test]
2835 fn a_mac_listing_reads_as_segments_and_sections() {
2836 let text = "\t.section\t__TEXT,__text,regular,pure_instructions
2837\t.p2align\t2
2838\t.globl\t_main
2839\t.private_extern\t_main
2840_main:
2841Lmain_0:
2842\tadrp x0, _counter@PAGE
2843\tldr w0, [x0, _counter@PAGEOFF]
2844\tret
2845\t.section\t__DATA,__data
2846\t.globl\t_counter
2847_counter:
2848\t.long\t3
2849\t.globl\t_zeros
2850\t.zerofill\t__DATA,__bss,_zeros,400,4
2851\t.long\t4
2852\t.section\t__DATA,__thread_data,thread_local_regular
2853_tls$tlv$init:
2854\t.long\t5
2855\t.weak_definition\t_counter
2856\t.subsections_via_symbols
2857";
2858 let done = match read_as(text, Arch::Aarch64, ObjectFormat::MachO) {
2859 Ok(done) => done,
2860 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2861 };
2862 assert!(done.subsections);
2863 let names: Vec<&str> = done.parts.iter().map(|part| part.name.as_str()).collect();
2864 assert_eq!(
2865 names,
2866 ["__TEXT,__text", "__DATA,__data", "__DATA,__bss", "__DATA,__thread_data"]
2867 );
2868 assert!(done.parts[0].shape.exec && done.parts[3].shape.thread);
2869 assert_eq!(done.parts[1].bytes, [3, 0, 0, 0, 4, 0, 0, 0]);
2871 assert_eq!((done.parts[2].size, done.parts[2].align), (400, 16));
2872 let named = |name: &str| done.names.iter().find(|n| n.name == name).unwrap();
2873 assert_eq!(named("_main").visibility, Visibility::Hidden);
2874 assert_eq!(named("_counter").binding, Binding::Weak);
2875 assert_eq!(named("_zeros").at, Held::In { part: 2, offset: 0 });
2876 assert_eq!(done.parts[0].relocs.len(), 2);
2877 }
2878
2879 fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
2881 let part = assembled
2882 .parts
2883 .iter()
2884 .find(|part| part.name == name)
2885 .unwrap_or_else(|| panic!("there is no section called '{name}'"));
2886 part.bytes.clone()
2887 }
2888
2889 fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
2891 assembled
2892 .names
2893 .iter()
2894 .find(|name| name.name == want)
2895 .unwrap_or_else(|| panic!("there is no name called '{want}'"))
2896 }
2897
2898 fn refused(text: &str) -> Trouble {
2900 read(text, Arch::X86_64)
2901 .err()
2902 .unwrap_or_else(|| panic!("this was read and should not have been"))
2903 }
2904
2905 fn aarch64(text: &str) -> Assembled {
2907 match read(text, Arch::Aarch64) {
2908 Ok(assembled) => assembled,
2909 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2910 }
2911 }
2912
2913 fn words(assembled: &Assembled, name: &str) -> Vec<u32> {
2915 let bytes = bytes(assembled, name);
2916 bytes.chunks(4).map(|word| u32::from_le_bytes(word.try_into().unwrap())).collect()
2917 }
2918
2919 #[test]
2920 fn an_aarch64_branch_in_the_file_is_filled_in_and_a_name_is_left_to_the_linker() {
2921 let read = aarch64(concat!(
2922 "# 1 \"f.s\"\n",
2923 "f:\tcbz x0, 1f // to the return\n",
2924 "\tbl g\n",
2925 "\tadrp x1, table+8\n",
2926 "\tadd x1, x1, :lo12:table+8\n",
2927 "1:\tret\n",
2928 "\t.p2align 3\n",
2929 "\t.word 5\n",
2930 ));
2931 assert_eq!(
2933 words(&read, ".text"),
2934 [0xb400_0080, 0x9400_0000, 0x9000_0001, 0x9100_0021, 0xd65f_03c0, 0xd503_201f, 5]
2935 );
2936 let text = read.parts.iter().find(|part| part.name == ".text").unwrap();
2937 let relocs: Vec<_> =
2938 text.relocs.iter().map(|r| (r.at, r.symbol.as_str(), r.kind, r.addend)).collect();
2939 assert_eq!(
2940 relocs,
2941 [
2942 (4, "g", Reference::Field(aarch64::Fixup::Call26), 0),
2943 (8, "table", Reference::Field(aarch64::Fixup::AdrPage21), 8),
2944 (12, "table", Reference::Field(aarch64::Fixup::AddLo12), 8),
2945 ]
2946 );
2947 }
2948
2949 #[test]
2950 fn an_aarch64_frame_starts_at_the_stack_pointer_with_the_return_address_in_x30() {
2951 let read = aarch64(concat!(
2952 "f:\n\t.cfi_startproc\n\tstr x19, [sp, #-16]!\n\t.cfi_def_cfa_offset 16\n",
2953 "\t.cfi_offset x19, -16\n\tldr x19, [sp], #16\n\t.cfi_restore 19\n",
2954 "\t.cfi_def_cfa_offset 0\n\tret\n\t.cfi_endproc\n",
2955 ));
2956 let frame = bytes(&read, ".eh_frame");
2957 assert_eq!(&frame[12..20], &[1, 0x78, 30, 1, 0x1b, 0x0c, 31, 0]);
2961 assert_eq!(&frame[20..24], &[0; 4]);
2962 assert!(frame.windows(5).any(|w| w == [0x44, 0x0e, 16, 0x93, 2]), "{frame:x?}");
2964 }
2965
2966 #[test]
2967 fn an_aarch64_line_that_is_not_an_instruction_is_refused_with_its_line() {
2968 let trouble = read("f:\n\tadd x0, x1, #zz\n", Arch::Aarch64).unwrap_err();
2969 assert_eq!(trouble.line, 2);
2970 let trouble = read("\tb 1f\n", Arch::Aarch64).unwrap_err();
2971 assert!(trouble.why.contains("'1f'"), "{trouble}");
2972 let trouble = read("\tcbz x0, far\n\t.skip 2000000\nfar:\tret\n", Arch::Aarch64);
2973 assert!(trouble.unwrap_err().why.contains("R_AARCH64_CONDBR19"));
2974 }
2975
2976 #[test]
2977 fn a_repeat_prefix_is_read_with_the_string_instruction_behind_it() {
2978 let assembled =
2979 assembled("\t.text\n\trep movsl\n\trepnz scasb\n\trepz cmpsb\n\trep stosq\n");
2980 assert_eq!(
2981 bytes(&assembled, ".text"),
2982 [0xF3, 0xA5, 0xF2, 0xAE, 0xF3, 0xA6, 0xF3, 0x48, 0xAB]
2983 );
2984 }
2985
2986 #[test]
2987 fn notrack_is_read_with_the_jump_behind_it() {
2988 let assembled = assembled("\t.text\n\tnotrack jmp\t*%rax\n\tnotrack jmp *%r8\n\tleave\n");
2989 assert_eq!(bytes(&assembled, ".text"), [0x3E, 0xFF, 0xE0, 0x3E, 0x41, 0xFF, 0xE0, 0xC9]);
2990 }
2991
2992 #[test]
2993 fn rep_bsf_is_read_as_tzcnt() {
2994 let assembled = assembled("\t.text\n\trep bsfq\t-8(%rbp), %rax\n\trep bsfl %edi, %eax\n");
2995 assert_eq!(
2996 bytes(&assembled, ".text"),
2997 [0xF3, 0x48, 0x0F, 0xBC, 0x45, 0xF8, 0xF3, 0x0F, 0xBC, 0xC7]
2998 );
2999 }
3000
3001 #[test]
3007 fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
3008 let out =
3009 assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
3010 let text = bytes(&out, ".text");
3011 assert_eq!(text, vec![0x90, 0xeb, 0xfd, 0x90, 0xeb, 0x01, 0x90, 0xc3]);
3014 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
3015 let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
3017 assert_eq!(written, vec!["foo"]);
3018 }
3019
3020 #[test]
3021 fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
3022 let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
3023 assert!(back.why.contains("none above it"), "{}", back.why);
3024 let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
3025 assert!(forward.why.contains("none below it"), "{}", forward.why);
3026 }
3027
3028 #[test]
3035 fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
3036 let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
3037 assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
3038 let split = assembled("\t.text\n\trep\n\tmovsq\n");
3039 assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
3040 let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
3041 assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
3042 }
3043
3044 #[test]
3051 fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
3052 let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
3053 let relocs = &out.parts[0].relocs;
3054 assert_eq!(relocs.len(), 1);
3055 assert_eq!(relocs[0].symbol, "table");
3056 assert_eq!(relocs[0].kind, Reference::Got);
3057 assert_eq!(relocs[0].addend, -4);
3060 let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
3061 assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
3062 }
3063
3064 #[test]
3068 fn only_an_instruction_the_linker_can_rewrite_asks_it_to() {
3069 for (line, kind) in [
3070 ("movq f@GOTPCREL(%rip), %rax", Reference::Got),
3071 ("cmpq f@GOTPCREL(%rip), %rdx", Reference::Got),
3072 ("addq f@GOTPCREL(%rip), %rdx", Reference::Got),
3073 ("movl f@GOTPCREL(%rip), %eax", Reference::GotBare),
3074 ("call *f@GOTPCREL(%rip)", Reference::GotBare),
3075 ("jmp *f@GOTPCREL(%rip)", Reference::GotBare),
3076 ("movq f@GOTPCREL(%rip), %xmm0", Reference::GotKept),
3077 ("movhps f@GOTPCREL(%rip), %xmm0", Reference::GotKept),
3078 ("movq %rax, f@GOTPCREL(%rip)", Reference::GotKept),
3079 ("movw f@GOTPCREL(%rip), %ax", Reference::GotKept),
3080 ] {
3081 let out = assembled(&format!("\t.text\n\t{line}\n"));
3082 assert_eq!(out.parts[0].relocs[0].kind, kind, "{line}");
3083 }
3084 }
3085
3086 #[test]
3092 fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
3093 let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
3094 let relocs = &out.parts[0].relocs;
3095 assert_eq!(relocs.len(), 1);
3096 assert_eq!(relocs[0].symbol, "table");
3097 assert_eq!(relocs[0].addend, -516);
3098 let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
3101 assert_eq!(named, ["table"]);
3102 }
3103
3104 #[test]
3105 fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
3106 refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
3109 }
3110
3111 #[test]
3112 fn the_probe_gmp_writes() {
3113 let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
3117 assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
3118 let foo = name(&out, "foo");
3119 assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
3120 assert_eq!(foo.binding, Binding::Global);
3121 }
3122
3123 #[test]
3124 fn every_width_of_number_is_the_bytes_it_says_it_is() {
3125 let out = assembled(
3126 "\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",
3127 );
3128 let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
3129 want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
3130 assert_eq!(bytes(&out, ".data"), want);
3131 }
3132
3133 #[test]
3134 fn octa_is_sixteen_bytes_of_a_number_as_wide_as_that() {
3135 let out = assembled(
3136 "\t.data\n\t.octa 0x000102030405060708090a0b0c0d0e0f\n\t.octa -1, 2\n\t.octa 1+2\n",
3137 );
3138 let mut want: Vec<u8> = (0..16).rev().collect();
3139 want.extend_from_slice(&[0xff; 16]);
3140 want.extend_from_slice(&2u128.to_le_bytes());
3141 want.extend_from_slice(&3u128.to_le_bytes());
3142 assert_eq!(bytes(&out, ".data"), want);
3143 }
3144
3145 #[test]
3146 fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
3147 let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
3150 assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
3151 }
3152
3153 #[test]
3154 fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
3155 let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
3156 assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
3157 }
3158
3159 #[test]
3160 fn space_and_fill_put_that_many_bytes_there() {
3161 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");
3162 assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
3163 }
3164
3165 #[test]
3166 fn aligning_moves_on_to_the_boundary_and_no_further() {
3167 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");
3170 let data = bytes(&out, ".data");
3171 assert_eq!(data.len(), 17);
3172 assert_eq!(data[0], 1);
3173 assert_eq!(data[8], 2);
3174 assert_eq!(data[16], 3);
3175 assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
3176 }
3177
3178 #[test]
3179 fn a_fill_of_the_nop_byte_in_code_is_long_nops_the_way_gas_writes_them() {
3180 let asked = assembled("\t.text\n\tret\n\t.align 16, 0x90\n\tret\n");
3181 let plain = assembled("\t.text\n\tret\n\t.align 16\n\tret\n");
3182 assert_eq!(bytes(&asked, ".text"), bytes(&plain, ".text"));
3183 assert_ne!(
3184 bytes(&asked, ".text")[1],
3185 0x90,
3186 "fifteen bytes of padding are not one byte long"
3187 );
3188 let other = assembled("\t.text\n\tret\n\t.align 4, 0xcc\n\tret\n");
3190 assert_eq!(bytes(&other, ".text"), vec![0xc3, 0xcc, 0xcc, 0xcc, 0xc3]);
3191 let data = assembled("\t.data\n\t.byte 1\n\t.align 4, 0x90\n");
3192 assert_eq!(bytes(&data, ".data"), vec![1, 0x90, 0x90, 0x90]);
3193 }
3194
3195 #[test]
3196 fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
3197 let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
3198 let part = &out.parts[0];
3199 assert_eq!(part.name, ".bss");
3200 assert_eq!(part.size, 4096);
3201 assert!(part.bytes.is_empty(), "the zeroes were carried after all");
3202 assert!(!part.shape.bits);
3203 }
3204
3205 #[test]
3206 fn what_a_section_directive_said_about_a_section_is_what_it_is() {
3207 let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
3208 let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
3209 assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
3210 assert!(!part.shape.write, "nothing said it was writable");
3211 }
3212
3213 #[test]
3214 fn a_section_gas_knows_by_name_keeps_the_flags_the_name_gives_it() {
3215 let out = assembled(
3216 "\t.section .data.rel.ro.local,\"a\",@progbits\n\t.quad 0\n\
3217 \t.section .text.hot,\"a\",@progbits\n\t.byte 0x90\n\
3218 \t.section .init.data,\"aw\",@progbits\n\t.byte 1\n",
3219 );
3220 let shape = |name: &str| out.parts.iter().find(|part| part.name == name).unwrap().shape;
3221 assert!(shape(".data.rel.ro.local").write, "a jump table the linker cannot fix up");
3222 assert!(shape(".text.hot").exec);
3223 assert!(!shape(".init.data").exec, "only `.init` itself is code");
3224 }
3225
3226 #[test]
3227 fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
3228 let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
3229 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
3230 assert_eq!(bytes(&out, ".text"), vec![0x90]);
3231 }
3232
3233 #[test]
3234 fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
3235 let out = assembled(
3236 "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
3237 );
3238 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
3239 assert_eq!(bytes(&out, ".rodata"), vec![9]);
3240 }
3241
3242 #[test]
3243 fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
3244 let out = assembled(
3247 "\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",
3248 );
3249 let f = name(&out, "f");
3250 assert_eq!(f.size, 5);
3251 assert_eq!(f.sort, Sort::Func);
3252 }
3253
3254 #[test]
3255 fn a_set_may_name_something_further_down_the_file() {
3256 let out = assembled(
3259 "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
3260 table_end - table\n",
3261 );
3262 assert_eq!(name(&out, "width").at, Held::Absolute(12));
3263 }
3264
3265 #[test]
3266 fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
3267 let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
3268 assert_eq!(name(&out, "a").at, Held::Absolute(11));
3269 assert_eq!(name(&out, "b").at, Held::Absolute(10));
3270 }
3271
3272 #[test]
3273 fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
3274 let why = refused("\t.set a, b\n\t.set b, a\n");
3275 assert!(why.why.contains("neither has a value"), "{why}");
3276 }
3277
3278 #[test]
3279 fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
3280 let out = assembled("\t.data\n\t.quad message\n");
3281 let reloc = &out.parts[0].relocs[0];
3282 assert_eq!(reloc.at, 0);
3283 assert_eq!(reloc.symbol, "message");
3284 assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
3285 assert_eq!(reloc.addend, 0);
3286 assert_eq!(name(&out, "message").at, Held::Undefined);
3287 }
3288
3289 #[test]
3290 fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
3291 let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
3294 let reloc = &out.parts[0].relocs[0];
3295 assert_eq!(reloc.at, 8);
3296 assert_eq!(reloc.symbol, "message");
3297 assert_eq!(reloc.kind, Reference::Data);
3298 assert_eq!(reloc.addend, 0);
3299 }
3300
3301 #[test]
3302 fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
3303 let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
3308 let reloc = &out.parts[0].relocs[0];
3309 assert_eq!(reloc.at, 8);
3310 assert_eq!(reloc.kind, Reference::Data);
3311 assert_eq!(reloc.addend, 8);
3312 }
3313
3314 #[test]
3315 fn a_number_added_to_a_name_rides_along_in_the_addend() {
3316 let out = assembled("\t.data\n\t.quad message + 16\n");
3317 assert_eq!(out.parts[0].relocs[0].addend, 16);
3318 }
3319
3320 #[test]
3321 fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
3322 let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
3323 assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
3324 assert_eq!(name(&out, "shared").binding, Binding::Global);
3325 assert_eq!(name(&out, "mine").binding, Binding::Local);
3328 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
3329 }
3330
3331 #[test]
3332 fn comm_of_a_name_said_to_be_local_is_room_here_as_lcomm_is() {
3333 let out = assembled("\t.local mine\n\t.comm mine, 8, 8\n");
3334 assert_eq!(name(&out, "mine").binding, Binding::Local);
3335 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
3336 }
3337
3338 #[test]
3339 fn what_a_file_says_about_who_can_see_a_name_is_kept() {
3340 let out = assembled(
3341 "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
3342 inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
3343 );
3344 assert_eq!(name(&out, "seen").binding, Binding::Global);
3345 assert_eq!(name(&out, "maybe").binding, Binding::Weak);
3346 assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
3347 }
3348
3349 #[test]
3350 fn the_name_of_the_file_is_a_symbol_of_its_own() {
3351 let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
3354 assert_eq!(out.names[0].name, "big.s");
3355 assert_eq!(out.names[0].sort, Sort::File);
3356 assert_eq!(out.names[0].binding, Binding::Local);
3357 assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
3358 }
3359
3360 #[test]
3361 fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
3362 let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
3365 assert!(out.names.is_empty(), "{:?}", out.names);
3366 }
3367
3368 #[test]
3369 fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
3370 let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\taesenc %xmm1, %xmm0\n\tret\n");
3374 assert_eq!(why.line, 4);
3375 assert!(why.why.contains("aesenc"), "{why}");
3376 }
3377
3378 #[test]
3379 fn a_function_of_instructions_is_its_bytes_and_its_size() {
3380 let out = assembled(
3383 "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
3384 id, .-id\n",
3385 );
3386 assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
3387 assert_eq!(name(&out, "id").size, 4);
3388 assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
3389 }
3390
3391 #[test]
3392 fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
3393 let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
3397 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0, 0xc3]);
3398 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
3399 }
3400
3401 #[test]
3402 fn a_jump_backwards_is_the_negative_distance_to_it() {
3403 let out = assembled("\t.text\nagain:\n\tjmp again\n");
3404 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0xfe]);
3405 }
3406
3407 #[test]
3408 fn a_branch_is_as_short_as_the_distance_lets_it_be() {
3409 let out = assembled("\tjne far\n\t.zero 127\nfar:\n\tret\n");
3412 assert_eq!(bytes(&out, ".text")[..2], [0x75, 127]);
3413 let out = assembled("\tjne far\n\t.zero 128\nfar:\n\tret\n");
3414 assert_eq!(bytes(&out, ".text")[..6], [0x0f, 0x85, 128, 0, 0, 0]);
3415 let out = assembled("back:\n\t.zero 126\n\tjmp back\n");
3416 assert_eq!(bytes(&out, ".text")[126..], [0xeb, 0x80]);
3417 let out = assembled("back:\n\t.zero 127\n\tjmp back\n");
3418 assert_eq!(bytes(&out, ".text")[127..], [0xe9, 0x7c, 0xff, 0xff, 0xff]);
3419 }
3420
3421 #[test]
3422 fn a_branch_made_long_can_push_another_one_out_of_reach() {
3423 let out = assembled("\tjmp a\n\t.zero 125\n\tjmp b\na:\n\t.zero 128\nb:\n\tret\n");
3427 let text = bytes(&out, ".text");
3428 assert_eq!(text[..5], [0xe9, 130, 0, 0, 0]);
3429 assert_eq!(text[130..135], [0xe9, 128, 0, 0, 0]);
3430 }
3431
3432 #[test]
3433 fn a_jump_past_an_alignment_is_judged_the_way_gas_judges_it() {
3434 let out = assembled(
3441 "\tjmp far1\n\tje far1\n\tje far2\n\t.zero 123\n\t.p2align 3\nfar2:\n\tret\n\t.zero \
3442 200\nfar1:\n\tret\n",
3443 );
3444 let text = bytes(&out, ".text");
3445 assert_eq!(text[..13], [0xe9, 0x4c, 1, 0, 0, 0x0f, 0x84, 0x46, 1, 0, 0, 0x74, 123]);
3446 assert_eq!(text.len(), 0x152);
3447 }
3448
3449 #[test]
3450 fn a_branch_that_leaves_the_section_or_goes_to_a_weak_name_is_long() {
3451 let out = assembled("\tjmp elsewhere\n\tjz maybe\n\t.weak maybe\nmaybe:\n\tret\n");
3453 assert_eq!(bytes(&out, ".text")[..1], [0xe9]);
3454 assert_eq!(bytes(&out, ".text")[5..7], [0x0f, 0x84]);
3455 }
3456
3457 #[test]
3458 fn a_section_of_constants_says_how_long_each_one_is() {
3459 let out = assembled(
3460 "\t.section .rodata.str1.1,\"aMS\",@progbits,1\n\t.string \"hi\"\n\t\
3461 .section .rodata.cst8,\"aM\",@progbits,8\n\t.quad 1\n\t.section .rodata.x,\"aM\"\n\t.byte 1\n",
3462 );
3463 let shapes: Vec<_> =
3464 out.parts.iter().map(|part| (part.shape.merge, part.shape.strings)).collect();
3465 assert_eq!(shapes, [(1, true), (8, false), (0, false)]);
3466 }
3467
3468 #[test]
3469 fn a_global_name_defined_here_is_still_left_to_the_linker() {
3470 let out = assembled(
3474 "\t.globl f\nf:\n\tcall f\n\tjmp f\n\tleaq f(%rip), %rax\n\tcall g\n\t\
3475 .long f - g\ng:\n\tret\n",
3476 );
3477 let relocs = &out.parts[0].relocs;
3478 let kinds: Vec<_> = relocs.iter().map(|r| (r.at, r.symbol.as_str(), r.kind)).collect();
3479 assert_eq!(kinds, [(1, "f", Reference::Call), (10, "f", Reference::Data)]);
3480 assert!(relocs.iter().all(|r| r.addend == -4));
3481 let text = bytes(&out, ".text");
3482 assert_eq!(text[..7], [0xe8, 0, 0, 0, 0, 0xeb, 0xf9]);
3483 assert_eq!(text[14..19], [0xe8, 4, 0, 0, 0]);
3484 assert_eq!(text[19..23], (-23i32).to_le_bytes());
3485 }
3486
3487 #[test]
3488 fn a_call_to_a_static_name_in_another_section_needs_no_stub() {
3489 let out = assembled("\t.text\n\tcall cold\n\t.section .text.unlikely\ncold:\n\tret\n");
3490 let reloc = &out.parts[0].relocs[0];
3491 assert_eq!((reloc.symbol.as_str(), reloc.kind), ("cold", Reference::Data));
3492 }
3493
3494 #[test]
3495 fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
3496 let out = assembled("\t.text\n\tcall puts\n");
3502 let reloc = &out.parts[0].relocs[0];
3503 assert_eq!(reloc.at, 1);
3504 assert_eq!(reloc.symbol, "puts");
3505 assert_eq!(reloc.kind, Reference::Call);
3506 assert_eq!(reloc.addend, -4);
3507 }
3508
3509 #[test]
3510 fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
3511 let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
3512 let reloc = &out.parts[0].relocs[0];
3513 assert_eq!(reloc.symbol, "message");
3514 assert_eq!(reloc.kind, Reference::Data);
3515 assert_eq!(reloc.at, 3);
3517 assert_eq!(reloc.addend, -4);
3518 }
3519
3520 #[test]
3521 fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
3522 let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
3525 assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
3526 }
3527
3528 #[test]
3529 fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
3530 let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
3534 assert_eq!(why.line, 2);
3535 assert!(why.why.contains("does not reach"), "{why}");
3536 }
3537
3538 #[test]
3539 fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
3540 let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
3545 assert_eq!(bytes(&out, ".data")[200], 200);
3546 let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
3547 assert!(why.why.contains("does not reach"), "{why}");
3548 }
3549
3550 #[test]
3551 fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
3552 let why = refused("\t.bss\n\tret\n");
3553 assert!(why.why.contains("holds no bytes"), "{why}");
3554 }
3555
3556 #[test]
3557 fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
3558 let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
3559 assert_eq!(why.line, 3);
3560 assert!(why.why.contains(".reloc"), "{why}");
3561 }
3562
3563 #[test]
3564 fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
3565 let out = assembled(
3568 "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
3569 3\n",
3570 );
3571 assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
3572 }
3573
3574 #[test]
3575 fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
3576 let why = refused("\t.data\n\t/* and then nothing\n");
3577 assert!(why.why.contains("never closed"), "{why}");
3578 }
3579
3580 #[test]
3581 fn a_string_with_a_comment_character_in_it_is_a_string() {
3582 let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
3583 assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
3584 }
3585
3586 #[test]
3587 fn several_statements_on_one_line_are_several_statements() {
3588 let out = assembled("\t.data; .byte 1; .byte 2\n");
3589 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
3590 }
3591
3592 #[test]
3593 fn a_section_nothing_was_ever_put_in_is_dropped() {
3594 let out = assembled("\t.data\n\t.byte 1\n");
3597 assert_eq!(out.parts.len(), 1);
3598 assert_eq!(out.parts[0].name, ".data");
3599 }
3600
3601 #[test]
3602 fn a_section_with_nothing_in_it_but_a_name_is_kept() {
3603 let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
3606 assert_eq!(out.parts.len(), 1);
3607 assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
3608 }
3609
3610 #[test]
3611 fn an_error_directive_is_the_file_saying_it_refuses_itself() {
3612 let why = refused("\t.error \"this is not the machine for it\"\n");
3613 assert!(why.why.contains("not the machine for it"), "{why}");
3614 }
3615
3616 #[test]
3617 fn a_jump_counted_from_itself_is_the_short_one_gas_writes() {
3618 let out = assembled("\tjmp .+6\n\t.int 123\n\tmov .-4(%rip), %eax\n");
3621 assert_eq!(
3622 bytes(&out, ".text"),
3623 vec![0xeb, 0x04, 123, 0, 0, 0, 0x8b, 0x05, 0xf6, 0xff, 0xff, 0xff]
3624 );
3625 }
3626
3627 #[test]
3628 fn a_numbered_label_in_an_expression_is_a_place() {
3629 let out =
3630 assembled("2:\n\tjmp .+6\n1:\n\t.pushsection .data\n\t.long 1b - 2b\n\t.popsection\n");
3631 assert_eq!(bytes(&out, ".data"), vec![2, 0, 0, 0]);
3632 let out = assembled("\t.data\n\t.byte 0b101\n");
3634 assert_eq!(bytes(&out, ".data"), vec![5]);
3635 }
3636
3637 #[test]
3638 fn a_number_an_instruction_carries_may_be_an_expression_over_labels() {
3639 let out = assembled("3:\tmov $4f-3b, %eax\n4:\n");
3640 assert_eq!(bytes(&out, ".text"), vec![0xb8, 5, 0, 0, 0]);
3641 }
3642
3643 #[test]
3644 fn a_number_an_instruction_carries_may_not_name_something_elsewhere() {
3645 let why = refused("\tmov $elsewhere, %eax\n");
3646 assert!(why.why.contains("relocation"), "{why}");
3647 }
3648
3649 #[test]
3650 fn a_name_set_twice_means_what_it_was_where_it_is_used() {
3651 let out = assembled(
3652 "\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",
3653 );
3654 assert_eq!(bytes(&out, ".data"), vec![3, 1, 2]);
3655 }
3656
3657 #[test]
3658 fn a_place_set_twice_and_reached_from_another_section_is_relocated_against() {
3659 let out = assembled(
3660 "\t.data\n\tx = .\n\t.int 1\n\tx = .\n\t.int 2\n\t.text\n\tmov x(%rip), %eax\n",
3661 );
3662 let reloc = &out.parts.iter().find(|part| part.name == ".text").unwrap().relocs[0];
3663 let target = name(&out, &reloc.symbol);
3664 let data = out.parts.iter().position(|part| part.name == ".data").unwrap();
3665 assert_eq!(target.at, Held::In { part: data, offset: 4 });
3666 }
3667
3668 #[test]
3669 fn frame_rules_are_an_unwind_table_pointing_at_the_function() {
3670 let out = assembled(
3671 "f:\n\t.cfi_startproc\n\tpush %rbp\n\t.cfi_def_cfa_offset 16\n\t.cfi_offset %rbp, \
3672 -16\n\tpop %rbp\n\t.cfi_def_cfa_offset 8\n\tret\n\t.cfi_endproc\n",
3673 );
3674 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3675 let rows = [0x41, 0x0e, 0x10, 0x86, 0x02, 0x41, 0x0e, 0x08];
3678 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3679 let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
3680 let text = out.parts.iter().position(|part| part.name == ".text").unwrap();
3681 assert_eq!(name(&out, &reloc.symbol).at, Held::In { part: text, offset: 0 });
3682 }
3683
3684 #[test]
3685 fn a_frame_rule_relative_to_the_register_is_the_same_slot() {
3686 let out = assembled(
3687 "\t.cfi_startproc\n\tpush %rbx\n\t.cfi_adjust_cfa_offset 8\n\t.cfi_rel_offset \
3688 %rbx, 0\n\t.cfi_endproc\n",
3689 );
3690 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3691 let rows = [0x41, 0x0e, 0x10, 0x83, 0x02];
3692 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3693 }
3694
3695 #[test]
3696 fn frame_rules_for_a_debugger_only_are_no_unwind_table() {
3697 let out =
3698 assembled("\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\tret\n\t.cfi_endproc\n");
3699 assert!(out.parts.iter().all(|part| part.name != ".eh_frame"));
3700 }
3701
3702 #[test]
3703 fn a_frame_rule_outside_a_function_or_a_function_never_ended_is_refused() {
3704 let why = refused("\t.cfi_def_cfa_offset 16\n");
3705 assert!(why.why.contains("outside"), "{why}");
3706 let why = refused("\t.cfi_startproc\n\tret\n");
3707 assert!(why.why.contains("never ended"), "{why}");
3708 }
3709}