1use std::collections::{BTreeMap, HashMap};
32
33use rucc_object::{
34 Array, Assembled, Binding, Held, Name, Part, Reference, Reloc, Shape, Sort, Visibility,
35};
36
37use crate::instruction::Sort as Reach;
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Trouble {
44 pub line: usize,
47 pub why: String,
49}
50
51impl std::fmt::Display for Trouble {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 write!(f, "{}: {}", self.line, self.why)
54 }
55}
56
57impl std::error::Error for Trouble {}
58
59pub fn read(text: &str) -> Result<Assembled, Trouble> {
67 let mut reader = Reader::default();
68 reader.run(text)?;
69 reader.finish()
70}
71
72#[derive(Debug, Clone)]
78struct Sym {
79 name: String,
80 at: Held,
81 size: u64,
82 sort: Sort,
83 binding: Binding,
84 visibility: Visibility,
85 numbered: bool,
88}
89
90#[derive(Debug, Clone)]
92struct Fixup {
93 part: usize,
94 at: u64,
95 width: u8,
96 sum: Sum,
97 reach: Reach,
101 line: usize,
102}
103
104#[derive(Debug, Default)]
106struct Reader {
107 parts: Vec<Part>,
108 named: HashMap<String, usize>,
110 here: usize,
112 stack: Vec<usize>,
114 before: Option<usize>,
116 syms: Vec<Sym>,
117 known: HashMap<String, usize>,
118 counts: HashMap<String, usize>,
121 labelled: std::collections::HashSet<usize>,
124 fixups: Vec<Fixup>,
125 sets: Vec<(usize, Sum, usize)>,
127 sizes: Vec<(usize, Sum, usize)>,
129 files: Vec<String>,
133 line: usize,
134}
135
136impl Reader {
137 fn run(&mut self, text: &str) -> Result<(), Trouble> {
139 self.section(".text", Shape::of(".text"));
142 let mut commenting = false;
143 for (index, raw) in text.lines().enumerate() {
144 self.line = index + 1;
145 let line = self.strip(raw, &mut commenting)?;
146 for statement in split(&line, ';') {
147 self.statement(statement.trim())?;
148 }
149 }
150 if commenting {
151 return Err(self.bad("a block comment was opened and never closed"));
152 }
153 Ok(())
154 }
155
156 fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
163 let mut out = String::with_capacity(raw.len());
164 let bytes = raw.as_bytes();
165 let mut i = 0;
166 let mut quote = None;
167 while i < bytes.len() {
168 let rest = &raw[i..];
169 if *commenting {
170 if let Some(end) = rest.find("*/") {
171 *commenting = false;
172 out.push(' ');
175 i += end + 2;
176 } else {
177 return Ok(out);
178 }
179 continue;
180 }
181 let ch = bytes[i] as char;
182 if let Some(mark) = quote {
183 out.push(ch);
184 if ch == '\\' && i + 1 < bytes.len() {
185 out.push(bytes[i + 1] as char);
186 i += 2;
187 continue;
188 }
189 if ch == mark {
190 quote = None;
191 }
192 i += 1;
193 continue;
194 }
195 if ch == '"' {
196 quote = Some('"');
197 out.push(ch);
198 i += 1;
199 continue;
200 }
201 if rest.starts_with("/*") {
202 *commenting = true;
203 i += 2;
204 continue;
205 }
206 if rest.starts_with("//") || ch == '#' {
207 return Ok(out);
208 }
209 out.push(ch);
210 i += 1;
211 }
212 if quote.is_some() {
213 return Err(self.bad("a string was opened and the line ended before it closed"));
214 }
215 Ok(out)
216 }
217
218 fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
220 loop {
221 text = text.trim_start();
222 let Some(name) = labelled(text) else { break };
223 self.label(&name)?;
224 text = &text[name.len() + 1..];
225 }
226 let text = text.trim();
227 if text.is_empty() {
228 return Ok(());
229 }
230 let (word, rest) = match text.find(char::is_whitespace) {
231 Some(cut) => (&text[..cut], text[cut..].trim()),
232 None => (text, ""),
233 };
234 if let Some(directive) = word.strip_prefix('.') {
235 return self.directive(directive, rest);
236 }
237 self.instruction(word, rest)
238 }
239
240 fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
254 let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
255 let written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
256 let part = self.here;
257 let at = self.at();
258 self.put(&written.bytes)?;
259 let end = at + written.bytes.len() as u64;
260 for hole in written.holes {
261 let name = self.numbered(&hole.name)?.unwrap_or(hole.name);
262 self.sym(&name);
265 let sum = Sum {
266 constant: hole.addend,
267 terms: vec![
268 Term { coeff: 1, what: What::Symbol(name) },
269 Term { coeff: -1, what: What::Here { part, at: end as i64 } },
270 ],
271 };
272 self.fixups.push(Fixup {
273 part,
274 at: at + hole.at as u64,
275 width: hole.width,
276 sum,
277 reach: hole.sort,
278 line: self.line,
279 });
280 }
281 Ok(())
282 }
283
284 fn label(&mut self, name: &str) -> Result<(), Trouble> {
286 let at = self.at();
287 let part = self.here;
288 let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
291 let held = if numbered {
292 let count = self.counts.entry(name.to_owned()).or_insert(0);
293 *count += 1;
294 counted(name, *count)
295 } else {
296 name.to_owned()
297 };
298 let sym = self.sym(&held);
299 if self.syms[sym].at != Held::Undefined {
300 let what = format!("'{name}' is defined twice");
301 return Err(self.bad(&what));
302 }
303 self.syms[sym].at = Held::In { part, offset: at };
304 self.labelled.insert(part);
305 Ok(())
306 }
307
308 fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
315 let Some(number) = word.strip_suffix(['b', 'f']) else {
316 return Ok(None);
317 };
318 if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
319 return Ok(None);
320 }
321 let count = self.counts.get(number).copied().unwrap_or(0);
322 if word.ends_with('b') {
323 if count == 0 {
324 let what =
325 format!("'{word}' goes back to a '{number}:' and there is none above it");
326 return Err(self.bad(&what));
327 }
328 return Ok(Some(counted(number, count)));
329 }
330 Ok(Some(counted(number, count + 1)))
331 }
332
333 #[allow(clippy::too_many_lines)]
335 fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
336 let args = split(rest, ',');
337 match word {
338 "text" | "data" | "bss" | "rodata" => {
339 self.plain(word, rest)?;
340 }
341 "section" => self.section_directive(&args)?,
342 "pushsection" => {
343 self.stack.push(self.here);
344 self.section_directive(&args)?;
345 }
346 "popsection" => {
347 let Some(back) = self.stack.pop() else {
348 return Err(self.bad(".popsection with nothing pushed"));
349 };
350 self.go(back);
351 }
352 "previous" => {
353 let Some(back) = self.before else {
354 return Err(self.bad(".previous with no section before this one"));
355 };
356 self.go(back);
357 }
358
359 "byte" => self.data(&args, 1)?,
360 "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
361 "long" | "int" | "4byte" => self.data(&args, 4)?,
362 "quad" | "8byte" => self.data(&args, 8)?,
363
364 "ascii" => self.text_bytes(&args, false)?,
365 "asciz" | "string" => self.text_bytes(&args, true)?,
366
367 "space" | "skip" | "zero" => {
368 if args.is_empty() || args.len() > 2 {
369 return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
370 }
371 let size = self.number(&args[0])?;
372 let size = self.count(size)?;
373 let fill = match args.get(1) {
374 Some(arg) => self.byte(arg)?,
375 None => 0,
376 };
377 self.pad(size, fill)?;
378 }
379 "fill" => {
380 if args.is_empty() || args.len() > 3 {
384 return Err(self.bad(".fill wants a count and an optional width and value"));
385 }
386 let count = self.number(&args[0])?;
387 let count = self.count(count)?;
388 let width = match args.get(1) {
389 Some(arg) => {
390 let width = self.number(arg)?;
391 self.count(width)?
392 }
393 None => 1,
394 };
395 let value = match args.get(2) {
396 Some(arg) => self.number(arg)?,
397 None => 0,
398 };
399 if width > 8 {
400 return Err(self.bad(".fill of items wider than eight bytes is not written"));
401 }
402 let one = value.to_le_bytes();
403 for _ in 0..count {
404 self.put(&one[..width as usize])?;
405 }
406 }
407
408 "align" | "balign" | "p2align" => self.align(word, &args)?,
409 "org" => {
410 let Some(first) = args.first() else {
411 return Err(self.bad(".org with nothing after it"));
412 };
413 let to = self.number(first)?;
414 let to = self.count(to)?;
415 let fill = match args.get(1) {
416 Some(arg) => self.byte(arg)?,
417 None => 0,
418 };
419 let at = self.at();
420 if to < at {
421 let what = format!(".org back to {to} from {at}, which would overwrite bytes");
422 return Err(self.bad(&what));
423 }
424 self.pad(to - at, fill)?;
425 }
426
427 "globl" | "global" => self.bind(&args, Binding::Global)?,
428 "weak" => self.bind(&args, Binding::Weak)?,
429 "local" => self.bind(&args, Binding::Local)?,
430 "hidden" => self.sight(&args, Visibility::Hidden)?,
431 "protected" => self.sight(&args, Visibility::Protected)?,
432 "internal" => self.sight(&args, Visibility::Hidden)?,
435
436 "type" => self.type_directive(&args)?,
437 "err" | "error" => {
438 let what = unquoted(args.first().map_or("", |arg| arg.trim()));
439 return Err(self.bad(&format!("the file says so itself: {what}")));
440 }
441 "size" => {
442 let [name, what] = self.two(&args, ".size")?;
443 let sum = self.expression(&what)?;
444 let sym = self.sym(&name);
445 self.sizes.push((sym, sum, self.line));
446 }
447 "set" | "equ" | "equiv" => {
448 let [name, what] = self.two(&args, &format!(".{word}"))?;
449 let sum = self.expression(&what)?;
450 let sym = self.sym(&name);
451 self.sets.push((sym, sum, self.line));
452 }
453 "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
454
455 "file" => {
459 let what = args.first().map_or("", |arg| arg.trim());
460 if what.starts_with('"') {
461 self.files.push(unquoted(what));
462 }
463 }
464
465 "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
469 | "intel_syntax" | "warning" => {}
470 _ if word.starts_with("cfi_") => {}
471
472 _ => {
473 let what = format!(
474 "'.{word}' is a directive this compiler does not know, so nothing was written \
475 for it"
476 );
477 return Err(self.bad(&what));
478 }
479 }
480 Ok(())
481 }
482
483 fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
485 if !rest.trim().is_empty() && rest.trim() != "0" {
490 let what =
491 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
492 return Err(self.bad(&what));
493 }
494 let name = format!(".{word}");
495 let shape = Shape::of(&name);
496 self.section(&name, shape);
497 Ok(())
498 }
499
500 fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
502 let Some(name) = args.first() else {
503 return Err(self.bad(".section with no name"));
504 };
505 let name = unquoted(name.trim());
506 if name.is_empty() {
507 return Err(self.bad(".section with no name"));
508 }
509 let mut shape = Shape::of(&name);
512 if let Some(flags) = args.get(1) {
513 let letters = unquoted(flags.trim());
514 shape = Shape { bits: true, ..Shape::default() };
515 for letter in letters.chars() {
516 match letter {
517 'a' => shape.alloc = true,
518 'w' => shape.write = true,
519 'x' => shape.exec = true,
520 'T' => shape.thread = true,
521 'M' | 'S' | 'G' | 'o' | 'e' | 'R' | 'd' => {}
525 _ => {
526 let what = format!("'{letter}' is not a section flag this compiler knows");
527 return Err(self.bad(&what));
528 }
529 }
530 }
531 }
532 if let Some(kind) = args.get(2) {
533 let kind = kind.trim().trim_start_matches(['@', '%']);
534 let kind = unquoted(kind);
535 match kind.as_str() {
536 "progbits" => shape.bits = true,
537 "nobits" => shape.bits = false,
538 "init_array" => shape.array = Some(Array::Init),
539 "fini_array" => shape.array = Some(Array::Fini),
540 "preinit_array" => shape.array = Some(Array::Preinit),
541 "note" => shape.bits = true,
542 _ => {
543 let what = format!("'{kind}' is not a section type this compiler writes");
544 return Err(self.bad(&what));
545 }
546 }
547 }
548 self.section(&name, shape);
549 Ok(())
550 }
551
552 fn section(&mut self, name: &str, shape: Shape) {
558 if let Some(&at) = self.named.get(name) {
559 self.go(at);
560 return;
561 }
562 let at = self.parts.len();
563 self.parts.push(Part {
564 name: name.to_owned(),
565 bytes: Vec::new(),
566 size: 0,
567 align: 1,
568 shape,
569 relocs: Vec::new(),
570 });
571 self.named.insert(name.to_owned(), at);
572 self.go(at);
573 }
574
575 fn go(&mut self, at: usize) {
577 if at != self.here {
578 self.before = Some(self.here);
579 self.here = at;
580 }
581 }
582
583 fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
585 if args.is_empty() {
586 return Err(self.bad("a data directive with nothing after it"));
587 }
588 for arg in args {
589 let sum = self.expression(arg)?;
590 let at = self.at();
591 if let Some(value) = sum.flat() {
592 self.put(&value.to_le_bytes()[..width as usize])?;
593 continue;
594 }
595 let part = self.here;
598 if !self.parts[part].shape.bits {
599 let what = format!(
600 "'{}' holds no bytes and this asks the linker to write some into it",
601 self.parts[part].name
602 );
603 return Err(self.bad(&what));
604 }
605 self.put(&vec![0u8; width as usize])?;
606 self.fixups.push(Fixup { part, at, width, sum, reach: Reach::Near, line: self.line });
607 }
608 Ok(())
609 }
610
611 fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
613 for arg in args {
614 let mut bytes = self.string(arg.trim())?;
615 if terminated {
616 bytes.push(0);
617 }
618 self.put(&bytes)?;
619 }
620 Ok(())
621 }
622
623 fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
629 let Some(head) = args.first() else {
630 return Err(self.bad(&format!(".{word} with nothing after it")));
631 };
632 let first = self.number(head)?;
633 let first = self.count(first)?;
634 let boundary = if word == "p2align" {
635 if first > 31 {
636 return Err(self.bad(".p2align of more than two gigabytes"));
637 }
638 1u64 << first
639 } else {
640 first
641 };
642 if boundary == 0 || !boundary.is_power_of_two() {
643 let what = format!("an alignment of {boundary}, which is not a power of two");
644 return Err(self.bad(&what));
645 }
646 let default = if self.parts[self.here].shape.exec { 0x90 } else { 0 };
650 let fill = match args.get(1) {
651 Some(arg) if !arg.trim().is_empty() => self.byte(arg)?,
652 _ => default,
653 };
654 let at = self.at();
655 let over = at % boundary;
656 let need = if over == 0 { 0 } else { boundary - over };
657 if let Some(most) = args.get(2).filter(|arg| !arg.trim().is_empty()) {
660 let most = self.number(&most.clone())?;
661 if need > self.count(most)? {
662 return Ok(());
663 }
664 }
665 let part = &mut self.parts[self.here];
666 part.align = part.align.max(boundary);
667 self.pad(need, fill)
668 }
669
670 fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
672 for arg in args {
673 let sym = self.sym(arg.trim());
674 self.syms[sym].binding = binding;
675 }
676 Ok(())
677 }
678
679 fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
681 for arg in args {
682 let sym = self.sym(arg.trim());
683 self.syms[sym].visibility = visibility;
684 }
685 Ok(())
686 }
687
688 fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
690 let [name, what] = self.two(args, ".type")?;
691 let what = unquoted(what.trim().trim_start_matches(['@', '%']));
692 let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
693 "func" | "function" => Sort::Func,
694 "object" | "gnu_unique_object" => Sort::Object,
695 "tls_object" | "tls" => Sort::Thread,
696 "notype" | "" => Sort::Untyped,
697 other => {
698 let what = format!("'{other}' is not a symbol type this compiler writes");
699 return Err(self.bad(&what));
700 }
701 };
702 let sym = self.sym(name.trim());
703 self.syms[sym].sort = sort;
704 Ok(())
705 }
706
707 fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
714 if !(2..=3).contains(&args.len()) {
715 return Err(
716 self.bad("a common directive wants a name, a size and an optional alignment")
717 );
718 }
719 let name = args[0].trim().to_owned();
720 let size = self.number(&args[1])?;
721 let size = self.count(size)?;
722 let align = match args.get(2) {
723 Some(arg) => {
724 let align = self.number(&arg.clone())?;
725 self.count(align)?.max(1)
726 }
727 None => size.next_power_of_two().clamp(1, 16),
730 };
731 if !align.is_power_of_two() {
732 let what = format!("an alignment of {align}, which is not a power of two");
733 return Err(self.bad(&what));
734 }
735 let sym = self.sym(&name);
736 self.syms[sym].sort = Sort::Object;
740 if local {
741 let was = self.here;
742 self.section(".bss", Shape::of(".bss"));
743 let part = &mut self.parts[self.here];
744 part.align = part.align.max(align);
745 let over = part.size % align;
746 if over != 0 {
747 part.size += align - over;
748 }
749 let offset = self.parts[self.here].size;
750 self.parts[self.here].size += size;
751 let at = self.here;
752 self.syms[sym].at = Held::In { part: at, offset };
753 self.syms[sym].size = size;
754 self.syms[sym].binding = Binding::Local;
755 self.go(was);
756 } else {
757 self.syms[sym].at = Held::Common { size, align };
758 self.syms[sym].size = size;
759 self.syms[sym].binding = Binding::Global;
760 }
761 Ok(())
762 }
763
764 fn at(&self) -> u64 {
766 let part = &self.parts[self.here];
767 if part.shape.bits { part.bytes.len() as u64 } else { part.size }
768 }
769
770 fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
772 let part = &mut self.parts[self.here];
773 if !part.shape.bits {
774 if bytes.iter().all(|byte| *byte == 0) {
775 part.size += bytes.len() as u64;
778 return Ok(());
779 }
780 let what = format!("'{}' holds no bytes and this puts some in it", part.name);
781 return Err(Trouble { line: self.line, why: what });
782 }
783 part.bytes.extend_from_slice(bytes);
784 part.size = part.bytes.len() as u64;
785 Ok(())
786 }
787
788 fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
790 let part = &mut self.parts[self.here];
791 if !part.shape.bits {
792 part.size += count;
793 return Ok(());
794 }
795 part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
796 part.size = part.bytes.len() as u64;
797 Ok(())
798 }
799
800 fn sym(&mut self, name: &str) -> usize {
802 if let Some(&at) = self.known.get(name) {
803 return at;
804 }
805 let at = self.syms.len();
806 self.syms.push(Sym {
807 name: name.to_owned(),
808 at: Held::Undefined,
809 size: 0,
810 sort: Sort::Untyped,
811 binding: Binding::Local,
815 visibility: Visibility::Default,
816 numbered: name.contains('\u{1}'),
819 });
820 self.known.insert(name.to_owned(), at);
821 at
822 }
823
824 fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
826 if args.len() != 2 {
827 let why = format!("{what} wants two operands and was given {}", args.len());
828 return Err(Trouble { line: self.line, why });
829 }
830 Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
831 }
832
833 fn number(&mut self, text: &str) -> Result<i64, Trouble> {
835 let sum = self.expression(text)?;
836 sum.flat().ok_or_else(|| Trouble {
837 line: self.line,
838 why: format!("'{}' has to be a number here and it names something", text.trim()),
839 })
840 }
841
842 fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
844 let value = self.number(text)?;
845 u8::try_from(value & 0xff).map_err(|_| Trouble {
846 line: self.line,
847 why: format!("{value} does not fit in a byte"),
848 })
849 }
850
851 fn count(&self, value: i64) -> Result<u64, Trouble> {
853 u64::try_from(value).map_err(|_| Trouble {
854 line: self.line,
855 why: format!("{value} is negative and this is a length"),
856 })
857 }
858
859 fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
861 let here = (self.here, self.at() as i64);
862 let mut parser = Parser { text: text.trim(), at: 0, here };
863 let sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
864 for term in &sum.terms {
867 if let What::Symbol(name) = &term.what {
868 let name = name.clone();
869 self.sym(&name);
870 }
871 }
872 Ok(sum)
873 }
874
875 fn bad(&self, why: &str) -> Trouble {
877 Trouble { line: self.line, why: why.to_owned() }
878 }
879
880 fn finish(mut self) -> Result<Assembled, Trouble> {
882 self.resolve_sets()?;
883 self.resolve_sizes()?;
884 self.resolve_fixups()?;
885 let keep: Vec<bool> = self
889 .parts
890 .iter()
891 .enumerate()
892 .map(|(at, part)| {
893 part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
894 })
895 .collect();
896 let mut moved = vec![0usize; self.parts.len()];
897 let mut parts = Vec::with_capacity(self.parts.len());
898 for (at, part) in self.parts.into_iter().enumerate() {
899 if keep[at] {
900 moved[at] = parts.len();
901 parts.push(part);
902 }
903 }
904 let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
905 for file in self.files {
908 names.push(Name {
909 name: file,
910 at: Held::Absolute(0),
911 size: 0,
912 sort: Sort::File,
913 binding: Binding::Local,
914 visibility: Visibility::Default,
915 });
916 }
917 for sym in self.syms {
918 if sym.numbered {
923 continue;
924 }
925 let at = match sym.at {
926 Held::In { part, offset } => Held::In { part: moved[part], offset },
927 other => other,
928 };
929 let binding = match (at, sym.binding) {
930 (Held::Undefined, Binding::Local) => Binding::Global,
931 (_, binding) => binding,
932 };
933 names.push(Name {
934 name: sym.name,
935 at,
936 size: sym.size,
937 sort: sym.sort,
938 binding,
939 visibility: sym.visibility,
940 });
941 }
942 Ok(Assembled { parts, names })
943 }
944
945 fn resolve_sets(&mut self) -> Result<(), Trouble> {
948 while !self.sets.is_empty() {
949 let mut done = Vec::new();
950 for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
951 if let Ok(residue) = self.reduce(sum) {
952 done.push((at, *sym, self.settled(&residue, *line)?));
953 }
954 }
955 if done.is_empty() {
956 let (sym, _, line) = &self.sets[0];
957 let why = format!(
958 "'{}' is set to something that is set to it, so neither has a value",
959 self.syms[*sym].name
960 );
961 return Err(Trouble { line: *line, why });
962 }
963 for (_, sym, held) in &done {
964 self.syms[*sym].at = *held;
965 }
966 for (at, _, _) in done.iter().rev() {
968 self.sets.remove(*at);
969 }
970 }
971 Ok(())
972 }
973
974 fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
976 match residue.left.as_slice() {
977 [] => Ok(Held::Absolute(residue.constant as u64)),
978 [Left { coeff: 1, at: Some((part, offset)), .. }] => {
981 Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
982 }
983 _ => Err(Trouble {
984 line,
985 why: "a set to something that is neither a number nor a place in this file"
986 .to_owned(),
987 }),
988 }
989 }
990
991 fn resolve_sizes(&mut self) -> Result<(), Trouble> {
993 for (sym, sum, line) in std::mem::take(&mut self.sizes) {
994 let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
995 if !residue.left.is_empty() {
996 let why = format!(
997 "the size of '{}' is not a number, and a size has to be one",
998 self.syms[sym].name
999 );
1000 return Err(Trouble { line, why });
1001 }
1002 let size = self.count(residue.constant).map_err(|_| Trouble {
1003 line,
1004 why: format!("'{}' is given a negative size", self.syms[sym].name),
1005 })?;
1006 self.syms[sym].size = size;
1007 }
1008 Ok(())
1009 }
1010
1011 fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1013 for fixup in std::mem::take(&mut self.fixups) {
1014 let line = fixup.line;
1015 let bad = |why: String| Trouble { line, why };
1016 if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1024 let [
1025 Term { coeff: 1, what: What::Symbol(name) },
1026 Term { coeff: -1, what: What::Here { at: end, .. } },
1027 ] = fixup.sum.terms.as_slice()
1028 else {
1029 return Err(bad(
1030 "a reach through the global offset table in something other than an \
1031 instruction, which is not an expression this compiler writes"
1032 .to_owned(),
1033 ));
1034 };
1035 let kind =
1036 if fixup.reach == Reach::Table { Reference::Got } else { Reference::Thread };
1037 self.parts[fixup.part].relocs.push(Reloc {
1038 at: fixup.at as usize,
1039 symbol: name.clone(),
1040 kind,
1041 addend: fixup.sum.constant + fixup.at as i64 - end,
1042 after: (end - fixup.at as i64 - 4).max(0) as u8,
1043 });
1044 continue;
1045 }
1046 let residue = self.reduce(&fixup.sum).map_err(|why| Trouble { line, why })?;
1047 let (symbol, kind, addend, after) = match residue.left.as_slice() {
1048 [] => {
1049 let width = fixup.width as usize;
1057 let room = 8 * width as u32;
1058 let low = -(1i64 << (room - 1));
1059 let high = if fixup.reach == Reach::Branch {
1060 (1i64 << (room - 1)) - 1
1061 } else {
1062 (1i64 << room) - 1
1063 };
1064 if width < 8 && (residue.constant < low || residue.constant > high) {
1065 return Err(bad(format!(
1066 "{} written into {width} bytes, which does not reach it",
1067 residue.constant
1068 )));
1069 }
1070 let bytes = residue.constant.to_le_bytes();
1071 let at = fixup.at as usize;
1072 let part = &mut self.parts[fixup.part];
1073 part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1074 continue;
1075 }
1076 [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1078 let kind = Reference::Address { bytes: fixup.width };
1079 (name.clone(), kind, residue.constant, 0)
1080 }
1081 [
1086 Left { coeff: 1, what: What::Symbol(name), .. },
1087 Left { coeff: -1, at: Some((part, offset)), .. },
1088 ]
1089 | [
1090 Left { coeff: -1, at: Some((part, offset)), .. },
1091 Left { coeff: 1, what: What::Symbol(name), .. },
1092 ] => {
1093 if *part != fixup.part {
1094 return Err(bad(
1095 "a distance that is subtracted from somewhere in another section"
1096 .to_owned(),
1097 ));
1098 }
1099 if fixup.width != 4 {
1100 return Err(bad(format!(
1101 "a distance written into {} bytes, and four is the only width a \
1102 relocation says one at",
1103 fixup.width
1104 )));
1105 }
1106 let addend = residue.constant + fixup.at as i64 - offset;
1113 let kind = if fixup.reach == Reach::Branch {
1114 Reference::Call
1115 } else {
1116 Reference::Data
1117 };
1118 let after = (offset - fixup.at as i64 - 4).max(0);
1121 (name.clone(), kind, addend, after as u8)
1122 }
1123 [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1124 return Err(bad(
1125 "the address of these bytes themselves, which has no symbol to be \
1126 relocated against"
1127 .to_owned(),
1128 ));
1129 }
1130 _ => {
1131 return Err(bad(
1132 "an expression that does not come out as a number, an address, or a \
1133 distance, and those are what a relocation can say"
1134 .to_owned(),
1135 ));
1136 }
1137 };
1138 if let Some(&sym) = self.known.get(&symbol) {
1142 if self.syms[sym].numbered {
1143 let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1144 return Err(bad(format!(
1145 "'{number}f' goes on to a '{number}:' and there is none below it"
1146 )));
1147 }
1148 }
1149 if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1150 return Err(bad(format!(
1151 "the address of '{symbol}' written into {} bytes, and this machine relocates \
1152 an address at four or eight",
1153 fixup.width
1154 )));
1155 }
1156 self.parts[fixup.part].relocs.push(Reloc {
1157 at: fixup.at as usize,
1158 symbol,
1159 kind,
1160 addend,
1161 after,
1162 });
1163 }
1164 Ok(())
1165 }
1166
1167 fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
1176 let mut constant = sum.constant;
1177 let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
1178 let mut outside: Vec<(i64, String)> = Vec::new();
1179 for term in &sum.terms {
1180 match &term.what {
1181 What::Here { part, at } => {
1182 placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
1183 }
1184 What::Symbol(name) => {
1185 let Some(&at) = self.known.get(name) else {
1186 return Err(format!("'{name}' is named and never said"));
1187 };
1188 match self.syms[at].at {
1189 Held::Absolute(value) => constant += term.coeff * value as i64,
1190 Held::In { part, offset } => placed.entry(part).or_default().push((
1191 term.coeff,
1192 term.what.clone(),
1193 offset as i64,
1194 )),
1195 Held::Undefined | Held::Common { .. } => {
1198 if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
1199 outside.push((term.coeff, name.clone()));
1200 } else {
1201 return Err(format!("'{name}' is not worked out yet"));
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 let mut left: Vec<Left> = Vec::new();
1209 for (part, terms) in placed {
1210 let (_, chosen, base) = terms[0].clone();
1211 let mut net = 0;
1212 for (coeff, _, offset) in &terms {
1213 net += coeff;
1214 constant += coeff * (offset - base);
1215 }
1216 if net != 0 {
1217 left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
1218 }
1219 }
1220 let mut together: BTreeMap<String, i64> = BTreeMap::new();
1221 for (coeff, name) in outside {
1222 *together.entry(name).or_default() += coeff;
1223 }
1224 for (name, coeff) in together {
1225 if coeff != 0 {
1226 left.push(Left { coeff, what: What::Symbol(name), at: None });
1227 }
1228 }
1229 Ok(Residue { constant, left })
1230 }
1231}
1232
1233#[derive(Debug, Clone)]
1235struct Residue {
1236 constant: i64,
1237 left: Vec<Left>,
1238}
1239
1240#[derive(Debug, Clone)]
1242struct Left {
1243 coeff: i64,
1245 what: What,
1247 at: Option<(usize, i64)>,
1250}
1251
1252#[derive(Debug, Clone, Default, PartialEq, Eq)]
1254struct Sum {
1255 constant: i64,
1256 terms: Vec<Term>,
1257}
1258
1259#[derive(Debug, Clone, PartialEq, Eq)]
1261struct Term {
1262 coeff: i64,
1263 what: What,
1264}
1265
1266#[derive(Debug, Clone, PartialEq, Eq)]
1268enum What {
1269 Symbol(String),
1271 Here { part: usize, at: i64 },
1274}
1275
1276impl Sum {
1277 fn flat(&self) -> Option<i64> {
1279 self.terms.is_empty().then_some(self.constant)
1280 }
1281
1282 fn of(what: What) -> Sum {
1284 Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1285 }
1286
1287 fn just(value: i64) -> Sum {
1289 Sum { constant: value, terms: Vec::new() }
1290 }
1291
1292 fn plus(mut self, other: Sum) -> Sum {
1294 self.constant = self.constant.wrapping_add(other.constant);
1295 self.terms.extend(other.terms);
1296 self
1297 }
1298
1299 fn minus(self) -> Sum {
1301 Sum {
1302 constant: self.constant.wrapping_neg(),
1303 terms: self
1304 .terms
1305 .into_iter()
1306 .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
1307 .collect(),
1308 }
1309 }
1310
1311 fn times(self, factor: i64) -> Sum {
1313 Sum {
1314 constant: self.constant.wrapping_mul(factor),
1315 terms: self
1316 .terms
1317 .into_iter()
1318 .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
1319 .collect(),
1320 }
1321 }
1322}
1323
1324struct Parser<'a> {
1326 text: &'a str,
1327 at: usize,
1328 here: (usize, i64),
1329}
1330
1331impl Parser<'_> {
1332 fn whole(&mut self) -> Result<Sum, String> {
1334 let sum = self.bitwise()?;
1335 self.space();
1336 if self.at < self.text.len() {
1337 return Err(format!(
1338 "'{}' is left over at the end of an expression",
1339 &self.text[self.at..]
1340 ));
1341 }
1342 Ok(sum)
1343 }
1344
1345 fn bitwise(&mut self) -> Result<Sum, String> {
1347 let mut left = self.shift()?;
1348 loop {
1349 self.space();
1350 let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
1351 let right = self.shift()?;
1352 left = self.arithmetic(left, right, op)?;
1353 }
1354 }
1355
1356 fn shift(&mut self) -> Result<Sum, String> {
1358 let mut left = self.sum()?;
1359 loop {
1360 self.space();
1361 let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
1362 let right = self.sum()?;
1363 left = self.arithmetic(left, right, op)?;
1364 }
1365 }
1366
1367 fn sum(&mut self) -> Result<Sum, String> {
1369 let mut left = self.product()?;
1370 loop {
1371 self.space();
1372 let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
1374 let right = self.product()?;
1375 left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
1376 }
1377 }
1378
1379 fn product(&mut self) -> Result<Sum, String> {
1381 let mut left = self.unary()?;
1382 loop {
1383 self.space();
1384 let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
1385 let right = self.unary()?;
1386 left = match (op, left.flat(), right.flat()) {
1390 ("*", _, Some(factor)) => left.times(factor),
1391 ("*", Some(factor), _) => right.times(factor),
1392 (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
1393 _ => return Err(format!("'{op}' of something that names a symbol")),
1394 };
1395 }
1396 }
1397
1398 fn unary(&mut self) -> Result<Sum, String> {
1400 self.space();
1401 if self.eat("-") {
1402 return Ok(self.unary()?.minus());
1403 }
1404 if self.eat("+") {
1405 return self.unary();
1406 }
1407 if self.eat("~") {
1408 let inner = self.unary()?;
1409 let value = inner
1410 .flat()
1411 .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
1412 return Ok(Sum::just(!value));
1413 }
1414 if self.eat("!") {
1415 let inner = self.unary()?;
1416 let value = inner
1417 .flat()
1418 .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
1419 return Ok(Sum::just(i64::from(value == 0)));
1420 }
1421 self.primary()
1422 }
1423
1424 fn primary(&mut self) -> Result<Sum, String> {
1426 self.space();
1427 let rest = &self.text[self.at..];
1428 if rest.is_empty() {
1429 return Err("an expression that stops before it says anything".to_owned());
1430 }
1431 if self.eat("(") {
1432 let inner = self.bitwise()?;
1433 self.space();
1434 if !self.eat(")") {
1435 return Err("a bracket that was opened and never closed".to_owned());
1436 }
1437 return Ok(inner);
1438 }
1439 let first = rest.as_bytes()[0];
1440 if first == b'\'' {
1441 return self.character();
1442 }
1443 if first.is_ascii_digit() {
1444 return self.digits();
1445 }
1446 if starts(first) {
1447 let name = self.word();
1448 if name == "." {
1450 let (part, at) = self.here;
1451 return Ok(Sum::of(What::Here { part, at }));
1452 }
1453 if self.text[self.at..].starts_with('@') {
1457 return Err(format!(
1458 "'{name}@' asks for a relocation only an instruction can carry"
1459 ));
1460 }
1461 return Ok(Sum::of(What::Symbol(name)));
1462 }
1463 Err(format!("'{rest}' is not the start of an expression"))
1464 }
1465
1466 fn digits(&mut self) -> Result<Sum, String> {
1468 let rest = &self.text[self.at..];
1469 let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
1470 (16, 2)
1471 } else if rest.starts_with("0b") || rest.starts_with("0B") {
1472 (2, 2)
1473 } else if rest.len() > 1 && rest.starts_with('0') {
1474 (8, 1)
1475 } else {
1476 (10, 0)
1477 };
1478 let body = &rest[skip..];
1479 let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
1480 if end == 0 {
1481 return Err(format!("'{rest}' starts like a number and is not one"));
1482 }
1483 let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
1484 let value = u64::from_str_radix(&text, radix)
1487 .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
1488 self.at += skip + end;
1489 while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
1492 self.at += 1;
1493 }
1494 Ok(Sum::just(value as i64))
1495 }
1496
1497 fn character(&mut self) -> Result<Sum, String> {
1499 self.at += 1;
1500 let rest = &self.text[self.at..];
1501 let mut chars = rest.chars();
1502 let Some(first) = chars.next() else {
1503 return Err("a quote with no character after it".to_owned());
1504 };
1505 let (value, used) = if first == '\\' {
1506 let (value, used) = escape(&rest[1..])?;
1507 (value, used + 1)
1508 } else {
1509 (first as u8, first.len_utf8())
1510 };
1511 self.at += used;
1512 if self.text[self.at..].starts_with('\'') {
1515 self.at += 1;
1516 }
1517 Ok(Sum::just(i64::from(value)))
1518 }
1519
1520 fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
1522 let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
1523 return Err(format!("'{op}' of something that names a symbol"));
1524 };
1525 Ok(Sum::just(self.arithmetic_number(a, b, op)?))
1526 }
1527
1528 fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
1530 Ok(match op {
1531 "|" => a | b,
1532 "^" => a ^ b,
1533 "&" => a & b,
1534 "<<" => a.wrapping_shl(shift(b)?),
1535 ">>" => a.wrapping_shr(shift(b)?),
1536 "*" => a.wrapping_mul(b),
1537 "/" if b == 0 => return Err("a division by zero".to_owned()),
1538 "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
1539 "/" => a.wrapping_div(b),
1540 "%" => a.wrapping_rem(b),
1541 _ => return Err(format!("'{op}' is not an operator this compiler knows")),
1542 })
1543 }
1544
1545 fn word(&mut self) -> String {
1547 let body = &self.text[self.at..];
1548 let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
1549 let word = body[..end].to_owned();
1550 self.at += end;
1551 word
1552 }
1553
1554 fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
1559 for op in ops {
1560 if self.text[self.at..].starts_with(op) {
1561 self.at += op.len();
1562 return Some(op);
1563 }
1564 }
1565 None
1566 }
1567
1568 fn eat(&mut self, what: &str) -> bool {
1570 if self.text[self.at..].starts_with(what) {
1571 self.at += what.len();
1572 return true;
1573 }
1574 false
1575 }
1576
1577 fn space(&mut self) {
1579 while self.text[self.at..].starts_with([' ', '\t']) {
1580 self.at += 1;
1581 }
1582 }
1583}
1584
1585impl Reader {
1586 fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
1588 let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
1589 let body = text
1590 .strip_prefix('"')
1591 .and_then(|rest| rest.strip_suffix('"'))
1592 .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
1593 let mut out = Vec::with_capacity(body.len());
1594 let mut at = 0;
1595 while at < body.len() {
1596 let rest = &body[at..];
1597 let first = rest.as_bytes()[0];
1598 if first == b'\\' {
1599 let (value, used) =
1600 escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
1601 out.push(value);
1602 at += used + 1;
1603 continue;
1604 }
1605 let ch = rest.chars().next().unwrap_or('\0');
1606 let mut buffer = [0u8; 4];
1607 out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
1608 at += ch.len_utf8();
1609 }
1610 Ok(out)
1611 }
1612}
1613
1614fn shift(by: i64) -> Result<u32, String> {
1616 u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
1617}
1618
1619fn escape(rest: &str) -> Result<(u8, usize), String> {
1623 let bytes = rest.as_bytes();
1624 let Some(&first) = bytes.first() else {
1625 return Err("a backslash with nothing after it".to_owned());
1626 };
1627 let simple = match first {
1628 b'n' => Some(b'\n'),
1629 b't' => Some(b'\t'),
1630 b'r' => Some(b'\r'),
1631 b'f' => Some(0x0c),
1632 b'b' => Some(0x08),
1633 b'v' => Some(0x0b),
1634 b'a' => Some(0x07),
1635 b'e' => Some(0x1b),
1636 b'\\' => Some(b'\\'),
1637 b'"' => Some(b'"'),
1638 b'\'' => Some(b'\''),
1639 _ => None,
1640 };
1641 if let Some(value) = simple {
1642 return Ok((value, 1));
1643 }
1644 if first == b'x' || first == b'X' {
1645 let end = bytes[1..]
1646 .iter()
1647 .position(|byte| !byte.is_ascii_hexdigit())
1648 .map_or(bytes.len(), |at| at + 1);
1649 if end == 1 {
1650 return Err("a hex escape with no digits in it".to_owned());
1651 }
1652 let text = &rest[1..end];
1655 let text = &text[text.len().saturating_sub(2)..];
1656 let value =
1657 u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
1658 return Ok((value, end));
1659 }
1660 if (b'0'..=b'7').contains(&first) {
1661 let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
1662 let value = u32::from_str_radix(&rest[..end], 8)
1663 .map_err(|_| "an octal escape that is not one".to_owned())?;
1664 return Ok(((value & 0xff) as u8, end));
1665 }
1666 Err(format!("'\\{}' is not an escape this compiler knows", first as char))
1670}
1671
1672fn labelled(text: &str) -> Option<String> {
1679 let bytes = text.as_bytes();
1680 if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
1681 return None;
1682 }
1683 let end = text.find(|ch: char| !carries_on(ch as u8))?;
1684 if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
1686 return None;
1687 }
1688 Some(text[..end].to_owned())
1689}
1690
1691fn starts(byte: u8) -> bool {
1693 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
1694}
1695
1696fn carries_on(byte: u8) -> bool {
1698 starts(byte) || byte.is_ascii_digit()
1699}
1700
1701fn counted(number: &str, nth: usize) -> String {
1708 format!("{number}\u{1}{nth}")
1709}
1710
1711fn unquoted(text: &str) -> String {
1713 text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
1714}
1715
1716pub(crate) fn split(text: &str, on: char) -> Vec<String> {
1721 let mut out = Vec::new();
1722 let mut piece = String::new();
1723 let mut depth = 0i32;
1724 let mut quote = None;
1725 let mut chars = text.chars();
1726 while let Some(ch) = chars.next() {
1727 if let Some(mark) = quote {
1728 piece.push(ch);
1729 if ch == '\\' {
1730 if let Some(next) = chars.next() {
1731 piece.push(next);
1732 }
1733 continue;
1734 }
1735 if ch == mark {
1736 quote = None;
1737 }
1738 continue;
1739 }
1740 match ch {
1741 '"' => {
1742 quote = Some(ch);
1743 piece.push(ch);
1744 }
1745 '(' => {
1746 depth += 1;
1747 piece.push(ch);
1748 }
1749 ')' => {
1750 depth -= 1;
1751 piece.push(ch);
1752 }
1753 _ if ch == on && depth == 0 => {
1754 out.push(std::mem::take(&mut piece));
1755 }
1756 _ => piece.push(ch),
1757 }
1758 }
1759 if !piece.trim().is_empty() || !out.is_empty() {
1760 out.push(piece);
1761 }
1762 out.into_iter().map(|piece| piece.trim().to_owned()).collect()
1763}
1764
1765#[cfg(test)]
1766mod tests {
1767 use super::*;
1768
1769 use rucc_object::Reference;
1770
1771 fn assembled(text: &str) -> Assembled {
1773 match read(text) {
1774 Ok(assembled) => assembled,
1775 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
1776 }
1777 }
1778
1779 fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
1781 let part = assembled
1782 .parts
1783 .iter()
1784 .find(|part| part.name == name)
1785 .unwrap_or_else(|| panic!("there is no section called '{name}'"));
1786 part.bytes.clone()
1787 }
1788
1789 fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
1791 assembled
1792 .names
1793 .iter()
1794 .find(|name| name.name == want)
1795 .unwrap_or_else(|| panic!("there is no name called '{want}'"))
1796 }
1797
1798 fn refused(text: &str) -> Trouble {
1800 read(text).err().unwrap_or_else(|| panic!("this was read and should not have been"))
1801 }
1802
1803 #[test]
1809 fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
1810 let out =
1811 assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
1812 let text = bytes(&out, ".text");
1813 assert_eq!(
1816 text,
1817 vec![0x90, 0xe9, 0xfa, 0xff, 0xff, 0xff, 0x90, 0xe9, 0x01, 0, 0, 0, 0x90, 0xc3]
1818 );
1819 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
1820 let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
1822 assert_eq!(written, vec!["foo"]);
1823 }
1824
1825 #[test]
1826 fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
1827 let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
1828 assert!(back.why.contains("none above it"), "{}", back.why);
1829 let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
1830 assert!(forward.why.contains("none below it"), "{}", forward.why);
1831 }
1832
1833 #[test]
1840 fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
1841 let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
1842 assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
1843 let split = assembled("\t.text\n\trep\n\tmovsq\n");
1844 assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
1845 let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
1846 assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
1847 }
1848
1849 #[test]
1856 fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
1857 let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
1858 let relocs = &out.parts[0].relocs;
1859 assert_eq!(relocs.len(), 1);
1860 assert_eq!(relocs[0].symbol, "table");
1861 assert_eq!(relocs[0].kind, Reference::Got);
1862 assert_eq!(relocs[0].addend, -4);
1865 let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
1866 assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
1867 }
1868
1869 #[test]
1875 fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
1876 let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
1877 let relocs = &out.parts[0].relocs;
1878 assert_eq!(relocs.len(), 1);
1879 assert_eq!(relocs[0].symbol, "table");
1880 assert_eq!(relocs[0].addend, -516);
1881 let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
1884 assert_eq!(named, ["table"]);
1885 }
1886
1887 #[test]
1888 fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
1889 refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
1892 }
1893
1894 #[test]
1895 fn the_probe_gmp_writes() {
1896 let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
1900 assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
1901 let foo = name(&out, "foo");
1902 assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
1903 assert_eq!(foo.binding, Binding::Global);
1904 }
1905
1906 #[test]
1907 fn every_width_of_number_is_the_bytes_it_says_it_is() {
1908 let out = assembled(
1909 "\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",
1910 );
1911 let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
1912 want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
1913 assert_eq!(bytes(&out, ".data"), want);
1914 }
1915
1916 #[test]
1917 fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
1918 let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
1921 assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
1922 }
1923
1924 #[test]
1925 fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
1926 let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
1927 assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
1928 }
1929
1930 #[test]
1931 fn space_and_fill_put_that_many_bytes_there() {
1932 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");
1933 assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
1934 }
1935
1936 #[test]
1937 fn aligning_moves_on_to_the_boundary_and_no_further() {
1938 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");
1941 let data = bytes(&out, ".data");
1942 assert_eq!(data.len(), 17);
1943 assert_eq!(data[0], 1);
1944 assert_eq!(data[8], 2);
1945 assert_eq!(data[16], 3);
1946 assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
1947 }
1948
1949 #[test]
1950 fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
1951 let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
1952 let part = &out.parts[0];
1953 assert_eq!(part.name, ".bss");
1954 assert_eq!(part.size, 4096);
1955 assert!(part.bytes.is_empty(), "the zeroes were carried after all");
1956 assert!(!part.shape.bits);
1957 }
1958
1959 #[test]
1960 fn what_a_section_directive_said_about_a_section_is_what_it_is() {
1961 let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
1962 let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
1963 assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
1964 assert!(!part.shape.write, "nothing said it was writable");
1965 }
1966
1967 #[test]
1968 fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
1969 let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
1970 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
1971 assert_eq!(bytes(&out, ".text"), vec![0x90]);
1972 }
1973
1974 #[test]
1975 fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
1976 let out = assembled(
1977 "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
1978 );
1979 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
1980 assert_eq!(bytes(&out, ".rodata"), vec![9]);
1981 }
1982
1983 #[test]
1984 fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
1985 let out = assembled(
1988 "\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",
1989 );
1990 let f = name(&out, "f");
1991 assert_eq!(f.size, 5);
1992 assert_eq!(f.sort, Sort::Func);
1993 }
1994
1995 #[test]
1996 fn a_set_may_name_something_further_down_the_file() {
1997 let out = assembled(
2000 "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
2001 table_end - table\n",
2002 );
2003 assert_eq!(name(&out, "width").at, Held::Absolute(12));
2004 }
2005
2006 #[test]
2007 fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
2008 let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
2009 assert_eq!(name(&out, "a").at, Held::Absolute(11));
2010 assert_eq!(name(&out, "b").at, Held::Absolute(10));
2011 }
2012
2013 #[test]
2014 fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
2015 let why = refused("\t.set a, b\n\t.set b, a\n");
2016 assert!(why.why.contains("neither has a value"), "{why}");
2017 }
2018
2019 #[test]
2020 fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
2021 let out = assembled("\t.data\n\t.quad message\n");
2022 let reloc = &out.parts[0].relocs[0];
2023 assert_eq!(reloc.at, 0);
2024 assert_eq!(reloc.symbol, "message");
2025 assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
2026 assert_eq!(reloc.addend, 0);
2027 assert_eq!(name(&out, "message").at, Held::Undefined);
2028 }
2029
2030 #[test]
2031 fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
2032 let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
2035 let reloc = &out.parts[0].relocs[0];
2036 assert_eq!(reloc.at, 8);
2037 assert_eq!(reloc.symbol, "message");
2038 assert_eq!(reloc.kind, Reference::Data);
2039 assert_eq!(reloc.addend, 0);
2040 }
2041
2042 #[test]
2043 fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
2044 let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
2049 let reloc = &out.parts[0].relocs[0];
2050 assert_eq!(reloc.at, 8);
2051 assert_eq!(reloc.kind, Reference::Data);
2052 assert_eq!(reloc.addend, 8);
2053 }
2054
2055 #[test]
2056 fn a_number_added_to_a_name_rides_along_in_the_addend() {
2057 let out = assembled("\t.data\n\t.quad message + 16\n");
2058 assert_eq!(out.parts[0].relocs[0].addend, 16);
2059 }
2060
2061 #[test]
2062 fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
2063 let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
2064 assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
2065 assert_eq!(name(&out, "shared").binding, Binding::Global);
2066 assert_eq!(name(&out, "mine").binding, Binding::Local);
2069 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2070 }
2071
2072 #[test]
2073 fn what_a_file_says_about_who_can_see_a_name_is_kept() {
2074 let out = assembled(
2075 "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
2076 inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
2077 );
2078 assert_eq!(name(&out, "seen").binding, Binding::Global);
2079 assert_eq!(name(&out, "maybe").binding, Binding::Weak);
2080 assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
2081 }
2082
2083 #[test]
2084 fn the_name_of_the_file_is_a_symbol_of_its_own() {
2085 let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
2088 assert_eq!(out.names[0].name, "big.s");
2089 assert_eq!(out.names[0].sort, Sort::File);
2090 assert_eq!(out.names[0].binding, Binding::Local);
2091 assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
2092 }
2093
2094 #[test]
2095 fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
2096 let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
2099 assert!(out.names.is_empty(), "{:?}", out.names);
2100 }
2101
2102 #[test]
2103 fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
2104 let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\tpopcnt %rax, %rdx\n\tret\n");
2108 assert_eq!(why.line, 4);
2109 assert!(why.why.contains("popcnt"), "{why}");
2110 }
2111
2112 #[test]
2113 fn a_function_of_instructions_is_its_bytes_and_its_size() {
2114 let out = assembled(
2117 "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
2118 id, .-id\n",
2119 );
2120 assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
2121 assert_eq!(name(&out, "id").size, 4);
2122 assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
2123 }
2124
2125 #[test]
2126 fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
2127 let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
2131 assert_eq!(bytes(&out, ".text"), vec![0xe9, 0, 0, 0, 0, 0xc3]);
2132 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2133 }
2134
2135 #[test]
2136 fn a_jump_backwards_is_the_negative_distance_to_it() {
2137 let out = assembled("\t.text\nagain:\n\tjmp again\n");
2138 assert_eq!(bytes(&out, ".text"), vec![0xe9, 0xfb, 0xff, 0xff, 0xff]);
2139 }
2140
2141 #[test]
2142 fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
2143 let out = assembled("\t.text\n\tcall puts\n");
2149 let reloc = &out.parts[0].relocs[0];
2150 assert_eq!(reloc.at, 1);
2151 assert_eq!(reloc.symbol, "puts");
2152 assert_eq!(reloc.kind, Reference::Call);
2153 assert_eq!(reloc.addend, -4);
2154 }
2155
2156 #[test]
2157 fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
2158 let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
2159 let reloc = &out.parts[0].relocs[0];
2160 assert_eq!(reloc.symbol, "message");
2161 assert_eq!(reloc.kind, Reference::Data);
2162 assert_eq!(reloc.at, 3);
2164 assert_eq!(reloc.addend, -4);
2165 }
2166
2167 #[test]
2168 fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
2169 let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
2172 assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
2173 }
2174
2175 #[test]
2176 fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
2177 let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
2181 assert_eq!(why.line, 2);
2182 assert!(why.why.contains("does not reach"), "{why}");
2183 }
2184
2185 #[test]
2186 fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
2187 let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
2192 assert_eq!(bytes(&out, ".data")[200], 200);
2193 let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
2194 assert!(why.why.contains("does not reach"), "{why}");
2195 }
2196
2197 #[test]
2198 fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
2199 let why = refused("\t.bss\n\tret\n");
2200 assert!(why.why.contains("holds no bytes"), "{why}");
2201 }
2202
2203 #[test]
2204 fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
2205 let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
2206 assert_eq!(why.line, 3);
2207 assert!(why.why.contains(".reloc"), "{why}");
2208 }
2209
2210 #[test]
2211 fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
2212 let out = assembled(
2215 "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
2216 3\n",
2217 );
2218 assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
2219 }
2220
2221 #[test]
2222 fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
2223 let why = refused("\t.data\n\t/* and then nothing\n");
2224 assert!(why.why.contains("never closed"), "{why}");
2225 }
2226
2227 #[test]
2228 fn a_string_with_a_comment_character_in_it_is_a_string() {
2229 let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
2230 assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
2231 }
2232
2233 #[test]
2234 fn several_statements_on_one_line_are_several_statements() {
2235 let out = assembled("\t.data; .byte 1; .byte 2\n");
2236 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2237 }
2238
2239 #[test]
2240 fn a_section_nothing_was_ever_put_in_is_dropped() {
2241 let out = assembled("\t.data\n\t.byte 1\n");
2244 assert_eq!(out.parts.len(), 1);
2245 assert_eq!(out.parts[0].name, ".data");
2246 }
2247
2248 #[test]
2249 fn a_section_with_nothing_in_it_but_a_name_is_kept() {
2250 let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
2253 assert_eq!(out.parts.len(), 1);
2254 assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
2255 }
2256
2257 #[test]
2258 fn an_error_directive_is_the_file_saying_it_refuses_itself() {
2259 let why = refused("\t.error \"this is not the machine for it\"\n");
2260 assert!(why.why.contains("not the machine for it"), "{why}");
2261 }
2262}