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::ObjectFormat;
38use rucc_target::x86_64::{SYSV, gpr_named};
39
40use crate::instruction::Sort as Reach;
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Trouble {
47 pub line: usize,
50 pub why: String,
52}
53
54impl std::fmt::Display for Trouble {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(f, "{}: {}", self.line, self.why)
57 }
58}
59
60impl std::error::Error for Trouble {}
61
62pub fn read(text: &str) -> Result<Assembled, Trouble> {
70 let mut reader = Reader::default();
71 reader.run(text)?;
72 reader.finish()
73}
74
75#[derive(Debug, Clone)]
81struct Sym {
82 name: String,
83 at: Held,
84 size: u64,
85 sort: Sort,
86 binding: Binding,
87 visibility: Visibility,
88 numbered: bool,
91}
92
93#[derive(Debug, Clone)]
95struct Fixup {
96 part: usize,
97 at: u64,
98 width: u8,
99 sum: Sum,
100 reach: Reach,
104 line: usize,
105}
106
107#[derive(Debug)]
109struct Frame {
110 part: usize,
111 start: u64,
112 len: u64,
113 sym: usize,
116 rows: crate::unwind::Rows,
117 cfa: i32,
120 remembered: Vec<i32>,
122}
123
124#[derive(Debug, Default)]
126struct Reader {
127 parts: Vec<Part>,
128 named: HashMap<String, usize>,
130 here: usize,
132 stack: Vec<usize>,
134 before: Option<usize>,
136 syms: Vec<Sym>,
137 known: HashMap<String, usize>,
138 counts: HashMap<String, usize>,
141 labelled: std::collections::HashSet<usize>,
144 fixups: Vec<Fixup>,
145 sets: Vec<(usize, Sum, usize)>,
147 sizes: Vec<(usize, Sum, usize)>,
149 current: HashMap<String, String>,
153 relocated: std::collections::HashSet<usize>,
157 frame: Option<Frame>,
159 frames: Vec<Frame>,
161 no_unwind: bool,
164 files: Vec<String>,
168 line: usize,
169}
170
171impl Reader {
172 fn run(&mut self, text: &str) -> Result<(), Trouble> {
174 self.section(".text", Shape::of(".text"));
177 let mut commenting = false;
178 for (index, raw) in text.lines().enumerate() {
179 self.line = index + 1;
180 let line = self.strip(raw, &mut commenting)?;
181 for statement in split(&line, ';') {
182 self.statement(statement.trim())?;
183 }
184 }
185 if commenting {
186 return Err(self.bad("a block comment was opened and never closed"));
187 }
188 Ok(())
189 }
190
191 fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
198 let mut out = String::with_capacity(raw.len());
199 let bytes = raw.as_bytes();
200 let mut i = 0;
201 let mut quote = None;
202 while i < bytes.len() {
203 let rest = &raw[i..];
204 if *commenting {
205 if let Some(end) = rest.find("*/") {
206 *commenting = false;
207 out.push(' ');
210 i += end + 2;
211 } else {
212 return Ok(out);
213 }
214 continue;
215 }
216 let ch = bytes[i] as char;
217 if let Some(mark) = quote {
218 out.push(ch);
219 if ch == '\\' && i + 1 < bytes.len() {
220 out.push(bytes[i + 1] as char);
221 i += 2;
222 continue;
223 }
224 if ch == mark {
225 quote = None;
226 }
227 i += 1;
228 continue;
229 }
230 if ch == '"' {
231 quote = Some('"');
232 out.push(ch);
233 i += 1;
234 continue;
235 }
236 if rest.starts_with("/*") {
237 *commenting = true;
238 i += 2;
239 continue;
240 }
241 if rest.starts_with("//") || ch == '#' {
242 return Ok(out);
243 }
244 out.push(ch);
245 i += 1;
246 }
247 if quote.is_some() {
248 return Err(self.bad("a string was opened and the line ended before it closed"));
249 }
250 Ok(out)
251 }
252
253 fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
255 loop {
256 text = text.trim_start();
257 let Some(name) = labelled(text) else { break };
258 self.label(&name)?;
259 text = &text[name.len() + 1..];
260 }
261 let text = text.trim();
262 if text.is_empty() {
263 return Ok(());
264 }
265 let (word, rest) = match text.find(char::is_whitespace) {
266 Some(cut) => (&text[..cut], text[cut..].trim()),
267 None => (text, ""),
268 };
269 if let Some((name, what)) = assigned(text) {
270 return self.assign(name, what);
271 }
272 if let Some(directive) = word.strip_prefix('.') {
273 return self.directive(directive, rest);
274 }
275 if let Some((word, rest)) = repeated(word, rest) {
276 return self.instruction(&word, rest);
277 }
278 self.instruction(word, rest)
279 }
280
281 fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
295 let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
296 let written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
297 let part = self.here;
298 let at = self.at();
299 self.put(&written.bytes)?;
300 let end = at + written.bytes.len() as u64;
301 for hole in written.holes {
302 let here = (part, at as i64);
305 let sum = if hole.sort == Reach::Value {
306 self.expression_at(&hole.name, here)?
308 } else {
309 let what = if hole.name == "." {
310 What::Here { part, at: here.1 }
311 } else {
312 let name = self.named(&hole.name)?;
315 self.sym(&name);
316 What::Symbol(name)
317 };
318 Sum {
319 constant: hole.addend,
320 terms: vec![
321 Term { coeff: 1, what },
322 Term { coeff: -1, what: What::Here { part, at: end as i64 } },
323 ],
324 }
325 };
326 self.fixups.push(Fixup {
327 part,
328 at: at + hole.at as u64,
329 width: hole.width,
330 sum,
331 reach: hole.sort,
332 line: self.line,
333 });
334 }
335 Ok(())
336 }
337
338 fn label(&mut self, name: &str) -> Result<(), Trouble> {
340 let at = self.at();
341 let part = self.here;
342 let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
345 let held = if numbered {
346 let count = self.counts.entry(name.to_owned()).or_insert(0);
347 *count += 1;
348 counted(name, *count)
349 } else {
350 name.to_owned()
351 };
352 let sym = self.sym(&held);
353 if self.syms[sym].at != Held::Undefined {
354 let what = format!("'{name}' is defined twice");
355 return Err(self.bad(&what));
356 }
357 self.syms[sym].at = Held::In { part, offset: at };
358 self.labelled.insert(part);
359 Ok(())
360 }
361
362 fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
369 let Some(number) = word.strip_suffix(['b', 'f']) else {
370 return Ok(None);
371 };
372 if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
373 return Ok(None);
374 }
375 let count = self.counts.get(number).copied().unwrap_or(0);
376 if word.ends_with('b') {
377 if count == 0 {
378 let what =
379 format!("'{word}' goes back to a '{number}:' and there is none above it");
380 return Err(self.bad(&what));
381 }
382 return Ok(Some(counted(number, count)));
383 }
384 Ok(Some(counted(number, count + 1)))
385 }
386
387 fn named(&self, word: &str) -> Result<String, Trouble> {
392 if let Some(place) = self.numbered(word)? {
393 return Ok(place);
394 }
395 Ok(self.current.get(word).cloned().unwrap_or_else(|| word.to_owned()))
396 }
397
398 fn assign(&mut self, name: &str, what: &str) -> Result<(), Trouble> {
406 let sum = self.expression(what)?;
407 let held = match self.current.get(name) {
408 Some(_) => format!("{name}\u{1}={}", self.syms.len()),
409 None => name.to_owned(),
410 };
411 let sym = self.sym(&held);
412 if self.syms[sym].at != Held::Undefined {
413 let what = format!("'{name}' is defined twice");
414 return Err(self.bad(&what));
415 }
416 self.current.insert(name.to_owned(), held);
417 self.sets.push((sym, sum, self.line));
418 Ok(())
419 }
420
421 fn cfi(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
430 match word {
431 "cfi_startproc" => {
432 if self.frame.is_some() {
433 return Err(self.bad("a '.cfi_startproc' inside another one"));
434 }
435 let sym = self.sym(&format!("\u{1}frame{}", self.frames.len()));
436 let (part, start) = (self.here, self.at());
437 self.syms[sym].at = Held::In { part, offset: start };
438 let frame = Frame {
441 part,
442 start,
443 len: 0,
444 sym,
445 rows: Vec::new(),
446 cfa: 8,
447 remembered: Vec::new(),
448 };
449 self.frame = Some(frame);
450 return Ok(());
451 }
452 "cfi_sections" => {
453 self.no_unwind = !args.iter().any(|arg| arg.trim() == ".eh_frame");
454 return Ok(());
455 }
456 "cfi_endproc"
457 | "cfi_def_cfa"
458 | "cfi_def_cfa_offset"
459 | "cfi_adjust_cfa_offset"
460 | "cfi_def_cfa_register"
461 | "cfi_offset"
462 | "cfi_rel_offset"
463 | "cfi_restore"
464 | "cfi_remember_state"
465 | "cfi_restore_state" => {}
466 _ => return Ok(()),
467 }
468 let (here, at) = (self.here, self.at());
469 let line = self.line;
470 let bad = |why: &str| Trouble { line, why: why.to_owned() };
471 let Some(mut frame) = self.frame.take() else {
472 return Err(bad("a frame rule outside '.cfi_startproc' and '.cfi_endproc'"));
473 };
474 if frame.part != here {
475 return Err(bad("a frame rule in another section from the function it is about"));
476 }
477 let op = match word {
478 "cfi_endproc" => {
479 frame.len = at - frame.start;
480 self.frames.push(frame);
481 return Ok(());
482 }
483 "cfi_def_cfa" => {
484 let [reg, offset] = self.two(args, ".cfi_def_cfa")?;
485 frame.cfa = self.distance(&offset)?;
486 CfiOp::DefCfa { reg: self.dwarf(®)?, offset: frame.cfa }
487 }
488 "cfi_def_cfa_offset" | "cfi_adjust_cfa_offset" => {
489 let by = self.distance(args.first().map_or("", |arg| arg.as_str()))?;
490 frame.cfa = if word == "cfi_def_cfa_offset" { by } else { frame.cfa + by };
491 CfiOp::DefCfaOffset(frame.cfa)
492 }
493 "cfi_def_cfa_register" => {
494 CfiOp::DefCfaRegister(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
495 }
496 "cfi_offset" | "cfi_rel_offset" => {
497 let [reg, offset] = self.two(args, &format!(".{word}"))?;
498 let mut offset = self.distance(&offset)?;
499 if word == "cfi_rel_offset" {
502 offset -= frame.cfa;
503 }
504 if offset >= 0 || offset % 8 != 0 {
505 return Err(bad(
506 "a register saved somewhere that is not a whole slot below the end of the \
507 frame, which is the only place this writes a rule for",
508 ));
509 }
510 CfiOp::Offset { reg: self.dwarf(®)?, offset }
511 }
512 "cfi_restore" => {
513 CfiOp::Restore(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
514 }
515 "cfi_remember_state" => {
516 frame.remembered.push(frame.cfa);
517 CfiOp::RememberState
518 }
519 "cfi_restore_state" => {
520 frame.cfa = frame.remembered.pop().ok_or_else(|| {
521 bad("a '.cfi_restore_state' with nothing remembered to restore")
522 })?;
523 CfiOp::RestoreState
524 }
525 _ => unreachable!("every other word returned above"),
526 };
527 frame.rows.push(((at - frame.start) as usize, op));
528 self.frame = Some(frame);
529 Ok(())
530 }
531
532 fn distance(&mut self, text: &str) -> Result<i32, Trouble> {
534 let value = self.number(text)?;
535 i32::try_from(value).map_err(|_| self.bad(&format!("{value} is not a distance in a frame")))
536 }
537
538 fn dwarf(&self, text: &str) -> Result<u16, Trouble> {
540 let text = text.trim();
541 if let Ok(number) = text.parse::<u16>() {
542 return Ok(number);
543 }
544 let name = text.strip_prefix('%').unwrap_or(text);
545 if name == "rip" {
546 return Ok(SYSV.dwarf_return_address);
547 }
548 gpr_named(name)
549 .and_then(|(reg, _)| SYSV.dwarf(SYSV.int_class, reg))
550 .ok_or_else(|| self.bad(&format!("'{text}' is not a register a frame rule can name")))
551 }
552
553 #[allow(clippy::too_many_lines)]
555 fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
556 let args = split(rest, ',');
557 match word {
558 "text" | "data" | "bss" | "rodata" => {
559 self.plain(word, rest)?;
560 }
561 "section" => self.section_directive(&args)?,
562 "pushsection" => {
563 self.stack.push(self.here);
564 self.section_directive(&args)?;
565 }
566 "popsection" => {
567 let Some(back) = self.stack.pop() else {
568 return Err(self.bad(".popsection with nothing pushed"));
569 };
570 self.go(back);
571 }
572 "previous" => {
573 let Some(back) = self.before else {
574 return Err(self.bad(".previous with no section before this one"));
575 };
576 self.go(back);
577 }
578
579 "byte" => self.data(&args, 1)?,
580 "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
581 "long" | "int" | "4byte" => self.data(&args, 4)?,
582 "quad" | "8byte" => self.data(&args, 8)?,
583
584 "ascii" => self.text_bytes(&args, false)?,
585 "asciz" | "string" => self.text_bytes(&args, true)?,
586
587 "space" | "skip" | "zero" => {
588 if args.is_empty() || args.len() > 2 {
589 return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
590 }
591 let size = self.number(&args[0])?;
592 let size = self.count(size)?;
593 let fill = match args.get(1) {
594 Some(arg) => self.byte(arg)?,
595 None => 0,
596 };
597 self.pad(size, fill)?;
598 }
599 "fill" => {
600 if args.is_empty() || args.len() > 3 {
604 return Err(self.bad(".fill wants a count and an optional width and value"));
605 }
606 let count = self.number(&args[0])?;
607 let count = self.count(count)?;
608 let width = match args.get(1) {
609 Some(arg) => {
610 let width = self.number(arg)?;
611 self.count(width)?
612 }
613 None => 1,
614 };
615 let value = match args.get(2) {
616 Some(arg) => self.number(arg)?,
617 None => 0,
618 };
619 if width > 8 {
620 return Err(self.bad(".fill of items wider than eight bytes is not written"));
621 }
622 let one = value.to_le_bytes();
623 for _ in 0..count {
624 self.put(&one[..width as usize])?;
625 }
626 }
627
628 "align" | "balign" | "p2align" => self.align(word, &args)?,
629 "org" => {
630 let Some(first) = args.first() else {
631 return Err(self.bad(".org with nothing after it"));
632 };
633 let to = self.number(first)?;
634 let to = self.count(to)?;
635 let fill = match args.get(1) {
636 Some(arg) => self.byte(arg)?,
637 None => 0,
638 };
639 let at = self.at();
640 if to < at {
641 let what = format!(".org back to {to} from {at}, which would overwrite bytes");
642 return Err(self.bad(&what));
643 }
644 self.pad(to - at, fill)?;
645 }
646
647 "globl" | "global" => self.bind(&args, Binding::Global)?,
648 "weak" => self.bind(&args, Binding::Weak)?,
649 "local" => self.bind(&args, Binding::Local)?,
650 "hidden" => self.sight(&args, Visibility::Hidden)?,
651 "protected" => self.sight(&args, Visibility::Protected)?,
652 "internal" => self.sight(&args, Visibility::Hidden)?,
655
656 "type" => self.type_directive(&args)?,
657 "err" | "error" => {
658 let what = unquoted(args.first().map_or("", |arg| arg.trim()));
659 return Err(self.bad(&format!("the file says so itself: {what}")));
660 }
661 "size" => {
662 let [name, what] = self.two(&args, ".size")?;
663 let sum = self.expression(&what)?;
664 let sym = self.sym(&name);
665 self.sizes.push((sym, sum, self.line));
666 }
667 "set" | "equ" | "equiv" => {
668 let [name, what] = self.two(&args, &format!(".{word}"))?;
669 self.assign(&name, &what)?;
670 }
671 "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
672
673 "file" => {
677 let what = args.first().map_or("", |arg| arg.trim());
678 if what.starts_with('"') {
679 self.files.push(unquoted(what));
680 }
681 }
682
683 "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
687 | "intel_syntax" | "warning" => {}
688 _ if word.starts_with("cfi_") => self.cfi(word, &args)?,
689
690 _ => {
691 let what = format!(
692 "'.{word}' is a directive this compiler does not know, so nothing was written \
693 for it"
694 );
695 return Err(self.bad(&what));
696 }
697 }
698 Ok(())
699 }
700
701 fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
703 if !rest.trim().is_empty() && rest.trim() != "0" {
708 let what =
709 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
710 return Err(self.bad(&what));
711 }
712 let name = format!(".{word}");
713 let shape = Shape::of(&name);
714 self.section(&name, shape);
715 Ok(())
716 }
717
718 fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
720 let Some(name) = args.first() else {
721 return Err(self.bad(".section with no name"));
722 };
723 let name = unquoted(name.trim());
724 if name.is_empty() {
725 return Err(self.bad(".section with no name"));
726 }
727 let mut shape = Shape::of(&name);
730 if let Some(flags) = args.get(1) {
731 let letters = unquoted(flags.trim());
732 shape = Shape { bits: true, ..Shape::default() };
733 for letter in letters.chars() {
734 match letter {
735 'a' => shape.alloc = true,
736 'w' => shape.write = true,
737 'x' => shape.exec = true,
738 'T' => shape.thread = true,
739 'M' | 'S' | 'G' | 'o' | 'e' | 'R' | 'd' => {}
743 _ => {
744 let what = format!("'{letter}' is not a section flag this compiler knows");
745 return Err(self.bad(&what));
746 }
747 }
748 }
749 }
750 if let Some(kind) = args.get(2) {
751 let kind = kind.trim().trim_start_matches(['@', '%']);
752 let kind = unquoted(kind);
753 match kind.as_str() {
754 "progbits" => shape.bits = true,
755 "nobits" => shape.bits = false,
756 "init_array" => shape.array = Some(Array::Init),
757 "fini_array" => shape.array = Some(Array::Fini),
758 "preinit_array" => shape.array = Some(Array::Preinit),
759 "note" => shape.bits = true,
760 _ => {
761 let what = format!("'{kind}' is not a section type this compiler writes");
762 return Err(self.bad(&what));
763 }
764 }
765 }
766 self.section(&name, shape);
767 Ok(())
768 }
769
770 fn section(&mut self, name: &str, shape: Shape) {
776 if let Some(&at) = self.named.get(name) {
777 self.go(at);
778 return;
779 }
780 let at = self.parts.len();
781 self.parts.push(Part {
782 name: name.to_owned(),
783 bytes: Vec::new(),
784 size: 0,
785 align: 1,
786 shape,
787 relocs: Vec::new(),
788 });
789 self.named.insert(name.to_owned(), at);
790 self.go(at);
791 }
792
793 fn go(&mut self, at: usize) {
795 if at != self.here {
796 self.before = Some(self.here);
797 self.here = at;
798 }
799 }
800
801 fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
803 if args.is_empty() {
804 return Err(self.bad("a data directive with nothing after it"));
805 }
806 for arg in args {
807 let sum = self.expression(arg)?;
808 let at = self.at();
809 if let Some(value) = sum.flat() {
810 self.put(&value.to_le_bytes()[..width as usize])?;
811 continue;
812 }
813 let part = self.here;
816 if !self.parts[part].shape.bits {
817 let what = format!(
818 "'{}' holds no bytes and this asks the linker to write some into it",
819 self.parts[part].name
820 );
821 return Err(self.bad(&what));
822 }
823 self.put(&vec![0u8; width as usize])?;
824 self.fixups.push(Fixup { part, at, width, sum, reach: Reach::Near, line: self.line });
825 }
826 Ok(())
827 }
828
829 fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
831 for arg in args {
832 let mut bytes = self.string(arg.trim())?;
833 if terminated {
834 bytes.push(0);
835 }
836 self.put(&bytes)?;
837 }
838 Ok(())
839 }
840
841 fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
847 let Some(head) = args.first() else {
848 return Err(self.bad(&format!(".{word} with nothing after it")));
849 };
850 let first = self.number(head)?;
851 let first = self.count(first)?;
852 let boundary = if word == "p2align" {
853 if first > 31 {
854 return Err(self.bad(".p2align of more than two gigabytes"));
855 }
856 1u64 << first
857 } else {
858 first
859 };
860 if boundary == 0 || !boundary.is_power_of_two() {
861 let what = format!("an alignment of {boundary}, which is not a power of two");
862 return Err(self.bad(&what));
863 }
864 let default = if self.parts[self.here].shape.exec { 0x90 } else { 0 };
868 let fill = match args.get(1) {
869 Some(arg) if !arg.trim().is_empty() => self.byte(arg)?,
870 _ => default,
871 };
872 let at = self.at();
873 let over = at % boundary;
874 let need = if over == 0 { 0 } else { boundary - over };
875 if let Some(most) = args.get(2).filter(|arg| !arg.trim().is_empty()) {
878 let most = self.number(&most.clone())?;
879 if need > self.count(most)? {
880 return Ok(());
881 }
882 }
883 let part = &mut self.parts[self.here];
884 part.align = part.align.max(boundary);
885 self.pad(need, fill)
886 }
887
888 fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
890 for arg in args {
891 let sym = self.sym(arg.trim());
892 self.syms[sym].binding = binding;
893 }
894 Ok(())
895 }
896
897 fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
899 for arg in args {
900 let sym = self.sym(arg.trim());
901 self.syms[sym].visibility = visibility;
902 }
903 Ok(())
904 }
905
906 fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
908 let [name, what] = self.two(args, ".type")?;
909 let what = unquoted(what.trim().trim_start_matches(['@', '%']));
910 let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
911 "func" | "function" => Sort::Func,
912 "object" | "gnu_unique_object" => Sort::Object,
913 "tls_object" | "tls" => Sort::Thread,
914 "notype" | "" => Sort::Untyped,
915 other => {
916 let what = format!("'{other}' is not a symbol type this compiler writes");
917 return Err(self.bad(&what));
918 }
919 };
920 let sym = self.sym(name.trim());
921 self.syms[sym].sort = sort;
922 Ok(())
923 }
924
925 fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
932 if !(2..=3).contains(&args.len()) {
933 return Err(
934 self.bad("a common directive wants a name, a size and an optional alignment")
935 );
936 }
937 let name = args[0].trim().to_owned();
938 let size = self.number(&args[1])?;
939 let size = self.count(size)?;
940 let align = match args.get(2) {
941 Some(arg) => {
942 let align = self.number(&arg.clone())?;
943 self.count(align)?.max(1)
944 }
945 None => size.next_power_of_two().clamp(1, 16),
948 };
949 if !align.is_power_of_two() {
950 let what = format!("an alignment of {align}, which is not a power of two");
951 return Err(self.bad(&what));
952 }
953 let sym = self.sym(&name);
954 self.syms[sym].sort = Sort::Object;
958 if local {
959 let was = self.here;
960 self.section(".bss", Shape::of(".bss"));
961 let part = &mut self.parts[self.here];
962 part.align = part.align.max(align);
963 let over = part.size % align;
964 if over != 0 {
965 part.size += align - over;
966 }
967 let offset = self.parts[self.here].size;
968 self.parts[self.here].size += size;
969 let at = self.here;
970 self.syms[sym].at = Held::In { part: at, offset };
971 self.syms[sym].size = size;
972 self.syms[sym].binding = Binding::Local;
973 self.go(was);
974 } else {
975 self.syms[sym].at = Held::Common { size, align };
976 self.syms[sym].size = size;
977 self.syms[sym].binding = Binding::Global;
978 }
979 Ok(())
980 }
981
982 fn at(&self) -> u64 {
984 let part = &self.parts[self.here];
985 if part.shape.bits { part.bytes.len() as u64 } else { part.size }
986 }
987
988 fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
990 let part = &mut self.parts[self.here];
991 if !part.shape.bits {
992 if bytes.iter().all(|byte| *byte == 0) {
993 part.size += bytes.len() as u64;
996 return Ok(());
997 }
998 let what = format!("'{}' holds no bytes and this puts some in it", part.name);
999 return Err(Trouble { line: self.line, why: what });
1000 }
1001 part.bytes.extend_from_slice(bytes);
1002 part.size = part.bytes.len() as u64;
1003 Ok(())
1004 }
1005
1006 fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
1008 let part = &mut self.parts[self.here];
1009 if !part.shape.bits {
1010 part.size += count;
1011 return Ok(());
1012 }
1013 part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
1014 part.size = part.bytes.len() as u64;
1015 Ok(())
1016 }
1017
1018 fn sym(&mut self, name: &str) -> usize {
1020 if let Some(&at) = self.known.get(name) {
1021 return at;
1022 }
1023 let at = self.syms.len();
1024 self.syms.push(Sym {
1025 name: name.to_owned(),
1026 at: Held::Undefined,
1027 size: 0,
1028 sort: Sort::Untyped,
1029 binding: Binding::Local,
1033 visibility: Visibility::Default,
1034 numbered: name.contains('\u{1}'),
1037 });
1038 self.known.insert(name.to_owned(), at);
1039 at
1040 }
1041
1042 fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
1044 if args.len() != 2 {
1045 let why = format!("{what} wants two operands and was given {}", args.len());
1046 return Err(Trouble { line: self.line, why });
1047 }
1048 Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
1049 }
1050
1051 fn number(&mut self, text: &str) -> Result<i64, Trouble> {
1053 let sum = self.expression(text)?;
1054 sum.flat().ok_or_else(|| Trouble {
1055 line: self.line,
1056 why: format!("'{}' has to be a number here and it names something", text.trim()),
1057 })
1058 }
1059
1060 fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
1062 let value = self.number(text)?;
1063 u8::try_from(value & 0xff).map_err(|_| Trouble {
1064 line: self.line,
1065 why: format!("{value} does not fit in a byte"),
1066 })
1067 }
1068
1069 fn count(&self, value: i64) -> Result<u64, Trouble> {
1071 u64::try_from(value).map_err(|_| Trouble {
1072 line: self.line,
1073 why: format!("{value} is negative and this is a length"),
1074 })
1075 }
1076
1077 fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
1079 self.expression_at(text, (self.here, self.at() as i64))
1080 }
1081
1082 fn expression_at(&mut self, text: &str, here: (usize, i64)) -> Result<Sum, Trouble> {
1084 let mut parser = Parser { text: text.trim(), at: 0, here };
1085 let mut sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
1086 for term in &mut sum.terms {
1089 if let What::Symbol(name) = &term.what {
1090 let name = self.named(name)?;
1091 self.sym(&name);
1092 term.what = What::Symbol(name);
1093 }
1094 }
1095 Ok(sum)
1096 }
1097
1098 fn bad(&self, why: &str) -> Trouble {
1100 Trouble { line: self.line, why: why.to_owned() }
1101 }
1102
1103 fn finish(mut self) -> Result<Assembled, Trouble> {
1105 if self.frame.is_some() {
1106 return Err(self.bad("a '.cfi_startproc' that is never ended"));
1107 }
1108 self.unwind_table();
1109 self.resolve_sets()?;
1110 self.resolve_sizes()?;
1111 self.resolve_fixups()?;
1112 let keep: Vec<bool> = self
1116 .parts
1117 .iter()
1118 .enumerate()
1119 .map(|(at, part)| {
1120 part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
1121 })
1122 .collect();
1123 let mut moved = vec![0usize; self.parts.len()];
1124 let mut parts = Vec::with_capacity(self.parts.len());
1125 for (at, part) in self.parts.into_iter().enumerate() {
1126 if keep[at] {
1127 moved[at] = parts.len();
1128 parts.push(part);
1129 }
1130 }
1131 let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
1132 for file in self.files {
1135 names.push(Name {
1136 name: file,
1137 at: Held::Absolute(0),
1138 size: 0,
1139 sort: Sort::File,
1140 binding: Binding::Local,
1141 visibility: Visibility::Default,
1142 });
1143 }
1144 for (index, sym) in self.syms.into_iter().enumerate() {
1145 if sym.numbered && !self.relocated.contains(&index) {
1150 continue;
1151 }
1152 let at = match sym.at {
1153 Held::In { part, offset } => Held::In { part: moved[part], offset },
1154 other => other,
1155 };
1156 let binding = match (at, sym.binding) {
1157 (Held::Undefined, Binding::Local) => Binding::Global,
1158 (_, binding) => binding,
1159 };
1160 names.push(Name {
1161 name: sym.name,
1162 at,
1163 size: sym.size,
1164 sort: sym.sort,
1165 binding,
1166 visibility: sym.visibility,
1167 });
1168 }
1169 Ok(Assembled { parts, names })
1170 }
1171
1172 fn unwind_table(&mut self) {
1178 if self.frames.is_empty() || self.no_unwind {
1179 return;
1180 }
1181 let funcs: Vec<Extent> = self
1182 .frames
1183 .iter()
1184 .map(|frame| Extent {
1185 name: self.syms[frame.sym].name.clone(),
1186 start: frame.start as usize,
1187 len: frame.len as usize,
1188 align: 1,
1189 binding: Binding::Local,
1190 visibility: Visibility::Default,
1191 patch: None,
1192 })
1193 .collect();
1194 let rows: Vec<_> = self.frames.iter().map(|frame| frame.rows.clone()).collect();
1195 let Ok(table) = crate::unwind::table(&funcs, &rows, &SYSV, ObjectFormat::Elf) else {
1196 return;
1197 };
1198 for frame in &self.frames {
1199 self.relocated.insert(frame.sym);
1200 }
1201 let size = table.bytes.len() as u64;
1202 self.parts.push(Part {
1203 name: ".eh_frame".to_owned(),
1204 bytes: table.bytes,
1205 size,
1206 align: 8,
1207 shape: Shape { alloc: true, bits: true, ..Shape::default() },
1208 relocs: table.relocs,
1209 });
1210 }
1211
1212 fn resolve_sets(&mut self) -> Result<(), Trouble> {
1215 while !self.sets.is_empty() {
1216 let mut done = Vec::new();
1217 for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
1218 if let Ok(residue) = self.reduce(sum) {
1219 done.push((at, *sym, self.settled(&residue, *line)?));
1220 }
1221 }
1222 if done.is_empty() {
1223 let (sym, _, line) = &self.sets[0];
1224 let why = format!(
1225 "'{}' is set to something that is set to it, so neither has a value",
1226 self.syms[*sym].name
1227 );
1228 return Err(Trouble { line: *line, why });
1229 }
1230 for (_, sym, held) in &done {
1231 self.syms[*sym].at = *held;
1232 }
1233 for (at, _, _) in done.iter().rev() {
1235 self.sets.remove(*at);
1236 }
1237 }
1238 Ok(())
1239 }
1240
1241 fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
1243 match residue.left.as_slice() {
1244 [] => Ok(Held::Absolute(residue.constant as u64)),
1245 [Left { coeff: 1, at: Some((part, offset)), .. }] => {
1248 Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
1249 }
1250 _ => Err(Trouble {
1251 line,
1252 why: "a set to something that is neither a number nor a place in this file"
1253 .to_owned(),
1254 }),
1255 }
1256 }
1257
1258 fn resolve_sizes(&mut self) -> Result<(), Trouble> {
1260 for (sym, sum, line) in std::mem::take(&mut self.sizes) {
1261 let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
1262 if !residue.left.is_empty() {
1263 let why = format!(
1264 "the size of '{}' is not a number, and a size has to be one",
1265 self.syms[sym].name
1266 );
1267 return Err(Trouble { line, why });
1268 }
1269 let size = self.count(residue.constant).map_err(|_| Trouble {
1270 line,
1271 why: format!("'{}' is given a negative size", self.syms[sym].name),
1272 })?;
1273 self.syms[sym].size = size;
1274 }
1275 Ok(())
1276 }
1277
1278 fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1280 for fixup in std::mem::take(&mut self.fixups) {
1281 let line = fixup.line;
1282 let bad = |why: String| Trouble { line, why };
1283 if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1291 let [
1292 Term { coeff: 1, what: What::Symbol(name) },
1293 Term { coeff: -1, what: What::Here { at: end, .. } },
1294 ] = fixup.sum.terms.as_slice()
1295 else {
1296 return Err(bad(
1297 "a reach through the global offset table in something other than an \
1298 instruction, which is not an expression this compiler writes"
1299 .to_owned(),
1300 ));
1301 };
1302 let kind =
1303 if fixup.reach == Reach::Table { Reference::Got } else { Reference::Thread };
1304 self.parts[fixup.part].relocs.push(Reloc {
1305 at: fixup.at as usize,
1306 symbol: name.clone(),
1307 kind,
1308 addend: fixup.sum.constant + fixup.at as i64 - end,
1309 after: (end - fixup.at as i64 - 4).max(0) as u8,
1310 });
1311 continue;
1312 }
1313 let residue = self.reduce(&fixup.sum).map_err(|why| Trouble { line, why })?;
1314 if fixup.reach == Reach::Value && !residue.left.is_empty() {
1315 return Err(bad(
1316 "a number in an instruction that names something outside this section, \
1317 which wants a relocation this compiler does not write yet"
1318 .to_owned(),
1319 ));
1320 }
1321 let (symbol, kind, addend, after) = match residue.left.as_slice() {
1322 [] => {
1323 let width = fixup.width as usize;
1331 let room = 8 * width as u32;
1332 let low = -(1i64 << (room - 1));
1333 let high = if fixup.reach == Reach::Branch {
1334 (1i64 << (room - 1)) - 1
1335 } else {
1336 (1i64 << room) - 1
1337 };
1338 if width < 8 && (residue.constant < low || residue.constant > high) {
1339 return Err(bad(format!(
1340 "{} written into {width} bytes, which does not reach it",
1341 residue.constant
1342 )));
1343 }
1344 let bytes = residue.constant.to_le_bytes();
1345 let at = fixup.at as usize;
1346 let part = &mut self.parts[fixup.part];
1347 part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1348 continue;
1349 }
1350 [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1352 let kind = Reference::Address { bytes: fixup.width };
1353 (name.clone(), kind, residue.constant, 0)
1354 }
1355 [
1360 Left { coeff: 1, what: What::Symbol(name), .. },
1361 Left { coeff: -1, at: Some((part, offset)), .. },
1362 ]
1363 | [
1364 Left { coeff: -1, at: Some((part, offset)), .. },
1365 Left { coeff: 1, what: What::Symbol(name), .. },
1366 ] => {
1367 if *part != fixup.part {
1368 return Err(bad(
1369 "a distance that is subtracted from somewhere in another section"
1370 .to_owned(),
1371 ));
1372 }
1373 if fixup.width != 4 {
1374 return Err(bad(format!(
1375 "a distance written into {} bytes, and four is the only width a \
1376 relocation says one at",
1377 fixup.width
1378 )));
1379 }
1380 let addend = residue.constant + fixup.at as i64 - offset;
1387 let kind = if fixup.reach == Reach::Branch {
1388 Reference::Call
1389 } else {
1390 Reference::Data
1391 };
1392 let after = (offset - fixup.at as i64 - 4).max(0);
1395 (name.clone(), kind, addend, after as u8)
1396 }
1397 [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1398 return Err(bad(
1399 "the address of these bytes themselves, which has no symbol to be \
1400 relocated against"
1401 .to_owned(),
1402 ));
1403 }
1404 _ => {
1405 return Err(bad(
1406 "an expression that does not come out as a number, an address, or a \
1407 distance, and those are what a relocation can say"
1408 .to_owned(),
1409 ));
1410 }
1411 };
1412 if let Some(&sym) = self.known.get(&symbol) {
1416 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1417 self.relocated.insert(sym);
1418 } else if self.syms[sym].numbered {
1419 let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1420 return Err(bad(format!(
1421 "'{number}f' goes on to a '{number}:' and there is none below it"
1422 )));
1423 }
1424 }
1425 if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1426 return Err(bad(format!(
1427 "the address of '{symbol}' written into {} bytes, and this machine relocates \
1428 an address at four or eight",
1429 fixup.width
1430 )));
1431 }
1432 self.parts[fixup.part].relocs.push(Reloc {
1433 at: fixup.at as usize,
1434 symbol,
1435 kind,
1436 addend,
1437 after,
1438 });
1439 }
1440 Ok(())
1441 }
1442
1443 fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
1452 let mut constant = sum.constant;
1453 let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
1454 let mut outside: Vec<(i64, String)> = Vec::new();
1455 for term in &sum.terms {
1456 match &term.what {
1457 What::Here { part, at } => {
1458 placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
1459 }
1460 What::Symbol(name) => {
1461 let Some(&at) = self.known.get(name) else {
1462 return Err(format!("'{name}' is named and never said"));
1463 };
1464 match self.syms[at].at {
1465 Held::Absolute(value) => constant += term.coeff * value as i64,
1466 Held::In { part, offset } => placed.entry(part).or_default().push((
1467 term.coeff,
1468 term.what.clone(),
1469 offset as i64,
1470 )),
1471 Held::Undefined | Held::Common { .. } => {
1474 if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
1475 outside.push((term.coeff, name.clone()));
1476 } else {
1477 return Err(format!("'{name}' is not worked out yet"));
1478 }
1479 }
1480 }
1481 }
1482 }
1483 }
1484 let mut left: Vec<Left> = Vec::new();
1485 for (part, terms) in placed {
1486 let (_, chosen, base) = terms[0].clone();
1487 let mut net = 0;
1488 for (coeff, _, offset) in &terms {
1489 net += coeff;
1490 constant += coeff * (offset - base);
1491 }
1492 if net != 0 {
1493 left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
1494 }
1495 }
1496 let mut together: BTreeMap<String, i64> = BTreeMap::new();
1497 for (coeff, name) in outside {
1498 *together.entry(name).or_default() += coeff;
1499 }
1500 for (name, coeff) in together {
1501 if coeff != 0 {
1502 left.push(Left { coeff, what: What::Symbol(name), at: None });
1503 }
1504 }
1505 Ok(Residue { constant, left })
1506 }
1507}
1508
1509#[derive(Debug, Clone)]
1511struct Residue {
1512 constant: i64,
1513 left: Vec<Left>,
1514}
1515
1516#[derive(Debug, Clone)]
1518struct Left {
1519 coeff: i64,
1521 what: What,
1523 at: Option<(usize, i64)>,
1526}
1527
1528#[derive(Debug, Clone, Default, PartialEq, Eq)]
1530struct Sum {
1531 constant: i64,
1532 terms: Vec<Term>,
1533}
1534
1535#[derive(Debug, Clone, PartialEq, Eq)]
1537struct Term {
1538 coeff: i64,
1539 what: What,
1540}
1541
1542#[derive(Debug, Clone, PartialEq, Eq)]
1544enum What {
1545 Symbol(String),
1547 Here { part: usize, at: i64 },
1550}
1551
1552impl Sum {
1553 fn flat(&self) -> Option<i64> {
1555 self.terms.is_empty().then_some(self.constant)
1556 }
1557
1558 fn of(what: What) -> Sum {
1560 Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1561 }
1562
1563 fn just(value: i64) -> Sum {
1565 Sum { constant: value, terms: Vec::new() }
1566 }
1567
1568 fn plus(mut self, other: Sum) -> Sum {
1570 self.constant = self.constant.wrapping_add(other.constant);
1571 self.terms.extend(other.terms);
1572 self
1573 }
1574
1575 fn minus(self) -> Sum {
1577 Sum {
1578 constant: self.constant.wrapping_neg(),
1579 terms: self
1580 .terms
1581 .into_iter()
1582 .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
1583 .collect(),
1584 }
1585 }
1586
1587 fn times(self, factor: i64) -> Sum {
1589 Sum {
1590 constant: self.constant.wrapping_mul(factor),
1591 terms: self
1592 .terms
1593 .into_iter()
1594 .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
1595 .collect(),
1596 }
1597 }
1598}
1599
1600struct Parser<'a> {
1602 text: &'a str,
1603 at: usize,
1604 here: (usize, i64),
1605}
1606
1607impl Parser<'_> {
1608 fn whole(&mut self) -> Result<Sum, String> {
1610 let sum = self.bitwise()?;
1611 self.space();
1612 if self.at < self.text.len() {
1613 return Err(format!(
1614 "'{}' is left over at the end of an expression",
1615 &self.text[self.at..]
1616 ));
1617 }
1618 Ok(sum)
1619 }
1620
1621 fn bitwise(&mut self) -> Result<Sum, String> {
1623 let mut left = self.shift()?;
1624 loop {
1625 self.space();
1626 let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
1627 let right = self.shift()?;
1628 left = self.arithmetic(left, right, op)?;
1629 }
1630 }
1631
1632 fn shift(&mut self) -> Result<Sum, String> {
1634 let mut left = self.sum()?;
1635 loop {
1636 self.space();
1637 let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
1638 let right = self.sum()?;
1639 left = self.arithmetic(left, right, op)?;
1640 }
1641 }
1642
1643 fn sum(&mut self) -> Result<Sum, String> {
1645 let mut left = self.product()?;
1646 loop {
1647 self.space();
1648 let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
1650 let right = self.product()?;
1651 left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
1652 }
1653 }
1654
1655 fn product(&mut self) -> Result<Sum, String> {
1657 let mut left = self.unary()?;
1658 loop {
1659 self.space();
1660 let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
1661 let right = self.unary()?;
1662 left = match (op, left.flat(), right.flat()) {
1666 ("*", _, Some(factor)) => left.times(factor),
1667 ("*", Some(factor), _) => right.times(factor),
1668 (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
1669 _ => return Err(format!("'{op}' of something that names a symbol")),
1670 };
1671 }
1672 }
1673
1674 fn unary(&mut self) -> Result<Sum, String> {
1676 self.space();
1677 if self.eat("-") {
1678 return Ok(self.unary()?.minus());
1679 }
1680 if self.eat("+") {
1681 return self.unary();
1682 }
1683 if self.eat("~") {
1684 let inner = self.unary()?;
1685 let value = inner
1686 .flat()
1687 .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
1688 return Ok(Sum::just(!value));
1689 }
1690 if self.eat("!") {
1691 let inner = self.unary()?;
1692 let value = inner
1693 .flat()
1694 .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
1695 return Ok(Sum::just(i64::from(value == 0)));
1696 }
1697 self.primary()
1698 }
1699
1700 fn primary(&mut self) -> Result<Sum, String> {
1702 self.space();
1703 let rest = &self.text[self.at..];
1704 if rest.is_empty() {
1705 return Err("an expression that stops before it says anything".to_owned());
1706 }
1707 if self.eat("(") {
1708 let inner = self.bitwise()?;
1709 self.space();
1710 if !self.eat(")") {
1711 return Err("a bracket that was opened and never closed".to_owned());
1712 }
1713 return Ok(inner);
1714 }
1715 let first = rest.as_bytes()[0];
1716 if first == b'\'' {
1717 return self.character();
1718 }
1719 if first.is_ascii_digit() {
1720 let end = rest.find(|ch: char| !ch.is_ascii_digit()).unwrap_or(rest.len());
1724 let bytes = rest.as_bytes();
1725 if matches!(bytes.get(end), Some(b'b' | b'f'))
1726 && !bytes.get(end + 1).is_some_and(|byte| carries_on(*byte))
1727 {
1728 self.at += end + 1;
1729 return Ok(Sum::of(What::Symbol(rest[..=end].to_owned())));
1730 }
1731 return self.digits();
1732 }
1733 if starts(first) {
1734 let name = self.word();
1735 if name == "." {
1737 let (part, at) = self.here;
1738 return Ok(Sum::of(What::Here { part, at }));
1739 }
1740 if self.text[self.at..].starts_with('@') {
1744 return Err(format!(
1745 "'{name}@' asks for a relocation only an instruction can carry"
1746 ));
1747 }
1748 return Ok(Sum::of(What::Symbol(name)));
1749 }
1750 Err(format!("'{rest}' is not the start of an expression"))
1751 }
1752
1753 fn digits(&mut self) -> Result<Sum, String> {
1755 let rest = &self.text[self.at..];
1756 let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
1757 (16, 2)
1758 } else if rest.starts_with("0b") || rest.starts_with("0B") {
1759 (2, 2)
1760 } else if rest.len() > 1 && rest.starts_with('0') {
1761 (8, 1)
1762 } else {
1763 (10, 0)
1764 };
1765 let body = &rest[skip..];
1766 let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
1767 if end == 0 {
1768 return Err(format!("'{rest}' starts like a number and is not one"));
1769 }
1770 let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
1771 let value = u64::from_str_radix(&text, radix)
1774 .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
1775 self.at += skip + end;
1776 while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
1779 self.at += 1;
1780 }
1781 Ok(Sum::just(value as i64))
1782 }
1783
1784 fn character(&mut self) -> Result<Sum, String> {
1786 self.at += 1;
1787 let rest = &self.text[self.at..];
1788 let mut chars = rest.chars();
1789 let Some(first) = chars.next() else {
1790 return Err("a quote with no character after it".to_owned());
1791 };
1792 let (value, used) = if first == '\\' {
1793 let (value, used) = escape(&rest[1..])?;
1794 (value, used + 1)
1795 } else {
1796 (first as u8, first.len_utf8())
1797 };
1798 self.at += used;
1799 if self.text[self.at..].starts_with('\'') {
1802 self.at += 1;
1803 }
1804 Ok(Sum::just(i64::from(value)))
1805 }
1806
1807 fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
1809 let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
1810 return Err(format!("'{op}' of something that names a symbol"));
1811 };
1812 Ok(Sum::just(self.arithmetic_number(a, b, op)?))
1813 }
1814
1815 fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
1817 Ok(match op {
1818 "|" => a | b,
1819 "^" => a ^ b,
1820 "&" => a & b,
1821 "<<" => a.wrapping_shl(shift(b)?),
1822 ">>" => a.wrapping_shr(shift(b)?),
1823 "*" => a.wrapping_mul(b),
1824 "/" if b == 0 => return Err("a division by zero".to_owned()),
1825 "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
1826 "/" => a.wrapping_div(b),
1827 "%" => a.wrapping_rem(b),
1828 _ => return Err(format!("'{op}' is not an operator this compiler knows")),
1829 })
1830 }
1831
1832 fn word(&mut self) -> String {
1834 let body = &self.text[self.at..];
1835 let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
1836 let word = body[..end].to_owned();
1837 self.at += end;
1838 word
1839 }
1840
1841 fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
1846 for op in ops {
1847 if self.text[self.at..].starts_with(op) {
1848 self.at += op.len();
1849 return Some(op);
1850 }
1851 }
1852 None
1853 }
1854
1855 fn eat(&mut self, what: &str) -> bool {
1857 if self.text[self.at..].starts_with(what) {
1858 self.at += what.len();
1859 return true;
1860 }
1861 false
1862 }
1863
1864 fn space(&mut self) {
1866 while self.text[self.at..].starts_with([' ', '\t']) {
1867 self.at += 1;
1868 }
1869 }
1870}
1871
1872impl Reader {
1873 fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
1875 let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
1876 let body = text
1877 .strip_prefix('"')
1878 .and_then(|rest| rest.strip_suffix('"'))
1879 .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
1880 let mut out = Vec::with_capacity(body.len());
1881 let mut at = 0;
1882 while at < body.len() {
1883 let rest = &body[at..];
1884 let first = rest.as_bytes()[0];
1885 if first == b'\\' {
1886 let (value, used) =
1887 escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
1888 out.push(value);
1889 at += used + 1;
1890 continue;
1891 }
1892 let ch = rest.chars().next().unwrap_or('\0');
1893 let mut buffer = [0u8; 4];
1894 out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
1895 at += ch.len_utf8();
1896 }
1897 Ok(out)
1898 }
1899}
1900
1901fn shift(by: i64) -> Result<u32, String> {
1903 u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
1904}
1905
1906fn escape(rest: &str) -> Result<(u8, usize), String> {
1910 let bytes = rest.as_bytes();
1911 let Some(&first) = bytes.first() else {
1912 return Err("a backslash with nothing after it".to_owned());
1913 };
1914 let simple = match first {
1915 b'n' => Some(b'\n'),
1916 b't' => Some(b'\t'),
1917 b'r' => Some(b'\r'),
1918 b'f' => Some(0x0c),
1919 b'b' => Some(0x08),
1920 b'v' => Some(0x0b),
1921 b'a' => Some(0x07),
1922 b'e' => Some(0x1b),
1923 b'\\' => Some(b'\\'),
1924 b'"' => Some(b'"'),
1925 b'\'' => Some(b'\''),
1926 _ => None,
1927 };
1928 if let Some(value) = simple {
1929 return Ok((value, 1));
1930 }
1931 if first == b'x' || first == b'X' {
1932 let end = bytes[1..]
1933 .iter()
1934 .position(|byte| !byte.is_ascii_hexdigit())
1935 .map_or(bytes.len(), |at| at + 1);
1936 if end == 1 {
1937 return Err("a hex escape with no digits in it".to_owned());
1938 }
1939 let text = &rest[1..end];
1942 let text = &text[text.len().saturating_sub(2)..];
1943 let value =
1944 u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
1945 return Ok((value, end));
1946 }
1947 if (b'0'..=b'7').contains(&first) {
1948 let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
1949 let value = u32::from_str_radix(&rest[..end], 8)
1950 .map_err(|_| "an octal escape that is not one".to_owned())?;
1951 return Ok(((value & 0xff) as u8, end));
1952 }
1953 Err(format!("'\\{}' is not an escape this compiler knows", first as char))
1957}
1958
1959fn labelled(text: &str) -> Option<String> {
1966 let bytes = text.as_bytes();
1967 if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
1968 return None;
1969 }
1970 let end = text.find(|ch: char| !carries_on(ch as u8))?;
1971 if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
1973 return None;
1974 }
1975 Some(text[..end].to_owned())
1976}
1977
1978fn assigned(text: &str) -> Option<(&str, &str)> {
1982 let bytes = text.as_bytes();
1983 if bytes.is_empty() || !starts(bytes[0]) {
1984 return None;
1985 }
1986 let end = text.find(|ch: char| !carries_on(ch as u8)).unwrap_or(text.len());
1987 let rest = text[end..].trim_start().strip_prefix('=')?;
1988 if rest.starts_with('=') {
1989 return None;
1990 }
1991 Some((&text[..end], rest.trim()))
1992}
1993
1994fn starts(byte: u8) -> bool {
1996 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
1997}
1998
1999fn carries_on(byte: u8) -> bool {
2001 starts(byte) || byte.is_ascii_digit()
2002}
2003
2004fn counted(number: &str, nth: usize) -> String {
2011 format!("{number}\u{1}{nth}")
2012}
2013
2014fn unquoted(text: &str) -> String {
2016 text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
2017}
2018
2019pub(crate) fn split(text: &str, on: char) -> Vec<String> {
2024 let mut out = Vec::new();
2025 let mut piece = String::new();
2026 let mut depth = 0i32;
2027 let mut quote = None;
2028 let mut chars = text.chars();
2029 while let Some(ch) = chars.next() {
2030 if let Some(mark) = quote {
2031 piece.push(ch);
2032 if ch == '\\' {
2033 if let Some(next) = chars.next() {
2034 piece.push(next);
2035 }
2036 continue;
2037 }
2038 if ch == mark {
2039 quote = None;
2040 }
2041 continue;
2042 }
2043 match ch {
2044 '"' => {
2045 quote = Some(ch);
2046 piece.push(ch);
2047 }
2048 '(' => {
2049 depth += 1;
2050 piece.push(ch);
2051 }
2052 ')' => {
2053 depth -= 1;
2054 piece.push(ch);
2055 }
2056 _ if ch == on && depth == 0 => {
2057 out.push(std::mem::take(&mut piece));
2058 }
2059 _ => piece.push(ch),
2060 }
2061 }
2062 if !piece.trim().is_empty() || !out.is_empty() {
2063 out.push(piece);
2064 }
2065 out.into_iter().map(|piece| piece.trim().to_owned()).collect()
2066}
2067
2068fn repeated<'a>(word: &str, rest: &'a str) -> Option<(String, &'a str)> {
2076 let unequal = match word {
2077 "rep" | "repe" | "repz" => false,
2078 "repne" | "repnz" => true,
2079 _ => return None,
2080 };
2081 let (next, after) = match rest.find(char::is_whitespace) {
2082 Some(cut) => (&rest[..cut], rest[cut..].trim()),
2083 None => (rest, ""),
2084 };
2085 let string = next.len() == 5 && next.ends_with(['b', 'w', 'l', 'q']);
2086 let which = if string { &next[..4] } else { "" };
2087 let prefix = match (unequal, which) {
2088 (false, "movs" | "stos") => "rep",
2089 (false, "scas" | "cmps") => "repe",
2090 (true, "scas" | "cmps") => "repne",
2091 _ => return None,
2092 };
2093 Some((format!("{prefix} {next}"), after))
2094}
2095
2096#[cfg(test)]
2097mod tests {
2098 use super::*;
2099
2100 use rucc_object::Reference;
2101
2102 fn assembled(text: &str) -> Assembled {
2104 match read(text) {
2105 Ok(assembled) => assembled,
2106 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2107 }
2108 }
2109
2110 fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
2112 let part = assembled
2113 .parts
2114 .iter()
2115 .find(|part| part.name == name)
2116 .unwrap_or_else(|| panic!("there is no section called '{name}'"));
2117 part.bytes.clone()
2118 }
2119
2120 fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
2122 assembled
2123 .names
2124 .iter()
2125 .find(|name| name.name == want)
2126 .unwrap_or_else(|| panic!("there is no name called '{want}'"))
2127 }
2128
2129 fn refused(text: &str) -> Trouble {
2131 read(text).err().unwrap_or_else(|| panic!("this was read and should not have been"))
2132 }
2133
2134 #[test]
2135 fn a_repeat_prefix_is_read_with_the_string_instruction_behind_it() {
2136 let assembled =
2137 assembled("\t.text\n\trep movsl\n\trepnz scasb\n\trepz cmpsb\n\trep stosq\n");
2138 assert_eq!(
2139 bytes(&assembled, ".text"),
2140 [0xF3, 0xA5, 0xF2, 0xAE, 0xF3, 0xA6, 0xF3, 0x48, 0xAB]
2141 );
2142 }
2143
2144 #[test]
2150 fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
2151 let out =
2152 assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
2153 let text = bytes(&out, ".text");
2154 assert_eq!(
2157 text,
2158 vec![0x90, 0xe9, 0xfa, 0xff, 0xff, 0xff, 0x90, 0xe9, 0x01, 0, 0, 0, 0x90, 0xc3]
2159 );
2160 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2161 let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2163 assert_eq!(written, vec!["foo"]);
2164 }
2165
2166 #[test]
2167 fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
2168 let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
2169 assert!(back.why.contains("none above it"), "{}", back.why);
2170 let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
2171 assert!(forward.why.contains("none below it"), "{}", forward.why);
2172 }
2173
2174 #[test]
2181 fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
2182 let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
2183 assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
2184 let split = assembled("\t.text\n\trep\n\tmovsq\n");
2185 assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
2186 let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
2187 assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
2188 }
2189
2190 #[test]
2197 fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
2198 let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
2199 let relocs = &out.parts[0].relocs;
2200 assert_eq!(relocs.len(), 1);
2201 assert_eq!(relocs[0].symbol, "table");
2202 assert_eq!(relocs[0].kind, Reference::Got);
2203 assert_eq!(relocs[0].addend, -4);
2206 let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
2207 assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
2208 }
2209
2210 #[test]
2216 fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
2217 let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
2218 let relocs = &out.parts[0].relocs;
2219 assert_eq!(relocs.len(), 1);
2220 assert_eq!(relocs[0].symbol, "table");
2221 assert_eq!(relocs[0].addend, -516);
2222 let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2225 assert_eq!(named, ["table"]);
2226 }
2227
2228 #[test]
2229 fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
2230 refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
2233 }
2234
2235 #[test]
2236 fn the_probe_gmp_writes() {
2237 let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
2241 assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
2242 let foo = name(&out, "foo");
2243 assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
2244 assert_eq!(foo.binding, Binding::Global);
2245 }
2246
2247 #[test]
2248 fn every_width_of_number_is_the_bytes_it_says_it_is() {
2249 let out = assembled(
2250 "\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",
2251 );
2252 let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
2253 want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
2254 assert_eq!(bytes(&out, ".data"), want);
2255 }
2256
2257 #[test]
2258 fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
2259 let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
2262 assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
2263 }
2264
2265 #[test]
2266 fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
2267 let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
2268 assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
2269 }
2270
2271 #[test]
2272 fn space_and_fill_put_that_many_bytes_there() {
2273 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");
2274 assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
2275 }
2276
2277 #[test]
2278 fn aligning_moves_on_to_the_boundary_and_no_further() {
2279 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");
2282 let data = bytes(&out, ".data");
2283 assert_eq!(data.len(), 17);
2284 assert_eq!(data[0], 1);
2285 assert_eq!(data[8], 2);
2286 assert_eq!(data[16], 3);
2287 assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
2288 }
2289
2290 #[test]
2291 fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
2292 let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
2293 let part = &out.parts[0];
2294 assert_eq!(part.name, ".bss");
2295 assert_eq!(part.size, 4096);
2296 assert!(part.bytes.is_empty(), "the zeroes were carried after all");
2297 assert!(!part.shape.bits);
2298 }
2299
2300 #[test]
2301 fn what_a_section_directive_said_about_a_section_is_what_it_is() {
2302 let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
2303 let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
2304 assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
2305 assert!(!part.shape.write, "nothing said it was writable");
2306 }
2307
2308 #[test]
2309 fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
2310 let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
2311 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2312 assert_eq!(bytes(&out, ".text"), vec![0x90]);
2313 }
2314
2315 #[test]
2316 fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
2317 let out = assembled(
2318 "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
2319 );
2320 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2321 assert_eq!(bytes(&out, ".rodata"), vec![9]);
2322 }
2323
2324 #[test]
2325 fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
2326 let out = assembled(
2329 "\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",
2330 );
2331 let f = name(&out, "f");
2332 assert_eq!(f.size, 5);
2333 assert_eq!(f.sort, Sort::Func);
2334 }
2335
2336 #[test]
2337 fn a_set_may_name_something_further_down_the_file() {
2338 let out = assembled(
2341 "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
2342 table_end - table\n",
2343 );
2344 assert_eq!(name(&out, "width").at, Held::Absolute(12));
2345 }
2346
2347 #[test]
2348 fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
2349 let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
2350 assert_eq!(name(&out, "a").at, Held::Absolute(11));
2351 assert_eq!(name(&out, "b").at, Held::Absolute(10));
2352 }
2353
2354 #[test]
2355 fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
2356 let why = refused("\t.set a, b\n\t.set b, a\n");
2357 assert!(why.why.contains("neither has a value"), "{why}");
2358 }
2359
2360 #[test]
2361 fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
2362 let out = assembled("\t.data\n\t.quad message\n");
2363 let reloc = &out.parts[0].relocs[0];
2364 assert_eq!(reloc.at, 0);
2365 assert_eq!(reloc.symbol, "message");
2366 assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
2367 assert_eq!(reloc.addend, 0);
2368 assert_eq!(name(&out, "message").at, Held::Undefined);
2369 }
2370
2371 #[test]
2372 fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
2373 let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
2376 let reloc = &out.parts[0].relocs[0];
2377 assert_eq!(reloc.at, 8);
2378 assert_eq!(reloc.symbol, "message");
2379 assert_eq!(reloc.kind, Reference::Data);
2380 assert_eq!(reloc.addend, 0);
2381 }
2382
2383 #[test]
2384 fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
2385 let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
2390 let reloc = &out.parts[0].relocs[0];
2391 assert_eq!(reloc.at, 8);
2392 assert_eq!(reloc.kind, Reference::Data);
2393 assert_eq!(reloc.addend, 8);
2394 }
2395
2396 #[test]
2397 fn a_number_added_to_a_name_rides_along_in_the_addend() {
2398 let out = assembled("\t.data\n\t.quad message + 16\n");
2399 assert_eq!(out.parts[0].relocs[0].addend, 16);
2400 }
2401
2402 #[test]
2403 fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
2404 let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
2405 assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
2406 assert_eq!(name(&out, "shared").binding, Binding::Global);
2407 assert_eq!(name(&out, "mine").binding, Binding::Local);
2410 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2411 }
2412
2413 #[test]
2414 fn what_a_file_says_about_who_can_see_a_name_is_kept() {
2415 let out = assembled(
2416 "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
2417 inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
2418 );
2419 assert_eq!(name(&out, "seen").binding, Binding::Global);
2420 assert_eq!(name(&out, "maybe").binding, Binding::Weak);
2421 assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
2422 }
2423
2424 #[test]
2425 fn the_name_of_the_file_is_a_symbol_of_its_own() {
2426 let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
2429 assert_eq!(out.names[0].name, "big.s");
2430 assert_eq!(out.names[0].sort, Sort::File);
2431 assert_eq!(out.names[0].binding, Binding::Local);
2432 assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
2433 }
2434
2435 #[test]
2436 fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
2437 let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
2440 assert!(out.names.is_empty(), "{:?}", out.names);
2441 }
2442
2443 #[test]
2444 fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
2445 let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\tpopcnt %rax, %rdx\n\tret\n");
2449 assert_eq!(why.line, 4);
2450 assert!(why.why.contains("popcnt"), "{why}");
2451 }
2452
2453 #[test]
2454 fn a_function_of_instructions_is_its_bytes_and_its_size() {
2455 let out = assembled(
2458 "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
2459 id, .-id\n",
2460 );
2461 assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
2462 assert_eq!(name(&out, "id").size, 4);
2463 assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
2464 }
2465
2466 #[test]
2467 fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
2468 let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
2472 assert_eq!(bytes(&out, ".text"), vec![0xe9, 0, 0, 0, 0, 0xc3]);
2473 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2474 }
2475
2476 #[test]
2477 fn a_jump_backwards_is_the_negative_distance_to_it() {
2478 let out = assembled("\t.text\nagain:\n\tjmp again\n");
2479 assert_eq!(bytes(&out, ".text"), vec![0xe9, 0xfb, 0xff, 0xff, 0xff]);
2480 }
2481
2482 #[test]
2483 fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
2484 let out = assembled("\t.text\n\tcall puts\n");
2490 let reloc = &out.parts[0].relocs[0];
2491 assert_eq!(reloc.at, 1);
2492 assert_eq!(reloc.symbol, "puts");
2493 assert_eq!(reloc.kind, Reference::Call);
2494 assert_eq!(reloc.addend, -4);
2495 }
2496
2497 #[test]
2498 fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
2499 let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
2500 let reloc = &out.parts[0].relocs[0];
2501 assert_eq!(reloc.symbol, "message");
2502 assert_eq!(reloc.kind, Reference::Data);
2503 assert_eq!(reloc.at, 3);
2505 assert_eq!(reloc.addend, -4);
2506 }
2507
2508 #[test]
2509 fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
2510 let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
2513 assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
2514 }
2515
2516 #[test]
2517 fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
2518 let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
2522 assert_eq!(why.line, 2);
2523 assert!(why.why.contains("does not reach"), "{why}");
2524 }
2525
2526 #[test]
2527 fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
2528 let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
2533 assert_eq!(bytes(&out, ".data")[200], 200);
2534 let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
2535 assert!(why.why.contains("does not reach"), "{why}");
2536 }
2537
2538 #[test]
2539 fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
2540 let why = refused("\t.bss\n\tret\n");
2541 assert!(why.why.contains("holds no bytes"), "{why}");
2542 }
2543
2544 #[test]
2545 fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
2546 let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
2547 assert_eq!(why.line, 3);
2548 assert!(why.why.contains(".reloc"), "{why}");
2549 }
2550
2551 #[test]
2552 fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
2553 let out = assembled(
2556 "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
2557 3\n",
2558 );
2559 assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
2560 }
2561
2562 #[test]
2563 fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
2564 let why = refused("\t.data\n\t/* and then nothing\n");
2565 assert!(why.why.contains("never closed"), "{why}");
2566 }
2567
2568 #[test]
2569 fn a_string_with_a_comment_character_in_it_is_a_string() {
2570 let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
2571 assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
2572 }
2573
2574 #[test]
2575 fn several_statements_on_one_line_are_several_statements() {
2576 let out = assembled("\t.data; .byte 1; .byte 2\n");
2577 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2578 }
2579
2580 #[test]
2581 fn a_section_nothing_was_ever_put_in_is_dropped() {
2582 let out = assembled("\t.data\n\t.byte 1\n");
2585 assert_eq!(out.parts.len(), 1);
2586 assert_eq!(out.parts[0].name, ".data");
2587 }
2588
2589 #[test]
2590 fn a_section_with_nothing_in_it_but_a_name_is_kept() {
2591 let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
2594 assert_eq!(out.parts.len(), 1);
2595 assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
2596 }
2597
2598 #[test]
2599 fn an_error_directive_is_the_file_saying_it_refuses_itself() {
2600 let why = refused("\t.error \"this is not the machine for it\"\n");
2601 assert!(why.why.contains("not the machine for it"), "{why}");
2602 }
2603
2604 #[test]
2605 fn a_jump_counted_from_itself_is_the_short_one_gas_writes() {
2606 let out = assembled("\tjmp .+6\n\t.int 123\n\tmov .-4(%rip), %eax\n");
2609 assert_eq!(
2610 bytes(&out, ".text"),
2611 vec![0xeb, 0x04, 123, 0, 0, 0, 0x8b, 0x05, 0xf6, 0xff, 0xff, 0xff]
2612 );
2613 }
2614
2615 #[test]
2616 fn a_numbered_label_in_an_expression_is_a_place() {
2617 let out =
2618 assembled("2:\n\tjmp .+6\n1:\n\t.pushsection .data\n\t.long 1b - 2b\n\t.popsection\n");
2619 assert_eq!(bytes(&out, ".data"), vec![2, 0, 0, 0]);
2620 let out = assembled("\t.data\n\t.byte 0b101\n");
2622 assert_eq!(bytes(&out, ".data"), vec![5]);
2623 }
2624
2625 #[test]
2626 fn a_number_an_instruction_carries_may_be_an_expression_over_labels() {
2627 let out = assembled("3:\tmov $4f-3b, %eax\n4:\n");
2628 assert_eq!(bytes(&out, ".text"), vec![0xb8, 5, 0, 0, 0]);
2629 }
2630
2631 #[test]
2632 fn a_number_an_instruction_carries_may_not_name_something_elsewhere() {
2633 let why = refused("\tmov $elsewhere, %eax\n");
2634 assert!(why.why.contains("relocation"), "{why}");
2635 }
2636
2637 #[test]
2638 fn a_name_set_twice_means_what_it_was_where_it_is_used() {
2639 let out = assembled(
2640 "\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",
2641 );
2642 assert_eq!(bytes(&out, ".data"), vec![3, 1, 2]);
2643 }
2644
2645 #[test]
2646 fn a_place_set_twice_and_reached_from_another_section_is_relocated_against() {
2647 let out = assembled(
2648 "\t.data\n\tx = .\n\t.int 1\n\tx = .\n\t.int 2\n\t.text\n\tmov x(%rip), %eax\n",
2649 );
2650 let reloc = &out.parts.iter().find(|part| part.name == ".text").unwrap().relocs[0];
2651 let target = name(&out, &reloc.symbol);
2652 let data = out.parts.iter().position(|part| part.name == ".data").unwrap();
2653 assert_eq!(target.at, Held::In { part: data, offset: 4 });
2654 }
2655
2656 #[test]
2657 fn frame_rules_are_an_unwind_table_pointing_at_the_function() {
2658 let out = assembled(
2659 "f:\n\t.cfi_startproc\n\tpush %rbp\n\t.cfi_def_cfa_offset 16\n\t.cfi_offset %rbp, \
2660 -16\n\tpop %rbp\n\t.cfi_def_cfa_offset 8\n\tret\n\t.cfi_endproc\n",
2661 );
2662 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
2663 let rows = [0x41, 0x0e, 0x10, 0x86, 0x02, 0x41, 0x0e, 0x08];
2666 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
2667 let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
2668 let text = out.parts.iter().position(|part| part.name == ".text").unwrap();
2669 assert_eq!(name(&out, &reloc.symbol).at, Held::In { part: text, offset: 0 });
2670 }
2671
2672 #[test]
2673 fn a_frame_rule_relative_to_the_register_is_the_same_slot() {
2674 let out = assembled(
2675 "\t.cfi_startproc\n\tpush %rbx\n\t.cfi_adjust_cfa_offset 8\n\t.cfi_rel_offset \
2676 %rbx, 0\n\t.cfi_endproc\n",
2677 );
2678 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
2679 let rows = [0x41, 0x0e, 0x10, 0x83, 0x02];
2680 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
2681 }
2682
2683 #[test]
2684 fn frame_rules_for_a_debugger_only_are_no_unwind_table() {
2685 let out =
2686 assembled("\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\tret\n\t.cfi_endproc\n");
2687 assert!(out.parts.iter().all(|part| part.name != ".eh_frame"));
2688 }
2689
2690 #[test]
2691 fn a_frame_rule_outside_a_function_or_a_function_never_ended_is_refused() {
2692 let why = refused("\t.cfi_def_cfa_offset 16\n");
2693 assert!(why.why.contains("outside"), "{why}");
2694 let why = refused("\t.cfi_startproc\n\tret\n");
2695 assert!(why.why.contains("never ended"), "{why}");
2696 }
2697}