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, nops};
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 long = std::collections::HashSet::new();
75 loop {
76 let mut reader = Reader { long: long.clone(), ..Reader::default() };
77 reader.run(text)?;
78 match reader.finish()? {
79 Ok(done) => return Ok(done),
80 Err(grow) => long.extend(grow),
81 }
82 }
83}
84
85#[derive(Debug, Clone)]
91struct Sym {
92 name: String,
93 at: Held,
94 size: u64,
95 sort: Sort,
96 binding: Binding,
97 visibility: Visibility,
98 numbered: bool,
101}
102
103#[derive(Debug, Clone)]
105struct Fixup {
106 part: usize,
107 at: u64,
108 width: u8,
109 sum: Sum,
110 reach: Reach,
114 branch: Option<usize>,
117 jump: bool,
120 line: usize,
121}
122
123#[derive(Debug, Clone, Copy)]
125struct Aligned {
126 part: usize,
127 at: u64,
129 boundary: u64,
130 most: Option<u64>,
132 need: u64,
134}
135
136#[derive(Debug)]
138struct Frame {
139 part: usize,
140 start: u64,
141 len: u64,
142 sym: usize,
145 rows: crate::unwind::Rows,
146 cfa: i32,
149 remembered: Vec<i32>,
151}
152
153#[derive(Debug, Default)]
155struct Reader {
156 parts: Vec<Part>,
157 named: HashMap<String, usize>,
159 here: usize,
161 stack: Vec<usize>,
163 before: Option<usize>,
165 syms: Vec<Sym>,
166 known: HashMap<String, usize>,
167 counts: HashMap<String, usize>,
170 labelled: std::collections::HashSet<usize>,
173 fixups: Vec<Fixup>,
174 sets: Vec<(usize, Sum, usize)>,
176 sizes: Vec<(usize, Sum, usize)>,
178 current: HashMap<String, String>,
182 relocated: std::collections::HashSet<usize>,
186 said_local: std::collections::HashSet<usize>,
190 frame: Option<Frame>,
192 frames: Vec<Frame>,
194 no_unwind: bool,
197 files: Vec<String>,
201 long: std::collections::HashSet<usize>,
203 branches: usize,
205 aligns: Vec<Aligned>,
207 line: usize,
208}
209
210impl Reader {
211 fn run(&mut self, text: &str) -> Result<(), Trouble> {
213 self.section(".text", Shape::of(".text"));
216 let mut commenting = false;
217 for (index, raw) in text.lines().enumerate() {
218 self.line = index + 1;
219 let line = self.strip(raw, &mut commenting)?;
220 for statement in split(&line, ';') {
221 self.statement(statement.trim())?;
222 }
223 }
224 if commenting {
225 return Err(self.bad("a block comment was opened and never closed"));
226 }
227 Ok(())
228 }
229
230 fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
237 let mut out = String::with_capacity(raw.len());
238 let bytes = raw.as_bytes();
239 let mut i = 0;
240 let mut quote = None;
241 while i < bytes.len() {
242 let rest = &raw[i..];
243 if *commenting {
244 if let Some(end) = rest.find("*/") {
245 *commenting = false;
246 out.push(' ');
249 i += end + 2;
250 } else {
251 return Ok(out);
252 }
253 continue;
254 }
255 let ch = bytes[i] as char;
256 if let Some(mark) = quote {
257 out.push(ch);
258 if ch == '\\' && i + 1 < bytes.len() {
259 out.push(bytes[i + 1] as char);
260 i += 2;
261 continue;
262 }
263 if ch == mark {
264 quote = None;
265 }
266 i += 1;
267 continue;
268 }
269 if ch == '"' {
270 quote = Some('"');
271 out.push(ch);
272 i += 1;
273 continue;
274 }
275 if rest.starts_with("/*") {
276 *commenting = true;
277 i += 2;
278 continue;
279 }
280 if rest.starts_with("//") || ch == '#' {
281 return Ok(out);
282 }
283 out.push(ch);
284 i += 1;
285 }
286 if quote.is_some() {
287 return Err(self.bad("a string was opened and the line ended before it closed"));
288 }
289 Ok(out)
290 }
291
292 fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
294 loop {
295 text = text.trim_start();
296 let Some(name) = labelled(text) else { break };
297 self.label(&name)?;
298 text = &text[name.len() + 1..];
299 }
300 let text = text.trim();
301 if text.is_empty() {
302 return Ok(());
303 }
304 let (word, rest) = match text.find(char::is_whitespace) {
305 Some(cut) => (&text[..cut], text[cut..].trim()),
306 None => (text, ""),
307 };
308 if let Some((name, what)) = assigned(text) {
309 return self.assign(name, what);
310 }
311 if let Some(directive) = word.strip_prefix('.') {
312 return self.directive(directive, rest);
313 }
314 if let Some((word, rest)) = repeated(word, rest) {
315 return self.instruction(&word, rest);
316 }
317 self.instruction(word, rest)
318 }
319
320 fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
334 let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
335 let mut written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
336 let mut branch = None;
338 let short = crate::instruction::short(&written).filter(|_| written.holes[0].name != ".");
339 let jump = short.is_some();
340 if let Some(short) = short {
341 if !self.long.contains(&self.branches) {
342 branch = Some(self.branches);
343 written = short;
344 }
345 self.branches += 1;
346 }
347 let part = self.here;
348 let at = self.at();
349 self.put(&written.bytes)?;
350 let end = at + written.bytes.len() as u64;
351 for hole in written.holes {
352 let here = (part, at as i64);
355 let sum = if hole.sort == Reach::Value {
356 self.expression_at(&hole.name, here)?
358 } else {
359 let what = if hole.name == "." {
360 What::Here { part, at: here.1 }
361 } else {
362 let name = self.named(&hole.name)?;
365 self.sym(&name);
366 What::Symbol(name)
367 };
368 Sum {
369 constant: hole.addend,
370 terms: vec![
371 Term { coeff: 1, what },
372 Term { coeff: -1, what: What::Here { part, at: end as i64 } },
373 ],
374 }
375 };
376 self.fixups.push(Fixup {
377 part,
378 at: at + hole.at as u64,
379 width: hole.width,
380 sum,
381 reach: hole.sort,
382 branch,
383 jump,
384 line: self.line,
385 });
386 }
387 Ok(())
388 }
389
390 fn label(&mut self, name: &str) -> Result<(), Trouble> {
392 let at = self.at();
393 let part = self.here;
394 let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
397 let held = if numbered {
398 let count = self.counts.entry(name.to_owned()).or_insert(0);
399 *count += 1;
400 counted(name, *count)
401 } else {
402 name.to_owned()
403 };
404 let sym = self.sym(&held);
405 if self.syms[sym].at != Held::Undefined {
406 let what = format!("'{name}' is defined twice");
407 return Err(self.bad(&what));
408 }
409 self.syms[sym].at = Held::In { part, offset: at };
410 self.labelled.insert(part);
411 Ok(())
412 }
413
414 fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
421 let Some(number) = word.strip_suffix(['b', 'f']) else {
422 return Ok(None);
423 };
424 if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
425 return Ok(None);
426 }
427 let count = self.counts.get(number).copied().unwrap_or(0);
428 if word.ends_with('b') {
429 if count == 0 {
430 let what =
431 format!("'{word}' goes back to a '{number}:' and there is none above it");
432 return Err(self.bad(&what));
433 }
434 return Ok(Some(counted(number, count)));
435 }
436 Ok(Some(counted(number, count + 1)))
437 }
438
439 fn named(&self, word: &str) -> Result<String, Trouble> {
444 if let Some(place) = self.numbered(word)? {
445 return Ok(place);
446 }
447 Ok(self.current.get(word).cloned().unwrap_or_else(|| word.to_owned()))
448 }
449
450 fn assign(&mut self, name: &str, what: &str) -> Result<(), Trouble> {
458 let sum = self.expression(what)?;
459 let held = match self.current.get(name) {
460 Some(_) => format!("{name}\u{1}={}", self.syms.len()),
461 None => name.to_owned(),
462 };
463 let sym = self.sym(&held);
464 if self.syms[sym].at != Held::Undefined {
465 let what = format!("'{name}' is defined twice");
466 return Err(self.bad(&what));
467 }
468 self.current.insert(name.to_owned(), held);
469 self.sets.push((sym, sum, self.line));
470 Ok(())
471 }
472
473 fn cfi(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
482 match word {
483 "cfi_startproc" => {
484 if self.frame.is_some() {
485 return Err(self.bad("a '.cfi_startproc' inside another one"));
486 }
487 let sym = self.sym(&format!("\u{1}frame{}", self.frames.len()));
488 let (part, start) = (self.here, self.at());
489 self.syms[sym].at = Held::In { part, offset: start };
490 let frame = Frame {
493 part,
494 start,
495 len: 0,
496 sym,
497 rows: Vec::new(),
498 cfa: 8,
499 remembered: Vec::new(),
500 };
501 self.frame = Some(frame);
502 return Ok(());
503 }
504 "cfi_sections" => {
505 self.no_unwind = !args.iter().any(|arg| arg.trim() == ".eh_frame");
506 return Ok(());
507 }
508 "cfi_endproc"
509 | "cfi_def_cfa"
510 | "cfi_def_cfa_offset"
511 | "cfi_adjust_cfa_offset"
512 | "cfi_def_cfa_register"
513 | "cfi_offset"
514 | "cfi_rel_offset"
515 | "cfi_restore"
516 | "cfi_remember_state"
517 | "cfi_restore_state" => {}
518 _ => return Ok(()),
519 }
520 let (here, at) = (self.here, self.at());
521 let line = self.line;
522 let bad = |why: &str| Trouble { line, why: why.to_owned() };
523 let Some(mut frame) = self.frame.take() else {
524 return Err(bad("a frame rule outside '.cfi_startproc' and '.cfi_endproc'"));
525 };
526 if frame.part != here {
527 return Err(bad("a frame rule in another section from the function it is about"));
528 }
529 let op = match word {
530 "cfi_endproc" => {
531 frame.len = at - frame.start;
532 self.frames.push(frame);
533 return Ok(());
534 }
535 "cfi_def_cfa" => {
536 let [reg, offset] = self.two(args, ".cfi_def_cfa")?;
537 frame.cfa = self.distance(&offset)?;
538 CfiOp::DefCfa { reg: self.dwarf(®)?, offset: frame.cfa }
539 }
540 "cfi_def_cfa_offset" | "cfi_adjust_cfa_offset" => {
541 let by = self.distance(args.first().map_or("", |arg| arg.as_str()))?;
542 frame.cfa = if word == "cfi_def_cfa_offset" { by } else { frame.cfa + by };
543 CfiOp::DefCfaOffset(frame.cfa)
544 }
545 "cfi_def_cfa_register" => {
546 CfiOp::DefCfaRegister(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
547 }
548 "cfi_offset" | "cfi_rel_offset" => {
549 let [reg, offset] = self.two(args, &format!(".{word}"))?;
550 let mut offset = self.distance(&offset)?;
551 if word == "cfi_rel_offset" {
554 offset -= frame.cfa;
555 }
556 if offset >= 0 || offset % 8 != 0 {
557 return Err(bad(
558 "a register saved somewhere that is not a whole slot below the end of the \
559 frame, which is the only place this writes a rule for",
560 ));
561 }
562 CfiOp::Offset { reg: self.dwarf(®)?, offset }
563 }
564 "cfi_restore" => {
565 CfiOp::Restore(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
566 }
567 "cfi_remember_state" => {
568 frame.remembered.push(frame.cfa);
569 CfiOp::RememberState
570 }
571 "cfi_restore_state" => {
572 frame.cfa = frame.remembered.pop().ok_or_else(|| {
573 bad("a '.cfi_restore_state' with nothing remembered to restore")
574 })?;
575 CfiOp::RestoreState
576 }
577 _ => unreachable!("every other word returned above"),
578 };
579 frame.rows.push(((at - frame.start) as usize, op));
580 self.frame = Some(frame);
581 Ok(())
582 }
583
584 fn distance(&mut self, text: &str) -> Result<i32, Trouble> {
586 let value = self.number(text)?;
587 i32::try_from(value).map_err(|_| self.bad(&format!("{value} is not a distance in a frame")))
588 }
589
590 fn dwarf(&self, text: &str) -> Result<u16, Trouble> {
592 let text = text.trim();
593 if let Ok(number) = text.parse::<u16>() {
594 return Ok(number);
595 }
596 let name = text.strip_prefix('%').unwrap_or(text);
597 if name == "rip" {
598 return Ok(SYSV.dwarf_return_address);
599 }
600 gpr_named(name)
601 .and_then(|(reg, _)| SYSV.dwarf(SYSV.int_class, reg))
602 .ok_or_else(|| self.bad(&format!("'{text}' is not a register a frame rule can name")))
603 }
604
605 #[allow(clippy::too_many_lines)]
607 fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
608 let args = split(rest, ',');
609 match word {
610 "text" | "data" | "bss" | "rodata" => {
611 self.plain(word, rest)?;
612 }
613 "section" => self.section_directive(&args)?,
614 "pushsection" => {
615 self.stack.push(self.here);
616 self.section_directive(&args)?;
617 }
618 "popsection" => {
619 let Some(back) = self.stack.pop() else {
620 return Err(self.bad(".popsection with nothing pushed"));
621 };
622 self.go(back);
623 }
624 "previous" => {
625 let Some(back) = self.before else {
626 return Err(self.bad(".previous with no section before this one"));
627 };
628 self.go(back);
629 }
630
631 "byte" => self.data(&args, 1)?,
632 "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
633 "long" | "int" | "4byte" => self.data(&args, 4)?,
634 "quad" | "8byte" => self.data(&args, 8)?,
635
636 "ascii" => self.text_bytes(&args, false)?,
637 "asciz" | "string" => self.text_bytes(&args, true)?,
638
639 "space" | "skip" | "zero" => {
640 if args.is_empty() || args.len() > 2 {
641 return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
642 }
643 let size = self.number(&args[0])?;
644 let size = self.count(size)?;
645 let fill = match args.get(1) {
646 Some(arg) => self.byte(arg)?,
647 None => 0,
648 };
649 self.pad(size, fill)?;
650 }
651 "fill" => {
652 if args.is_empty() || args.len() > 3 {
656 return Err(self.bad(".fill wants a count and an optional width and value"));
657 }
658 let count = self.number(&args[0])?;
659 let count = self.count(count)?;
660 let width = match args.get(1) {
661 Some(arg) => {
662 let width = self.number(arg)?;
663 self.count(width)?
664 }
665 None => 1,
666 };
667 let value = match args.get(2) {
668 Some(arg) => self.number(arg)?,
669 None => 0,
670 };
671 if width > 8 {
672 return Err(self.bad(".fill of items wider than eight bytes is not written"));
673 }
674 let one = value.to_le_bytes();
675 for _ in 0..count {
676 self.put(&one[..width as usize])?;
677 }
678 }
679
680 "align" | "balign" | "p2align" => self.align(word, &args)?,
681 "org" => {
682 let Some(first) = args.first() else {
683 return Err(self.bad(".org with nothing after it"));
684 };
685 let to = self.number(first)?;
686 let to = self.count(to)?;
687 let fill = match args.get(1) {
688 Some(arg) => self.byte(arg)?,
689 None => 0,
690 };
691 let at = self.at();
692 if to < at {
693 let what = format!(".org back to {to} from {at}, which would overwrite bytes");
694 return Err(self.bad(&what));
695 }
696 self.pad(to - at, fill)?;
697 }
698
699 "globl" | "global" => self.bind(&args, Binding::Global)?,
700 "weak" => self.bind(&args, Binding::Weak)?,
701 "local" => {
702 self.bind(&args, Binding::Local)?;
703 for arg in &args {
704 let sym = self.sym(arg.trim());
705 self.said_local.insert(sym);
706 }
707 }
708 "hidden" => self.sight(&args, Visibility::Hidden)?,
709 "protected" => self.sight(&args, Visibility::Protected)?,
710 "internal" => self.sight(&args, Visibility::Hidden)?,
713
714 "type" => self.type_directive(&args)?,
715 "err" | "error" => {
716 let what = unquoted(args.first().map_or("", |arg| arg.trim()));
717 return Err(self.bad(&format!("the file says so itself: {what}")));
718 }
719 "size" => {
720 let [name, what] = self.two(&args, ".size")?;
721 let sum = self.expression(&what)?;
722 let sym = self.sym(&name);
723 self.sizes.push((sym, sum, self.line));
724 }
725 "set" | "equ" | "equiv" => {
726 let [name, what] = self.two(&args, &format!(".{word}"))?;
727 self.assign(&name, &what)?;
728 }
729 "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
730
731 "file" => {
735 let what = args.first().map_or("", |arg| arg.trim());
736 if what.starts_with('"') {
737 self.files.push(unquoted(what));
738 }
739 }
740
741 "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
745 | "intel_syntax" | "warning" => {}
746 _ if word.starts_with("cfi_") => self.cfi(word, &args)?,
747
748 _ => {
749 let what = format!(
750 "'.{word}' is a directive this compiler does not know, so nothing was written \
751 for it"
752 );
753 return Err(self.bad(&what));
754 }
755 }
756 Ok(())
757 }
758
759 fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
761 if !rest.trim().is_empty() && rest.trim() != "0" {
766 let what =
767 format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
768 return Err(self.bad(&what));
769 }
770 let name = format!(".{word}");
771 let shape = Shape::of(&name);
772 self.section(&name, shape);
773 Ok(())
774 }
775
776 fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
778 let Some(name) = args.first() else {
779 return Err(self.bad(".section with no name"));
780 };
781 let name = unquoted(name.trim());
782 if name.is_empty() {
783 return Err(self.bad(".section with no name"));
784 }
785 let mut shape = Shape::of(&name);
788 let (mut merge, mut strings) = (false, false);
789 if let Some(flags) = args.get(1) {
790 let letters = unquoted(flags.trim());
791 shape = Shape { bits: true, ..Shape::default() };
792 for letter in letters.chars() {
793 match letter {
794 'a' => shape.alloc = true,
795 'w' => shape.write = true,
796 'x' => shape.exec = true,
797 'T' => shape.thread = true,
798 'M' => merge = true,
799 'S' => strings = true,
800 'G' | 'o' | 'e' | 'R' | 'd' => {}
804 _ => {
805 let what = format!("'{letter}' is not a section flag this compiler knows");
806 return Err(self.bad(&what));
807 }
808 }
809 }
810 }
811 if let Some(kind) = args.get(2) {
812 let kind = kind.trim().trim_start_matches(['@', '%']);
813 let kind = unquoted(kind);
814 match kind.as_str() {
815 "progbits" => shape.bits = true,
816 "nobits" => shape.bits = false,
817 "init_array" => shape.array = Some(Array::Init),
818 "fini_array" => shape.array = Some(Array::Fini),
819 "preinit_array" => shape.array = Some(Array::Preinit),
820 "note" => shape.bits = true,
821 _ => {
822 let what = format!("'{kind}' is not a section type this compiler writes");
823 return Err(self.bad(&what));
824 }
825 }
826 }
827 if merge {
830 shape.merge = args.get(3).and_then(|entry| entry.trim().parse().ok()).unwrap_or(0);
831 shape.strings = strings;
832 }
833 self.section(&name, shape);
834 Ok(())
835 }
836
837 fn section(&mut self, name: &str, shape: Shape) {
843 if let Some(&at) = self.named.get(name) {
844 self.go(at);
845 return;
846 }
847 let at = self.parts.len();
848 self.parts.push(Part {
849 name: name.to_owned(),
850 bytes: Vec::new(),
851 size: 0,
852 align: 1,
853 shape,
854 relocs: Vec::new(),
855 });
856 self.named.insert(name.to_owned(), at);
857 self.go(at);
858 }
859
860 fn go(&mut self, at: usize) {
862 if at != self.here {
863 self.before = Some(self.here);
864 self.here = at;
865 }
866 }
867
868 fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
870 if args.is_empty() {
871 return Err(self.bad("a data directive with nothing after it"));
872 }
873 for arg in args {
874 let sum = self.expression(arg)?;
875 let at = self.at();
876 if let Some(value) = sum.flat() {
877 self.put(&value.to_le_bytes()[..width as usize])?;
878 continue;
879 }
880 let part = self.here;
883 if !self.parts[part].shape.bits {
884 let what = format!(
885 "'{}' holds no bytes and this asks the linker to write some into it",
886 self.parts[part].name
887 );
888 return Err(self.bad(&what));
889 }
890 self.put(&vec![0u8; width as usize])?;
891 self.fixups.push(Fixup {
892 part,
893 at,
894 width,
895 sum,
896 reach: Reach::Near,
897 branch: None,
898 jump: false,
899 line: self.line,
900 });
901 }
902 Ok(())
903 }
904
905 fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
907 for arg in args {
908 let mut bytes = self.string(arg.trim())?;
909 if terminated {
910 bytes.push(0);
911 }
912 self.put(&bytes)?;
913 }
914 Ok(())
915 }
916
917 fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
923 let Some(head) = args.first() else {
924 return Err(self.bad(&format!(".{word} with nothing after it")));
925 };
926 let first = self.number(head)?;
927 let first = self.count(first)?;
928 let boundary = if word == "p2align" {
929 if first > 31 {
930 return Err(self.bad(".p2align of more than two gigabytes"));
931 }
932 1u64 << first
933 } else {
934 first
935 };
936 if boundary == 0 || !boundary.is_power_of_two() {
937 let what = format!("an alignment of {boundary}, which is not a power of two");
938 return Err(self.bad(&what));
939 }
940 let exec = self.parts[self.here].shape.exec;
944 let fill = match args.get(1) {
945 Some(arg) if !arg.trim().is_empty() => Some(self.byte(arg)?),
946 _ => None,
947 };
948 let at = self.at();
949 let most = match args.get(2).filter(|arg| !arg.trim().is_empty()) {
952 Some(most) => {
953 let most = self.number(&most.clone())?;
954 Some(self.count(most)?)
955 }
956 None => None,
957 };
958 let need = padding(at, boundary, most);
959 self.aligns.push(Aligned { part: self.here, at, boundary, most, need });
960 if need == 0 && most.is_some_and(|most| padding(at, boundary, None) > most) {
961 return Ok(());
962 }
963 let part = &mut self.parts[self.here];
964 part.align = part.align.max(boundary);
965 match fill {
966 Some(fill) => self.pad(need, fill),
967 None if exec => {
970 let mut bytes = Vec::new();
971 nops(usize::try_from(need).unwrap_or(usize::MAX), &mut bytes);
972 self.put(&bytes)
973 }
974 None => self.pad(need, 0),
975 }
976 }
977
978 fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
980 for arg in args {
981 let sym = self.sym(arg.trim());
982 self.syms[sym].binding = binding;
983 }
984 Ok(())
985 }
986
987 fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
989 for arg in args {
990 let sym = self.sym(arg.trim());
991 self.syms[sym].visibility = visibility;
992 }
993 Ok(())
994 }
995
996 fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
998 let [name, what] = self.two(args, ".type")?;
999 let what = unquoted(what.trim().trim_start_matches(['@', '%']));
1000 let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
1001 "func" | "function" => Sort::Func,
1002 "object" | "gnu_unique_object" => Sort::Object,
1003 "tls_object" | "tls" => Sort::Thread,
1004 "notype" | "" => Sort::Untyped,
1005 other => {
1006 let what = format!("'{other}' is not a symbol type this compiler writes");
1007 return Err(self.bad(&what));
1008 }
1009 };
1010 let sym = self.sym(name.trim());
1011 self.syms[sym].sort = sort;
1012 Ok(())
1013 }
1014
1015 fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
1022 if !(2..=3).contains(&args.len()) {
1023 return Err(
1024 self.bad("a common directive wants a name, a size and an optional alignment")
1025 );
1026 }
1027 let name = args[0].trim().to_owned();
1028 let size = self.number(&args[1])?;
1029 let size = self.count(size)?;
1030 let align = match args.get(2) {
1031 Some(arg) => {
1032 let align = self.number(&arg.clone())?;
1033 self.count(align)?.max(1)
1034 }
1035 None => size.next_power_of_two().clamp(1, 16),
1038 };
1039 if !align.is_power_of_two() {
1040 let what = format!("an alignment of {align}, which is not a power of two");
1041 return Err(self.bad(&what));
1042 }
1043 let sym = self.sym(&name);
1044 let local = local || self.said_local.contains(&sym);
1048 self.syms[sym].sort = Sort::Object;
1052 if local {
1053 let was = self.here;
1054 self.section(".bss", Shape::of(".bss"));
1055 let part = &mut self.parts[self.here];
1056 part.align = part.align.max(align);
1057 let over = part.size % align;
1058 if over != 0 {
1059 part.size += align - over;
1060 }
1061 let offset = self.parts[self.here].size;
1062 self.parts[self.here].size += size;
1063 let at = self.here;
1064 self.syms[sym].at = Held::In { part: at, offset };
1065 self.syms[sym].size = size;
1066 self.syms[sym].binding = Binding::Local;
1067 self.go(was);
1068 } else {
1069 self.syms[sym].at = Held::Common { size, align };
1070 self.syms[sym].size = size;
1071 self.syms[sym].binding = Binding::Global;
1072 }
1073 Ok(())
1074 }
1075
1076 fn at(&self) -> u64 {
1078 let part = &self.parts[self.here];
1079 if part.shape.bits { part.bytes.len() as u64 } else { part.size }
1080 }
1081
1082 fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
1084 let part = &mut self.parts[self.here];
1085 if !part.shape.bits {
1086 if bytes.iter().all(|byte| *byte == 0) {
1087 part.size += bytes.len() as u64;
1090 return Ok(());
1091 }
1092 let what = format!("'{}' holds no bytes and this puts some in it", part.name);
1093 return Err(Trouble { line: self.line, why: what });
1094 }
1095 part.bytes.extend_from_slice(bytes);
1096 part.size = part.bytes.len() as u64;
1097 Ok(())
1098 }
1099
1100 fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
1102 let part = &mut self.parts[self.here];
1103 if !part.shape.bits {
1104 part.size += count;
1105 return Ok(());
1106 }
1107 part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
1108 part.size = part.bytes.len() as u64;
1109 Ok(())
1110 }
1111
1112 fn sym(&mut self, name: &str) -> usize {
1114 if let Some(&at) = self.known.get(name) {
1115 return at;
1116 }
1117 let at = self.syms.len();
1118 self.syms.push(Sym {
1119 name: name.to_owned(),
1120 at: Held::Undefined,
1121 size: 0,
1122 sort: Sort::Untyped,
1123 binding: Binding::Local,
1127 visibility: Visibility::Default,
1128 numbered: name.contains('\u{1}'),
1131 });
1132 self.known.insert(name.to_owned(), at);
1133 at
1134 }
1135
1136 fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
1138 if args.len() != 2 {
1139 let why = format!("{what} wants two operands and was given {}", args.len());
1140 return Err(Trouble { line: self.line, why });
1141 }
1142 Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
1143 }
1144
1145 fn number(&mut self, text: &str) -> Result<i64, Trouble> {
1147 let sum = self.expression(text)?;
1148 sum.flat().ok_or_else(|| Trouble {
1149 line: self.line,
1150 why: format!("'{}' has to be a number here and it names something", text.trim()),
1151 })
1152 }
1153
1154 fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
1156 let value = self.number(text)?;
1157 u8::try_from(value & 0xff).map_err(|_| Trouble {
1158 line: self.line,
1159 why: format!("{value} does not fit in a byte"),
1160 })
1161 }
1162
1163 fn count(&self, value: i64) -> Result<u64, Trouble> {
1165 u64::try_from(value).map_err(|_| Trouble {
1166 line: self.line,
1167 why: format!("{value} is negative and this is a length"),
1168 })
1169 }
1170
1171 fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
1173 self.expression_at(text, (self.here, self.at() as i64))
1174 }
1175
1176 fn expression_at(&mut self, text: &str, here: (usize, i64)) -> Result<Sum, Trouble> {
1178 let mut parser = Parser { text: text.trim(), at: 0, here };
1179 let mut sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
1180 for term in &mut sum.terms {
1183 if let What::Symbol(name) = &term.what {
1184 let name = self.named(name)?;
1185 self.sym(&name);
1186 term.what = What::Symbol(name);
1187 }
1188 }
1189 Ok(sum)
1190 }
1191
1192 fn bad(&self, why: &str) -> Trouble {
1194 Trouble { line: self.line, why: why.to_owned() }
1195 }
1196
1197 fn finish(mut self) -> Result<Result<Assembled, Vec<usize>>, Trouble> {
1202 if self.frame.is_some() {
1203 return Err(self.bad("a '.cfi_startproc' that is never ended"));
1204 }
1205 self.unwind_table();
1206 self.resolve_sets()?;
1207 self.resolve_sizes()?;
1208 let grow = self.too_far()?;
1209 if !grow.is_empty() {
1210 return Ok(Err(grow));
1211 }
1212 self.resolve_fixups()?;
1213 let keep: Vec<bool> = self
1217 .parts
1218 .iter()
1219 .enumerate()
1220 .map(|(at, part)| {
1221 part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
1222 })
1223 .collect();
1224 let mut moved = vec![0usize; self.parts.len()];
1225 let mut parts = Vec::with_capacity(self.parts.len());
1226 for (at, part) in self.parts.into_iter().enumerate() {
1227 if keep[at] {
1228 moved[at] = parts.len();
1229 parts.push(part);
1230 }
1231 }
1232 let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
1233 for file in self.files {
1236 names.push(Name {
1237 name: file,
1238 at: Held::Absolute(0),
1239 size: 0,
1240 sort: Sort::File,
1241 binding: Binding::Local,
1242 visibility: Visibility::Default,
1243 });
1244 }
1245 for (index, sym) in self.syms.into_iter().enumerate() {
1246 if sym.numbered && !self.relocated.contains(&index) {
1251 continue;
1252 }
1253 let at = match sym.at {
1254 Held::In { part, offset } => Held::In { part: moved[part], offset },
1255 other => other,
1256 };
1257 let binding = match (at, sym.binding) {
1258 (Held::Undefined, Binding::Local) => Binding::Global,
1259 (_, binding) => binding,
1260 };
1261 names.push(Name {
1262 name: sym.name,
1263 at,
1264 size: sym.size,
1265 sort: sym.sort,
1266 binding,
1267 visibility: sym.visibility,
1268 });
1269 }
1270 Ok(Ok(Assembled { parts, names }))
1271 }
1272
1273 fn unwind_table(&mut self) {
1279 if self.frames.is_empty() || self.no_unwind {
1280 return;
1281 }
1282 let funcs: Vec<Extent> = self
1283 .frames
1284 .iter()
1285 .map(|frame| Extent {
1286 name: self.syms[frame.sym].name.clone(),
1287 start: frame.start as usize,
1288 len: frame.len as usize,
1289 align: 1,
1290 binding: Binding::Local,
1291 visibility: Visibility::Default,
1292 patch: None,
1293 })
1294 .collect();
1295 let rows: Vec<_> = self.frames.iter().map(|frame| frame.rows.clone()).collect();
1296 let Ok(table) = crate::unwind::table(&funcs, &rows, &SYSV, ObjectFormat::Elf) else {
1297 return;
1298 };
1299 for frame in &self.frames {
1300 self.relocated.insert(frame.sym);
1301 }
1302 let size = table.bytes.len() as u64;
1303 self.parts.push(Part {
1304 name: ".eh_frame".to_owned(),
1305 bytes: table.bytes,
1306 size,
1307 align: 8,
1308 shape: Shape { alloc: true, bits: true, ..Shape::default() },
1309 relocs: table.relocs,
1310 });
1311 }
1312
1313 fn resolve_sets(&mut self) -> Result<(), Trouble> {
1316 while !self.sets.is_empty() {
1317 let mut done = Vec::new();
1318 for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
1319 if let Ok(residue) = self.reduce(sum) {
1320 done.push((at, *sym, self.settled(&residue, *line)?));
1321 }
1322 }
1323 if done.is_empty() {
1324 let (sym, _, line) = &self.sets[0];
1325 let why = format!(
1326 "'{}' is set to something that is set to it, so neither has a value",
1327 self.syms[*sym].name
1328 );
1329 return Err(Trouble { line: *line, why });
1330 }
1331 for (_, sym, held) in &done {
1332 self.syms[*sym].at = *held;
1333 }
1334 for (at, _, _) in done.iter().rev() {
1336 self.sets.remove(*at);
1337 }
1338 }
1339 Ok(())
1340 }
1341
1342 fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
1344 match residue.left.as_slice() {
1345 [] => Ok(Held::Absolute(residue.constant as u64)),
1346 [Left { coeff: 1, at: Some((part, offset)), .. }] => {
1349 Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
1350 }
1351 _ => Err(Trouble {
1352 line,
1353 why: "a set to something that is neither a number nor a place in this file"
1354 .to_owned(),
1355 }),
1356 }
1357 }
1358
1359 fn resolve_sizes(&mut self) -> Result<(), Trouble> {
1361 for (sym, sum, line) in std::mem::take(&mut self.sizes) {
1362 let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
1363 if !residue.left.is_empty() {
1364 let why = format!(
1365 "the size of '{}' is not a number, and a size has to be one",
1366 self.syms[sym].name
1367 );
1368 return Err(Trouble { line, why });
1369 }
1370 let size = self.count(residue.constant).map_err(|_| Trouble {
1371 line,
1372 why: format!("'{}' is given a negative size", self.syms[sym].name),
1373 })?;
1374 self.syms[sym].size = size;
1375 }
1376 Ok(())
1377 }
1378
1379 fn too_far(&self) -> Result<Vec<usize>, Trouble> {
1399 let mut away = Vec::new();
1400 let mut jumps: Vec<(usize, i64, i64, usize)> = Vec::new();
1402 for fixup in &self.fixups {
1403 let Some(nth) = fixup.branch else { continue };
1404 let residue = self
1405 .reduce_kept(&fixup.sum, true)
1406 .map_err(|why| Trouble { line: fixup.line, why })?;
1407 if !residue.left.is_empty() {
1408 away.push(nth);
1409 } else {
1410 jumps.push((fixup.part, fixup.at as i64 + 1, residue.constant, nth));
1411 }
1412 }
1413 if !away.is_empty() {
1414 return Ok(away);
1415 }
1416 jumps.sort_unstable();
1417 let mut far = Vec::new();
1418 let mut jumps = jumps.into_iter().peekable();
1419 while let Some(&(part, ..)) = jumps.peek() {
1420 let aligns: Vec<Aligned> =
1421 self.aligns.iter().filter(|align| align.part == part).copied().collect();
1422 let mut aligns_left = aligns.iter().peekable();
1423 let mut stretch = 0i64;
1424 let mut moved: Vec<(i64, i64)> = Vec::new();
1426 while let Some(&(_, end, distance, nth)) = jumps.peek().filter(|jump| jump.0 == part) {
1427 jumps.next();
1428 while let Some(align) = aligns_left.next_if(|align| align.at as i64 <= end - 2) {
1429 let now =
1430 padding((align.at as i64 + stretch) as u64, align.boundary, align.most);
1431 stretch += now as i64 - align.need as i64;
1432 moved.push(((align.at + align.need) as i64, stretch));
1433 }
1434 let target = end + distance;
1435 let judged = if distance < 0 {
1436 let there = moved.iter().rev().find(|(from, _)| *from <= target);
1437 distance + there.map_or(0, |(_, by)| *by) - stretch
1438 } else if stretch > 0
1439 && aligns.iter().any(|align| {
1440 end <= align.at as i64 && (align.at + align.need) as i64 <= target
1441 })
1442 {
1443 distance - stretch
1444 } else {
1445 distance
1446 };
1447 if distance >= 0 && judged < -2 {
1451 continue;
1452 }
1453 if i8::try_from(judged).is_err() {
1454 far.push(nth);
1455 stretch += if self.parts[part].bytes[end as usize - 2] == 0xEB { 3 } else { 4 };
1456 moved.push((end, stretch));
1457 }
1458 }
1459 }
1460 Ok(far)
1461 }
1462
1463 fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1464 for fixup in std::mem::take(&mut self.fixups) {
1465 let line = fixup.line;
1466 let bad = |why: String| Trouble { line, why };
1467 if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1475 let [
1476 Term { coeff: 1, what: What::Symbol(name) },
1477 Term { coeff: -1, what: What::Here { at: end, .. } },
1478 ] = fixup.sum.terms.as_slice()
1479 else {
1480 return Err(bad(
1481 "a reach through the global offset table in something other than an \
1482 instruction, which is not an expression this compiler writes"
1483 .to_owned(),
1484 ));
1485 };
1486 let kind =
1487 if fixup.reach == Reach::Table { Reference::Got } else { Reference::Thread };
1488 self.parts[fixup.part].relocs.push(Reloc {
1489 at: fixup.at as usize,
1490 symbol: name.clone(),
1491 kind,
1492 addend: fixup.sum.constant + fixup.at as i64 - end,
1493 after: (end - fixup.at as i64 - 4).max(0) as u8,
1494 });
1495 continue;
1496 }
1497 let residue =
1498 self.reduce_kept(&fixup.sum, fixup.jump).map_err(|why| Trouble { line, why })?;
1499 if fixup.reach == Reach::Value && !residue.left.is_empty() {
1500 return Err(bad(
1501 "a number in an instruction that names something outside this section, \
1502 which wants a relocation this compiler does not write yet"
1503 .to_owned(),
1504 ));
1505 }
1506 let (symbol, kind, addend, after) = match residue.left.as_slice() {
1507 [] => {
1508 let width = fixup.width as usize;
1516 let room = 8 * width as u32;
1517 let low = -(1i64 << (room - 1));
1518 let high = if fixup.reach == Reach::Branch {
1519 (1i64 << (room - 1)) - 1
1520 } else {
1521 (1i64 << room) - 1
1522 };
1523 if width < 8 && (residue.constant < low || residue.constant > high) {
1524 return Err(bad(format!(
1525 "{} written into {width} bytes, which does not reach it",
1526 residue.constant
1527 )));
1528 }
1529 let bytes = residue.constant.to_le_bytes();
1530 let at = fixup.at as usize;
1531 let part = &mut self.parts[fixup.part];
1532 part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1533 continue;
1534 }
1535 [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1537 let kind = Reference::Address { bytes: fixup.width };
1538 (name.clone(), kind, residue.constant, 0)
1539 }
1540 [
1545 Left { coeff: 1, what: What::Symbol(name), .. },
1546 Left { coeff: -1, at: Some((part, offset)), .. },
1547 ]
1548 | [
1549 Left { coeff: -1, at: Some((part, offset)), .. },
1550 Left { coeff: 1, what: What::Symbol(name), .. },
1551 ] => {
1552 if *part != fixup.part {
1553 return Err(bad(
1554 "a distance that is subtracted from somewhere in another section"
1555 .to_owned(),
1556 ));
1557 }
1558 if fixup.width != 4 {
1559 return Err(bad(format!(
1560 "a distance written into {} bytes, and four is the only width a \
1561 relocation says one at",
1562 fixup.width
1563 )));
1564 }
1565 let addend = residue.constant + fixup.at as i64 - offset;
1572 let near = self.known.get(name).is_some_and(|&sym| {
1575 self.syms[sym].binding == Binding::Local
1576 && matches!(self.syms[sym].at, Held::In { .. })
1577 });
1578 let kind = if fixup.reach == Reach::Branch && !near {
1579 Reference::Call
1580 } else {
1581 Reference::Data
1582 };
1583 let after = (offset - fixup.at as i64 - 4).max(0);
1586 (name.clone(), kind, addend, after as u8)
1587 }
1588 [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1589 return Err(bad(
1590 "the address of these bytes themselves, which has no symbol to be \
1591 relocated against"
1592 .to_owned(),
1593 ));
1594 }
1595 _ => {
1596 return Err(bad(
1597 "an expression that does not come out as a number, an address, or a \
1598 distance, and those are what a relocation can say"
1599 .to_owned(),
1600 ));
1601 }
1602 };
1603 if let Some(&sym) = self.known.get(&symbol) {
1607 if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1608 self.relocated.insert(sym);
1609 } else if self.syms[sym].numbered {
1610 let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1611 return Err(bad(format!(
1612 "'{number}f' goes on to a '{number}:' and there is none below it"
1613 )));
1614 }
1615 }
1616 if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1617 return Err(bad(format!(
1618 "the address of '{symbol}' written into {} bytes, and this machine relocates \
1619 an address at four or eight",
1620 fixup.width
1621 )));
1622 }
1623 self.parts[fixup.part].relocs.push(Reloc {
1624 at: fixup.at as usize,
1625 symbol,
1626 kind,
1627 addend,
1628 after,
1629 });
1630 }
1631 Ok(())
1632 }
1633
1634 fn reduce_kept(&self, sum: &Sum, jump: bool) -> Result<Residue, String> {
1645 let mut named =
1646 sum.terms.iter().enumerate().filter(|(_, term)| matches!(term.what, What::Symbol(_)));
1647 let (Some((nth, Term { coeff: 1, what: What::Symbol(name) })), None) =
1648 (named.next(), named.next())
1649 else {
1650 return self.reduce(sum);
1651 };
1652 let kept = self.known.get(name).is_some_and(|&sym| {
1653 (self.syms[sym].binding == Binding::Weak
1654 || !jump && self.syms[sym].binding == Binding::Global)
1655 && matches!(self.syms[sym].at, Held::In { .. })
1656 });
1657 if !kept {
1658 return self.reduce(sum);
1659 }
1660 let mut rest = sum.clone();
1661 rest.terms.remove(nth);
1662 let mut residue = self.reduce(&rest)?;
1663 residue.left.push(Left { coeff: 1, what: What::Symbol(name.clone()), at: None });
1664 Ok(residue)
1665 }
1666
1667 fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
1676 let mut constant = sum.constant;
1677 let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
1678 let mut outside: Vec<(i64, String)> = Vec::new();
1679 for term in &sum.terms {
1680 match &term.what {
1681 What::Here { part, at } => {
1682 placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
1683 }
1684 What::Symbol(name) => {
1685 let Some(&at) = self.known.get(name) else {
1686 return Err(format!("'{name}' is named and never said"));
1687 };
1688 match self.syms[at].at {
1689 Held::Absolute(value) => constant += term.coeff * value as i64,
1690 Held::In { part, offset } => placed.entry(part).or_default().push((
1691 term.coeff,
1692 term.what.clone(),
1693 offset as i64,
1694 )),
1695 Held::Undefined | Held::Common { .. } => {
1698 if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
1699 outside.push((term.coeff, name.clone()));
1700 } else {
1701 return Err(format!("'{name}' is not worked out yet"));
1702 }
1703 }
1704 }
1705 }
1706 }
1707 }
1708 let mut left: Vec<Left> = Vec::new();
1709 for (part, terms) in placed {
1710 let (_, chosen, base) = terms[0].clone();
1711 let mut net = 0;
1712 for (coeff, _, offset) in &terms {
1713 net += coeff;
1714 constant += coeff * (offset - base);
1715 }
1716 if net != 0 {
1717 left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
1718 }
1719 }
1720 let mut together: BTreeMap<String, i64> = BTreeMap::new();
1721 for (coeff, name) in outside {
1722 *together.entry(name).or_default() += coeff;
1723 }
1724 for (name, coeff) in together {
1725 if coeff != 0 {
1726 left.push(Left { coeff, what: What::Symbol(name), at: None });
1727 }
1728 }
1729 Ok(Residue { constant, left })
1730 }
1731}
1732
1733#[derive(Debug, Clone)]
1735struct Residue {
1736 constant: i64,
1737 left: Vec<Left>,
1738}
1739
1740#[derive(Debug, Clone)]
1742struct Left {
1743 coeff: i64,
1745 what: What,
1747 at: Option<(usize, i64)>,
1750}
1751
1752#[derive(Debug, Clone, Default, PartialEq, Eq)]
1754struct Sum {
1755 constant: i64,
1756 terms: Vec<Term>,
1757}
1758
1759#[derive(Debug, Clone, PartialEq, Eq)]
1761struct Term {
1762 coeff: i64,
1763 what: What,
1764}
1765
1766#[derive(Debug, Clone, PartialEq, Eq)]
1768enum What {
1769 Symbol(String),
1771 Here { part: usize, at: i64 },
1774}
1775
1776impl Sum {
1777 fn flat(&self) -> Option<i64> {
1779 self.terms.is_empty().then_some(self.constant)
1780 }
1781
1782 fn of(what: What) -> Sum {
1784 Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1785 }
1786
1787 fn just(value: i64) -> Sum {
1789 Sum { constant: value, terms: Vec::new() }
1790 }
1791
1792 fn plus(mut self, other: Sum) -> Sum {
1794 self.constant = self.constant.wrapping_add(other.constant);
1795 self.terms.extend(other.terms);
1796 self
1797 }
1798
1799 fn minus(self) -> Sum {
1801 Sum {
1802 constant: self.constant.wrapping_neg(),
1803 terms: self
1804 .terms
1805 .into_iter()
1806 .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
1807 .collect(),
1808 }
1809 }
1810
1811 fn times(self, factor: i64) -> Sum {
1813 Sum {
1814 constant: self.constant.wrapping_mul(factor),
1815 terms: self
1816 .terms
1817 .into_iter()
1818 .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
1819 .collect(),
1820 }
1821 }
1822}
1823
1824struct Parser<'a> {
1826 text: &'a str,
1827 at: usize,
1828 here: (usize, i64),
1829}
1830
1831impl Parser<'_> {
1832 fn whole(&mut self) -> Result<Sum, String> {
1834 let sum = self.bitwise()?;
1835 self.space();
1836 if self.at < self.text.len() {
1837 return Err(format!(
1838 "'{}' is left over at the end of an expression",
1839 &self.text[self.at..]
1840 ));
1841 }
1842 Ok(sum)
1843 }
1844
1845 fn bitwise(&mut self) -> Result<Sum, String> {
1847 let mut left = self.shift()?;
1848 loop {
1849 self.space();
1850 let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
1851 let right = self.shift()?;
1852 left = self.arithmetic(left, right, op)?;
1853 }
1854 }
1855
1856 fn shift(&mut self) -> Result<Sum, String> {
1858 let mut left = self.sum()?;
1859 loop {
1860 self.space();
1861 let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
1862 let right = self.sum()?;
1863 left = self.arithmetic(left, right, op)?;
1864 }
1865 }
1866
1867 fn sum(&mut self) -> Result<Sum, String> {
1869 let mut left = self.product()?;
1870 loop {
1871 self.space();
1872 let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
1874 let right = self.product()?;
1875 left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
1876 }
1877 }
1878
1879 fn product(&mut self) -> Result<Sum, String> {
1881 let mut left = self.unary()?;
1882 loop {
1883 self.space();
1884 let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
1885 let right = self.unary()?;
1886 left = match (op, left.flat(), right.flat()) {
1890 ("*", _, Some(factor)) => left.times(factor),
1891 ("*", Some(factor), _) => right.times(factor),
1892 (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
1893 _ => return Err(format!("'{op}' of something that names a symbol")),
1894 };
1895 }
1896 }
1897
1898 fn unary(&mut self) -> Result<Sum, String> {
1900 self.space();
1901 if self.eat("-") {
1902 return Ok(self.unary()?.minus());
1903 }
1904 if self.eat("+") {
1905 return self.unary();
1906 }
1907 if self.eat("~") {
1908 let inner = self.unary()?;
1909 let value = inner
1910 .flat()
1911 .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
1912 return Ok(Sum::just(!value));
1913 }
1914 if self.eat("!") {
1915 let inner = self.unary()?;
1916 let value = inner
1917 .flat()
1918 .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
1919 return Ok(Sum::just(i64::from(value == 0)));
1920 }
1921 self.primary()
1922 }
1923
1924 fn primary(&mut self) -> Result<Sum, String> {
1926 self.space();
1927 let rest = &self.text[self.at..];
1928 if rest.is_empty() {
1929 return Err("an expression that stops before it says anything".to_owned());
1930 }
1931 if self.eat("(") {
1932 let inner = self.bitwise()?;
1933 self.space();
1934 if !self.eat(")") {
1935 return Err("a bracket that was opened and never closed".to_owned());
1936 }
1937 return Ok(inner);
1938 }
1939 let first = rest.as_bytes()[0];
1940 if first == b'\'' {
1941 return self.character();
1942 }
1943 if first.is_ascii_digit() {
1944 let end = rest.find(|ch: char| !ch.is_ascii_digit()).unwrap_or(rest.len());
1948 let bytes = rest.as_bytes();
1949 if matches!(bytes.get(end), Some(b'b' | b'f'))
1950 && !bytes.get(end + 1).is_some_and(|byte| carries_on(*byte))
1951 {
1952 self.at += end + 1;
1953 return Ok(Sum::of(What::Symbol(rest[..=end].to_owned())));
1954 }
1955 return self.digits();
1956 }
1957 if starts(first) {
1958 let name = self.word();
1959 if name == "." {
1961 let (part, at) = self.here;
1962 return Ok(Sum::of(What::Here { part, at }));
1963 }
1964 if self.text[self.at..].starts_with('@') {
1968 return Err(format!(
1969 "'{name}@' asks for a relocation only an instruction can carry"
1970 ));
1971 }
1972 return Ok(Sum::of(What::Symbol(name)));
1973 }
1974 Err(format!("'{rest}' is not the start of an expression"))
1975 }
1976
1977 fn digits(&mut self) -> Result<Sum, String> {
1979 let rest = &self.text[self.at..];
1980 let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
1981 (16, 2)
1982 } else if rest.starts_with("0b") || rest.starts_with("0B") {
1983 (2, 2)
1984 } else if rest.len() > 1 && rest.starts_with('0') {
1985 (8, 1)
1986 } else {
1987 (10, 0)
1988 };
1989 let body = &rest[skip..];
1990 let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
1991 if end == 0 {
1992 return Err(format!("'{rest}' starts like a number and is not one"));
1993 }
1994 let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
1995 let value = u64::from_str_radix(&text, radix)
1998 .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
1999 self.at += skip + end;
2000 while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
2003 self.at += 1;
2004 }
2005 Ok(Sum::just(value as i64))
2006 }
2007
2008 fn character(&mut self) -> Result<Sum, String> {
2010 self.at += 1;
2011 let rest = &self.text[self.at..];
2012 let mut chars = rest.chars();
2013 let Some(first) = chars.next() else {
2014 return Err("a quote with no character after it".to_owned());
2015 };
2016 let (value, used) = if first == '\\' {
2017 let (value, used) = escape(&rest[1..])?;
2018 (value, used + 1)
2019 } else {
2020 (first as u8, first.len_utf8())
2021 };
2022 self.at += used;
2023 if self.text[self.at..].starts_with('\'') {
2026 self.at += 1;
2027 }
2028 Ok(Sum::just(i64::from(value)))
2029 }
2030
2031 fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
2033 let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
2034 return Err(format!("'{op}' of something that names a symbol"));
2035 };
2036 Ok(Sum::just(self.arithmetic_number(a, b, op)?))
2037 }
2038
2039 fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
2041 Ok(match op {
2042 "|" => a | b,
2043 "^" => a ^ b,
2044 "&" => a & b,
2045 "<<" => a.wrapping_shl(shift(b)?),
2046 ">>" => a.wrapping_shr(shift(b)?),
2047 "*" => a.wrapping_mul(b),
2048 "/" if b == 0 => return Err("a division by zero".to_owned()),
2049 "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
2050 "/" => a.wrapping_div(b),
2051 "%" => a.wrapping_rem(b),
2052 _ => return Err(format!("'{op}' is not an operator this compiler knows")),
2053 })
2054 }
2055
2056 fn word(&mut self) -> String {
2058 let body = &self.text[self.at..];
2059 let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
2060 let word = body[..end].to_owned();
2061 self.at += end;
2062 word
2063 }
2064
2065 fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
2070 for op in ops {
2071 if self.text[self.at..].starts_with(op) {
2072 self.at += op.len();
2073 return Some(op);
2074 }
2075 }
2076 None
2077 }
2078
2079 fn eat(&mut self, what: &str) -> bool {
2081 if self.text[self.at..].starts_with(what) {
2082 self.at += what.len();
2083 return true;
2084 }
2085 false
2086 }
2087
2088 fn space(&mut self) {
2090 while self.text[self.at..].starts_with([' ', '\t']) {
2091 self.at += 1;
2092 }
2093 }
2094}
2095
2096impl Reader {
2097 fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
2099 let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
2100 let body = text
2101 .strip_prefix('"')
2102 .and_then(|rest| rest.strip_suffix('"'))
2103 .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
2104 let mut out = Vec::with_capacity(body.len());
2105 let mut at = 0;
2106 while at < body.len() {
2107 let rest = &body[at..];
2108 let first = rest.as_bytes()[0];
2109 if first == b'\\' {
2110 let (value, used) =
2111 escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
2112 out.push(value);
2113 at += used + 1;
2114 continue;
2115 }
2116 let ch = rest.chars().next().unwrap_or('\0');
2117 let mut buffer = [0u8; 4];
2118 out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
2119 at += ch.len_utf8();
2120 }
2121 Ok(out)
2122 }
2123}
2124
2125fn shift(by: i64) -> Result<u32, String> {
2127 u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
2128}
2129
2130fn escape(rest: &str) -> Result<(u8, usize), String> {
2134 let bytes = rest.as_bytes();
2135 let Some(&first) = bytes.first() else {
2136 return Err("a backslash with nothing after it".to_owned());
2137 };
2138 let simple = match first {
2139 b'n' => Some(b'\n'),
2140 b't' => Some(b'\t'),
2141 b'r' => Some(b'\r'),
2142 b'f' => Some(0x0c),
2143 b'b' => Some(0x08),
2144 b'v' => Some(0x0b),
2145 b'a' => Some(0x07),
2146 b'e' => Some(0x1b),
2147 b'\\' => Some(b'\\'),
2148 b'"' => Some(b'"'),
2149 b'\'' => Some(b'\''),
2150 _ => None,
2151 };
2152 if let Some(value) = simple {
2153 return Ok((value, 1));
2154 }
2155 if first == b'x' || first == b'X' {
2156 let end = bytes[1..]
2157 .iter()
2158 .position(|byte| !byte.is_ascii_hexdigit())
2159 .map_or(bytes.len(), |at| at + 1);
2160 if end == 1 {
2161 return Err("a hex escape with no digits in it".to_owned());
2162 }
2163 let text = &rest[1..end];
2166 let text = &text[text.len().saturating_sub(2)..];
2167 let value =
2168 u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
2169 return Ok((value, end));
2170 }
2171 if (b'0'..=b'7').contains(&first) {
2172 let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
2173 let value = u32::from_str_radix(&rest[..end], 8)
2174 .map_err(|_| "an octal escape that is not one".to_owned())?;
2175 return Ok(((value & 0xff) as u8, end));
2176 }
2177 Err(format!("'\\{}' is not an escape this compiler knows", first as char))
2181}
2182
2183fn labelled(text: &str) -> Option<String> {
2190 let bytes = text.as_bytes();
2191 if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
2192 return None;
2193 }
2194 let end = text.find(|ch: char| !carries_on(ch as u8))?;
2195 if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
2197 return None;
2198 }
2199 Some(text[..end].to_owned())
2200}
2201
2202fn assigned(text: &str) -> Option<(&str, &str)> {
2206 let bytes = text.as_bytes();
2207 if bytes.is_empty() || !starts(bytes[0]) {
2208 return None;
2209 }
2210 let end = text.find(|ch: char| !carries_on(ch as u8)).unwrap_or(text.len());
2211 let rest = text[end..].trim_start().strip_prefix('=')?;
2212 if rest.starts_with('=') {
2213 return None;
2214 }
2215 Some((&text[..end], rest.trim()))
2216}
2217
2218fn starts(byte: u8) -> bool {
2220 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
2221}
2222
2223fn carries_on(byte: u8) -> bool {
2225 starts(byte) || byte.is_ascii_digit()
2226}
2227
2228fn padding(at: u64, boundary: u64, most: Option<u64>) -> u64 {
2236 let over = at % boundary;
2237 let need = if over == 0 { 0 } else { boundary - over };
2238 if most.is_some_and(|most| need > most) { 0 } else { need }
2239}
2240
2241fn counted(number: &str, nth: usize) -> String {
2242 format!("{number}\u{1}{nth}")
2243}
2244
2245fn unquoted(text: &str) -> String {
2247 text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
2248}
2249
2250pub(crate) fn split(text: &str, on: char) -> Vec<String> {
2255 let mut out = Vec::new();
2256 let mut piece = String::new();
2257 let mut depth = 0i32;
2258 let mut quote = None;
2259 let mut chars = text.chars();
2260 while let Some(ch) = chars.next() {
2261 if let Some(mark) = quote {
2262 piece.push(ch);
2263 if ch == '\\' {
2264 if let Some(next) = chars.next() {
2265 piece.push(next);
2266 }
2267 continue;
2268 }
2269 if ch == mark {
2270 quote = None;
2271 }
2272 continue;
2273 }
2274 match ch {
2275 '"' => {
2276 quote = Some(ch);
2277 piece.push(ch);
2278 }
2279 '(' => {
2280 depth += 1;
2281 piece.push(ch);
2282 }
2283 ')' => {
2284 depth -= 1;
2285 piece.push(ch);
2286 }
2287 _ if ch == on && depth == 0 => {
2288 out.push(std::mem::take(&mut piece));
2289 }
2290 _ => piece.push(ch),
2291 }
2292 }
2293 if !piece.trim().is_empty() || !out.is_empty() {
2294 out.push(piece);
2295 }
2296 out.into_iter().map(|piece| piece.trim().to_owned()).collect()
2297}
2298
2299fn repeated<'a>(word: &str, rest: &'a str) -> Option<(String, &'a str)> {
2310 let (next, after) = match rest.find(char::is_whitespace) {
2311 Some(cut) => (&rest[..cut], rest[cut..].trim()),
2312 None => (rest, ""),
2313 };
2314 if word == "notrack" {
2315 return match next {
2316 "jmp" | "jmpq" => Some(("notrack jmp".to_owned(), after)),
2317 "call" | "callq" => Some(("notrack call".to_owned(), after)),
2318 _ => None,
2319 };
2320 }
2321 let unequal = match word {
2322 "rep" | "repe" | "repz" => false,
2323 "repne" | "repnz" => true,
2324 _ => return None,
2325 };
2326 let string = next.len() == 5 && next.ends_with(['b', 'w', 'l', 'q']);
2327 let which = if string { &next[..4] } else { "" };
2328 let prefix = match (unequal, which) {
2329 (false, "movs" | "stos") => "rep",
2330 (false, "scas" | "cmps") => "repe",
2331 (true, "scas" | "cmps") => "repne",
2332 _ => return None,
2333 };
2334 Some((format!("{prefix} {next}"), after))
2335}
2336
2337#[cfg(test)]
2338mod tests {
2339 use super::*;
2340
2341 use rucc_object::Reference;
2342
2343 fn assembled(text: &str) -> Assembled {
2345 match read(text) {
2346 Ok(assembled) => assembled,
2347 Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2348 }
2349 }
2350
2351 fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
2353 let part = assembled
2354 .parts
2355 .iter()
2356 .find(|part| part.name == name)
2357 .unwrap_or_else(|| panic!("there is no section called '{name}'"));
2358 part.bytes.clone()
2359 }
2360
2361 fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
2363 assembled
2364 .names
2365 .iter()
2366 .find(|name| name.name == want)
2367 .unwrap_or_else(|| panic!("there is no name called '{want}'"))
2368 }
2369
2370 fn refused(text: &str) -> Trouble {
2372 read(text).err().unwrap_or_else(|| panic!("this was read and should not have been"))
2373 }
2374
2375 #[test]
2376 fn a_repeat_prefix_is_read_with_the_string_instruction_behind_it() {
2377 let assembled =
2378 assembled("\t.text\n\trep movsl\n\trepnz scasb\n\trepz cmpsb\n\trep stosq\n");
2379 assert_eq!(
2380 bytes(&assembled, ".text"),
2381 [0xF3, 0xA5, 0xF2, 0xAE, 0xF3, 0xA6, 0xF3, 0x48, 0xAB]
2382 );
2383 }
2384
2385 #[test]
2386 fn notrack_is_read_with_the_jump_behind_it() {
2387 let assembled = assembled("\t.text\n\tnotrack jmp\t*%rax\n\tnotrack jmp *%r8\n\tleave\n");
2388 assert_eq!(bytes(&assembled, ".text"), [0x3E, 0xFF, 0xE0, 0x3E, 0x41, 0xFF, 0xE0, 0xC9]);
2389 }
2390
2391 #[test]
2397 fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
2398 let out =
2399 assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
2400 let text = bytes(&out, ".text");
2401 assert_eq!(text, vec![0x90, 0xeb, 0xfd, 0x90, 0xeb, 0x01, 0x90, 0xc3]);
2404 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2405 let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2407 assert_eq!(written, vec!["foo"]);
2408 }
2409
2410 #[test]
2411 fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
2412 let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
2413 assert!(back.why.contains("none above it"), "{}", back.why);
2414 let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
2415 assert!(forward.why.contains("none below it"), "{}", forward.why);
2416 }
2417
2418 #[test]
2425 fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
2426 let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
2427 assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
2428 let split = assembled("\t.text\n\trep\n\tmovsq\n");
2429 assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
2430 let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
2431 assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
2432 }
2433
2434 #[test]
2441 fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
2442 let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
2443 let relocs = &out.parts[0].relocs;
2444 assert_eq!(relocs.len(), 1);
2445 assert_eq!(relocs[0].symbol, "table");
2446 assert_eq!(relocs[0].kind, Reference::Got);
2447 assert_eq!(relocs[0].addend, -4);
2450 let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
2451 assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
2452 }
2453
2454 #[test]
2460 fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
2461 let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
2462 let relocs = &out.parts[0].relocs;
2463 assert_eq!(relocs.len(), 1);
2464 assert_eq!(relocs[0].symbol, "table");
2465 assert_eq!(relocs[0].addend, -516);
2466 let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2469 assert_eq!(named, ["table"]);
2470 }
2471
2472 #[test]
2473 fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
2474 refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
2477 }
2478
2479 #[test]
2480 fn the_probe_gmp_writes() {
2481 let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
2485 assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
2486 let foo = name(&out, "foo");
2487 assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
2488 assert_eq!(foo.binding, Binding::Global);
2489 }
2490
2491 #[test]
2492 fn every_width_of_number_is_the_bytes_it_says_it_is() {
2493 let out = assembled(
2494 "\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",
2495 );
2496 let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
2497 want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
2498 assert_eq!(bytes(&out, ".data"), want);
2499 }
2500
2501 #[test]
2502 fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
2503 let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
2506 assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
2507 }
2508
2509 #[test]
2510 fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
2511 let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
2512 assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
2513 }
2514
2515 #[test]
2516 fn space_and_fill_put_that_many_bytes_there() {
2517 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");
2518 assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
2519 }
2520
2521 #[test]
2522 fn aligning_moves_on_to_the_boundary_and_no_further() {
2523 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");
2526 let data = bytes(&out, ".data");
2527 assert_eq!(data.len(), 17);
2528 assert_eq!(data[0], 1);
2529 assert_eq!(data[8], 2);
2530 assert_eq!(data[16], 3);
2531 assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
2532 }
2533
2534 #[test]
2535 fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
2536 let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
2537 let part = &out.parts[0];
2538 assert_eq!(part.name, ".bss");
2539 assert_eq!(part.size, 4096);
2540 assert!(part.bytes.is_empty(), "the zeroes were carried after all");
2541 assert!(!part.shape.bits);
2542 }
2543
2544 #[test]
2545 fn what_a_section_directive_said_about_a_section_is_what_it_is() {
2546 let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
2547 let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
2548 assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
2549 assert!(!part.shape.write, "nothing said it was writable");
2550 }
2551
2552 #[test]
2553 fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
2554 let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
2555 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2556 assert_eq!(bytes(&out, ".text"), vec![0x90]);
2557 }
2558
2559 #[test]
2560 fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
2561 let out = assembled(
2562 "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
2563 );
2564 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2565 assert_eq!(bytes(&out, ".rodata"), vec![9]);
2566 }
2567
2568 #[test]
2569 fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
2570 let out = assembled(
2573 "\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",
2574 );
2575 let f = name(&out, "f");
2576 assert_eq!(f.size, 5);
2577 assert_eq!(f.sort, Sort::Func);
2578 }
2579
2580 #[test]
2581 fn a_set_may_name_something_further_down_the_file() {
2582 let out = assembled(
2585 "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
2586 table_end - table\n",
2587 );
2588 assert_eq!(name(&out, "width").at, Held::Absolute(12));
2589 }
2590
2591 #[test]
2592 fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
2593 let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
2594 assert_eq!(name(&out, "a").at, Held::Absolute(11));
2595 assert_eq!(name(&out, "b").at, Held::Absolute(10));
2596 }
2597
2598 #[test]
2599 fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
2600 let why = refused("\t.set a, b\n\t.set b, a\n");
2601 assert!(why.why.contains("neither has a value"), "{why}");
2602 }
2603
2604 #[test]
2605 fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
2606 let out = assembled("\t.data\n\t.quad message\n");
2607 let reloc = &out.parts[0].relocs[0];
2608 assert_eq!(reloc.at, 0);
2609 assert_eq!(reloc.symbol, "message");
2610 assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
2611 assert_eq!(reloc.addend, 0);
2612 assert_eq!(name(&out, "message").at, Held::Undefined);
2613 }
2614
2615 #[test]
2616 fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
2617 let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
2620 let reloc = &out.parts[0].relocs[0];
2621 assert_eq!(reloc.at, 8);
2622 assert_eq!(reloc.symbol, "message");
2623 assert_eq!(reloc.kind, Reference::Data);
2624 assert_eq!(reloc.addend, 0);
2625 }
2626
2627 #[test]
2628 fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
2629 let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
2634 let reloc = &out.parts[0].relocs[0];
2635 assert_eq!(reloc.at, 8);
2636 assert_eq!(reloc.kind, Reference::Data);
2637 assert_eq!(reloc.addend, 8);
2638 }
2639
2640 #[test]
2641 fn a_number_added_to_a_name_rides_along_in_the_addend() {
2642 let out = assembled("\t.data\n\t.quad message + 16\n");
2643 assert_eq!(out.parts[0].relocs[0].addend, 16);
2644 }
2645
2646 #[test]
2647 fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
2648 let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
2649 assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
2650 assert_eq!(name(&out, "shared").binding, Binding::Global);
2651 assert_eq!(name(&out, "mine").binding, Binding::Local);
2654 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2655 }
2656
2657 #[test]
2658 fn comm_of_a_name_said_to_be_local_is_room_here_as_lcomm_is() {
2659 let out = assembled("\t.local mine\n\t.comm mine, 8, 8\n");
2660 assert_eq!(name(&out, "mine").binding, Binding::Local);
2661 assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2662 }
2663
2664 #[test]
2665 fn what_a_file_says_about_who_can_see_a_name_is_kept() {
2666 let out = assembled(
2667 "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
2668 inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
2669 );
2670 assert_eq!(name(&out, "seen").binding, Binding::Global);
2671 assert_eq!(name(&out, "maybe").binding, Binding::Weak);
2672 assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
2673 }
2674
2675 #[test]
2676 fn the_name_of_the_file_is_a_symbol_of_its_own() {
2677 let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
2680 assert_eq!(out.names[0].name, "big.s");
2681 assert_eq!(out.names[0].sort, Sort::File);
2682 assert_eq!(out.names[0].binding, Binding::Local);
2683 assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
2684 }
2685
2686 #[test]
2687 fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
2688 let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
2691 assert!(out.names.is_empty(), "{:?}", out.names);
2692 }
2693
2694 #[test]
2695 fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
2696 let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\tpopcnt %rax, %rdx\n\tret\n");
2700 assert_eq!(why.line, 4);
2701 assert!(why.why.contains("popcnt"), "{why}");
2702 }
2703
2704 #[test]
2705 fn a_function_of_instructions_is_its_bytes_and_its_size() {
2706 let out = assembled(
2709 "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
2710 id, .-id\n",
2711 );
2712 assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
2713 assert_eq!(name(&out, "id").size, 4);
2714 assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
2715 }
2716
2717 #[test]
2718 fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
2719 let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
2723 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0, 0xc3]);
2724 assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2725 }
2726
2727 #[test]
2728 fn a_jump_backwards_is_the_negative_distance_to_it() {
2729 let out = assembled("\t.text\nagain:\n\tjmp again\n");
2730 assert_eq!(bytes(&out, ".text"), vec![0xeb, 0xfe]);
2731 }
2732
2733 #[test]
2734 fn a_branch_is_as_short_as_the_distance_lets_it_be() {
2735 let out = assembled("\tjne far\n\t.zero 127\nfar:\n\tret\n");
2738 assert_eq!(bytes(&out, ".text")[..2], [0x75, 127]);
2739 let out = assembled("\tjne far\n\t.zero 128\nfar:\n\tret\n");
2740 assert_eq!(bytes(&out, ".text")[..6], [0x0f, 0x85, 128, 0, 0, 0]);
2741 let out = assembled("back:\n\t.zero 126\n\tjmp back\n");
2742 assert_eq!(bytes(&out, ".text")[126..], [0xeb, 0x80]);
2743 let out = assembled("back:\n\t.zero 127\n\tjmp back\n");
2744 assert_eq!(bytes(&out, ".text")[127..], [0xe9, 0x7c, 0xff, 0xff, 0xff]);
2745 }
2746
2747 #[test]
2748 fn a_branch_made_long_can_push_another_one_out_of_reach() {
2749 let out = assembled("\tjmp a\n\t.zero 125\n\tjmp b\na:\n\t.zero 128\nb:\n\tret\n");
2753 let text = bytes(&out, ".text");
2754 assert_eq!(text[..5], [0xe9, 130, 0, 0, 0]);
2755 assert_eq!(text[130..135], [0xe9, 128, 0, 0, 0]);
2756 }
2757
2758 #[test]
2759 fn a_jump_past_an_alignment_is_judged_the_way_gas_judges_it() {
2760 let out = assembled(
2767 "\tjmp far1\n\tje far1\n\tje far2\n\t.zero 123\n\t.p2align 3\nfar2:\n\tret\n\t.zero \
2768 200\nfar1:\n\tret\n",
2769 );
2770 let text = bytes(&out, ".text");
2771 assert_eq!(text[..13], [0xe9, 0x4c, 1, 0, 0, 0x0f, 0x84, 0x46, 1, 0, 0, 0x74, 123]);
2772 assert_eq!(text.len(), 0x152);
2773 }
2774
2775 #[test]
2776 fn a_branch_that_leaves_the_section_or_goes_to_a_weak_name_is_long() {
2777 let out = assembled("\tjmp elsewhere\n\tjz maybe\n\t.weak maybe\nmaybe:\n\tret\n");
2779 assert_eq!(bytes(&out, ".text")[..1], [0xe9]);
2780 assert_eq!(bytes(&out, ".text")[5..7], [0x0f, 0x84]);
2781 }
2782
2783 #[test]
2784 fn a_section_of_constants_says_how_long_each_one_is() {
2785 let out = assembled(
2786 "\t.section .rodata.str1.1,\"aMS\",@progbits,1\n\t.string \"hi\"\n\t\
2787 .section .rodata.cst8,\"aM\",@progbits,8\n\t.quad 1\n\t.section .rodata.x,\"aM\"\n\t.byte 1\n",
2788 );
2789 let shapes: Vec<_> =
2790 out.parts.iter().map(|part| (part.shape.merge, part.shape.strings)).collect();
2791 assert_eq!(shapes, [(1, true), (8, false), (0, false)]);
2792 }
2793
2794 #[test]
2795 fn a_global_name_defined_here_is_still_left_to_the_linker() {
2796 let out = assembled(
2800 "\t.globl f\nf:\n\tcall f\n\tjmp f\n\tleaq f(%rip), %rax\n\tcall g\n\t\
2801 .long f - g\ng:\n\tret\n",
2802 );
2803 let relocs = &out.parts[0].relocs;
2804 let kinds: Vec<_> = relocs.iter().map(|r| (r.at, r.symbol.as_str(), r.kind)).collect();
2805 assert_eq!(kinds, [(1, "f", Reference::Call), (10, "f", Reference::Data)]);
2806 assert!(relocs.iter().all(|r| r.addend == -4));
2807 let text = bytes(&out, ".text");
2808 assert_eq!(text[..7], [0xe8, 0, 0, 0, 0, 0xeb, 0xf9]);
2809 assert_eq!(text[14..19], [0xe8, 4, 0, 0, 0]);
2810 assert_eq!(text[19..23], (-23i32).to_le_bytes());
2811 }
2812
2813 #[test]
2814 fn a_call_to_a_static_name_in_another_section_needs_no_stub() {
2815 let out = assembled("\t.text\n\tcall cold\n\t.section .text.unlikely\ncold:\n\tret\n");
2816 let reloc = &out.parts[0].relocs[0];
2817 assert_eq!((reloc.symbol.as_str(), reloc.kind), ("cold", Reference::Data));
2818 }
2819
2820 #[test]
2821 fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
2822 let out = assembled("\t.text\n\tcall puts\n");
2828 let reloc = &out.parts[0].relocs[0];
2829 assert_eq!(reloc.at, 1);
2830 assert_eq!(reloc.symbol, "puts");
2831 assert_eq!(reloc.kind, Reference::Call);
2832 assert_eq!(reloc.addend, -4);
2833 }
2834
2835 #[test]
2836 fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
2837 let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
2838 let reloc = &out.parts[0].relocs[0];
2839 assert_eq!(reloc.symbol, "message");
2840 assert_eq!(reloc.kind, Reference::Data);
2841 assert_eq!(reloc.at, 3);
2843 assert_eq!(reloc.addend, -4);
2844 }
2845
2846 #[test]
2847 fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
2848 let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
2851 assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
2852 }
2853
2854 #[test]
2855 fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
2856 let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
2860 assert_eq!(why.line, 2);
2861 assert!(why.why.contains("does not reach"), "{why}");
2862 }
2863
2864 #[test]
2865 fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
2866 let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
2871 assert_eq!(bytes(&out, ".data")[200], 200);
2872 let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
2873 assert!(why.why.contains("does not reach"), "{why}");
2874 }
2875
2876 #[test]
2877 fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
2878 let why = refused("\t.bss\n\tret\n");
2879 assert!(why.why.contains("holds no bytes"), "{why}");
2880 }
2881
2882 #[test]
2883 fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
2884 let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
2885 assert_eq!(why.line, 3);
2886 assert!(why.why.contains(".reloc"), "{why}");
2887 }
2888
2889 #[test]
2890 fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
2891 let out = assembled(
2894 "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
2895 3\n",
2896 );
2897 assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
2898 }
2899
2900 #[test]
2901 fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
2902 let why = refused("\t.data\n\t/* and then nothing\n");
2903 assert!(why.why.contains("never closed"), "{why}");
2904 }
2905
2906 #[test]
2907 fn a_string_with_a_comment_character_in_it_is_a_string() {
2908 let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
2909 assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
2910 }
2911
2912 #[test]
2913 fn several_statements_on_one_line_are_several_statements() {
2914 let out = assembled("\t.data; .byte 1; .byte 2\n");
2915 assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2916 }
2917
2918 #[test]
2919 fn a_section_nothing_was_ever_put_in_is_dropped() {
2920 let out = assembled("\t.data\n\t.byte 1\n");
2923 assert_eq!(out.parts.len(), 1);
2924 assert_eq!(out.parts[0].name, ".data");
2925 }
2926
2927 #[test]
2928 fn a_section_with_nothing_in_it_but_a_name_is_kept() {
2929 let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
2932 assert_eq!(out.parts.len(), 1);
2933 assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
2934 }
2935
2936 #[test]
2937 fn an_error_directive_is_the_file_saying_it_refuses_itself() {
2938 let why = refused("\t.error \"this is not the machine for it\"\n");
2939 assert!(why.why.contains("not the machine for it"), "{why}");
2940 }
2941
2942 #[test]
2943 fn a_jump_counted_from_itself_is_the_short_one_gas_writes() {
2944 let out = assembled("\tjmp .+6\n\t.int 123\n\tmov .-4(%rip), %eax\n");
2947 assert_eq!(
2948 bytes(&out, ".text"),
2949 vec![0xeb, 0x04, 123, 0, 0, 0, 0x8b, 0x05, 0xf6, 0xff, 0xff, 0xff]
2950 );
2951 }
2952
2953 #[test]
2954 fn a_numbered_label_in_an_expression_is_a_place() {
2955 let out =
2956 assembled("2:\n\tjmp .+6\n1:\n\t.pushsection .data\n\t.long 1b - 2b\n\t.popsection\n");
2957 assert_eq!(bytes(&out, ".data"), vec![2, 0, 0, 0]);
2958 let out = assembled("\t.data\n\t.byte 0b101\n");
2960 assert_eq!(bytes(&out, ".data"), vec![5]);
2961 }
2962
2963 #[test]
2964 fn a_number_an_instruction_carries_may_be_an_expression_over_labels() {
2965 let out = assembled("3:\tmov $4f-3b, %eax\n4:\n");
2966 assert_eq!(bytes(&out, ".text"), vec![0xb8, 5, 0, 0, 0]);
2967 }
2968
2969 #[test]
2970 fn a_number_an_instruction_carries_may_not_name_something_elsewhere() {
2971 let why = refused("\tmov $elsewhere, %eax\n");
2972 assert!(why.why.contains("relocation"), "{why}");
2973 }
2974
2975 #[test]
2976 fn a_name_set_twice_means_what_it_was_where_it_is_used() {
2977 let out = assembled(
2978 "\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",
2979 );
2980 assert_eq!(bytes(&out, ".data"), vec![3, 1, 2]);
2981 }
2982
2983 #[test]
2984 fn a_place_set_twice_and_reached_from_another_section_is_relocated_against() {
2985 let out = assembled(
2986 "\t.data\n\tx = .\n\t.int 1\n\tx = .\n\t.int 2\n\t.text\n\tmov x(%rip), %eax\n",
2987 );
2988 let reloc = &out.parts.iter().find(|part| part.name == ".text").unwrap().relocs[0];
2989 let target = name(&out, &reloc.symbol);
2990 let data = out.parts.iter().position(|part| part.name == ".data").unwrap();
2991 assert_eq!(target.at, Held::In { part: data, offset: 4 });
2992 }
2993
2994 #[test]
2995 fn frame_rules_are_an_unwind_table_pointing_at_the_function() {
2996 let out = assembled(
2997 "f:\n\t.cfi_startproc\n\tpush %rbp\n\t.cfi_def_cfa_offset 16\n\t.cfi_offset %rbp, \
2998 -16\n\tpop %rbp\n\t.cfi_def_cfa_offset 8\n\tret\n\t.cfi_endproc\n",
2999 );
3000 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3001 let rows = [0x41, 0x0e, 0x10, 0x86, 0x02, 0x41, 0x0e, 0x08];
3004 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3005 let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
3006 let text = out.parts.iter().position(|part| part.name == ".text").unwrap();
3007 assert_eq!(name(&out, &reloc.symbol).at, Held::In { part: text, offset: 0 });
3008 }
3009
3010 #[test]
3011 fn a_frame_rule_relative_to_the_register_is_the_same_slot() {
3012 let out = assembled(
3013 "\t.cfi_startproc\n\tpush %rbx\n\t.cfi_adjust_cfa_offset 8\n\t.cfi_rel_offset \
3014 %rbx, 0\n\t.cfi_endproc\n",
3015 );
3016 let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
3017 let rows = [0x41, 0x0e, 0x10, 0x83, 0x02];
3018 assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
3019 }
3020
3021 #[test]
3022 fn frame_rules_for_a_debugger_only_are_no_unwind_table() {
3023 let out =
3024 assembled("\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\tret\n\t.cfi_endproc\n");
3025 assert!(out.parts.iter().all(|part| part.name != ".eh_frame"));
3026 }
3027
3028 #[test]
3029 fn a_frame_rule_outside_a_function_or_a_function_never_ended_is_refused() {
3030 let why = refused("\t.cfi_def_cfa_offset 16\n");
3031 assert!(why.why.contains("outside"), "{why}");
3032 let why = refused("\t.cfi_startproc\n\tret\n");
3033 assert!(why.why.contains("never ended"), "{why}");
3034 }
3035}