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