Skip to main content

rucc_asm/
source.rs

1//! Reading a file of assembly.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.1, which asks for a real assembler with a real
4//! directive set rather than a call out to `as`.
5//!
6//! # What is here and what is not
7//!
8//! The directives, the labels and the expressions. The instructions are [`crate::instruction`],
9//! which this hands each line that is one and which hands back the bytes of it and the places in
10//! those bytes that name something. The names are the reason the split falls there: what an
11//! instruction is is a question about one line, and what it refers to is a question about the
12//! whole file, because the label a jump goes to is usually further down than the jump is.
13//!
14//! A mnemonic with no bytes behind it is refused by name with its line number, and so is an
15//! operand this cannot read. Guessing at either is the failure mode that matters here: an
16//! assembler that skipped what it did not recognise would write an object that links, and what
17//! would be wrong with it is a run of missing bytes in the middle of a function, which nothing
18//! finds until the program runs.
19//!
20//! # Why expressions are worth this much of the file
21//!
22//! Because `.size foo, .-foo` is on the end of nearly every function gas ever wrote, and because a
23//! table of addresses is `.quad` of a name. An expression here is kept as a constant plus a list of
24//! names with coefficients, rather than collapsed to a number as it is parsed, for two reasons. A
25//! name may not be defined yet when it is used, so nothing can be collapsed until the whole file has
26//! been read. And two names in the same section have a difference even when neither has an address,
27//! which is the whole of what `.-foo` is asking, so the pair has to survive as a pair to be
28//! subtracted at the end. What is left over after the subtractions is what the linker is asked
29//! about, and the shape of what is left is what says which relocation it is.
30
31use std::collections::{BTreeMap, HashMap};
32
33use rucc_object::{
34    Array, Assembled, Binding, Held, Name, Part, Reference, Reloc, Shape, Sort, Visibility,
35};
36
37/// What an instruction says about the place in it that names something, under a name that does not
38/// collide with the [`Sort`] an ELF symbol has.
39use crate::instruction::Sort as Reach;
40
41/// A file this could not read, and where in it.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Trouble {
44    /// Which line, counting from one, so that it can be put in front of a message the way every
45    /// other diagnostic in this compiler is.
46    pub line: usize,
47    /// What was wrong with it, already formatted and without the line number in it.
48    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
59/// What a file of assembly says, as the sections and names an object file is written from.
60///
61/// # Errors
62///
63/// [`Trouble`] for a directive this does not know, an instruction it has no bytes for, an operand
64/// it cannot read, an expression that does not reduce to something a relocation can say, or a file
65/// that is malformed. Every one of them carries the line it was on.
66pub fn read(text: &str) -> Result<Assembled, Trouble> {
67    let mut reader = Reader::default();
68    reader.run(text)?;
69    reader.finish()
70}
71
72/// One name, while the file is still being read.
73///
74/// Held apart from [`Name`] because two of its fields are not answers yet. A `.set` is an expression
75/// that may name something further down the file, and so is the second operand of `.size`, and both
76/// have to wait for the end.
77#[derive(Debug, Clone)]
78struct Sym {
79    name: String,
80    at: Held,
81    size: u64,
82    sort: Sort,
83    binding: Binding,
84    visibility: Visibility,
85    /// Whether this is a numbered local label, which is a place in the file rather than a name and
86    /// so is resolved like one and then left out of the symbol table.
87    numbered: bool,
88}
89
90/// A place in a section whose bytes are an expression that could not be worked out yet.
91#[derive(Debug, Clone)]
92struct Fixup {
93    part: usize,
94    at: u64,
95    width: u8,
96    sum: Sum,
97    /// Which of the four things these bytes are, since a jump is allowed to go through a stub and a
98    /// load of a datum is not, and a name reached through a table is a relocation however near it
99    /// turns out to be. A directive writes [`Reach::Near`], which is the plain one.
100    reach: Reach,
101    line: usize,
102}
103
104/// The file, as it is being read.
105#[derive(Debug, Default)]
106struct Reader {
107    parts: Vec<Part>,
108    /// Which index each section name is at, so that a second `.text` continues the first one.
109    named: HashMap<String, usize>,
110    /// The section being written to.
111    here: usize,
112    /// What `.pushsection` stacked up.
113    stack: Vec<usize>,
114    /// What `.previous` goes back to.
115    before: Option<usize>,
116    syms: Vec<Sym>,
117    known: HashMap<String, usize>,
118    /// How many times each numbered local label has been written so far, which is what `1b` counts
119    /// back from and what `1f` counts forward from.
120    counts: HashMap<String, usize>,
121    /// Which sections have a name pointing into them, so that an empty one that something is
122    /// defined in survives and an empty one nothing mentions does not.
123    labelled: std::collections::HashSet<usize>,
124    fixups: Vec<Fixup>,
125    /// `.set` and `.equ`, as the symbol they name and the expression they were given.
126    sets: Vec<(usize, Sum, usize)>,
127    /// `.size`, the same way.
128    sizes: Vec<(usize, Sum, usize)>,
129    /// What the file said it was called. Kept apart from the rest because it is not a name anything
130    /// refers to, and a file whose own name is also the name of something in it would otherwise be
131    /// one symbol where it should be two.
132    files: Vec<String>,
133    line: usize,
134}
135
136impl Reader {
137    /// Read the whole file.
138    fn run(&mut self, text: &str) -> Result<(), Trouble> {
139        // Before anything else, so that a file which never names a section still has one and a
140        // stray directive has somewhere to go. gas starts in `.text` and so does this.
141        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    /// One line without its comments.
157    ///
158    /// Three kinds, because gas takes three on this machine: `/* */` which may run over the end of
159    /// a line, `//` to the end of one, and `#` to the end of one. The last is why the output of the
160    /// preprocessor can be read directly: a `# 42 "foo.h"` line marker is a comment and nothing has
161    /// to know it is one.
162    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                    // A space, because a comment between two words is a separator and pasting the
173                    // two together would make one word out of them.
174                    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    /// One statement, which is any number of labels and then at most one directive.
219    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    /// One instruction, as the bytes of it.
241    ///
242    /// What an instruction is is [`crate::instruction`]'s business and what it refers to is this
243    /// one's, which is the same division as everywhere else in this file: the bytes come back with
244    /// the places in them that name something, and a name is the whole file's question because the
245    /// label a jump goes to is usually further down than the jump is.
246    ///
247    /// Each of those places becomes the same kind of fixup `.long foo - .` makes, written as the
248    /// name minus where the instruction ends, since that is what the machine counts a branch and a
249    /// rip-relative address from. Then the arithmetic already here does the rest: a target in this
250    /// section cancels down to a number and is written into the bytes, and one that does not is a
251    /// relocation with the right addend on it. A branch says so, because a call to a name another
252    /// object defines is allowed to go through a stub and a load of a datum is not.
253    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            // Written down as a name the file mentions, which is what a call to something in
263            // another object is and the only way it gets into the symbol table at all.
264            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    /// A name defined here, at wherever the current section has got to.
285    fn label(&mut self, name: &str) -> Result<(), Trouble> {
286        let at = self.at();
287        let part = self.here;
288        // A numbered one is a place and not a name, so each writing of it is its own entry and
289        // writing the same number again is what the file is for rather than a mistake.
290        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    /// The place `1b` or `2f` means, if the word is one of those.
309    ///
310    /// Backwards is the last writing of that number above this line and forwards is the next one
311    /// below it, which is why a file can use the same number over and over and why neither spelling
312    /// says anything on its own. Backwards with nothing above it is refused here. Forwards with
313    /// nothing below it cannot be seen yet, so it is refused where the places are worked out.
314    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    /// Everything that starts with a dot.
334    #[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                // The middle operand is the width of one item and the last is its value, and the
381                // default width is one byte, which is why `.fill 8` is eight zero bytes and not
382                // eight of anything else.
383                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            // Hidden and not in any dynamic table at all. Nothing this writes can say the second
433            // half, and the first half is the part a link depends on.
434            "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            // Two directives under one name. `.file "foo.c"` says what this was assembled from and
456            // becomes a symbol, and `.file 1 "foo.c"` is a line table entry which says the same
457            // thing to a debugger and does not. The number in front is the whole difference.
458            "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            // Said for a debugger or a reader and holding nothing a link depends on. Passed over
466            // rather than refused, because a file that carries them is otherwise readable and
467            // refusing would turn a note into a failure.
468            "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    /// `.text`, `.data`, `.bss` and `.rodata`, which name a section this already knows the flags of.
484    fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
485        // A number after one of these is a subsection, and gas lays the numbered ones out after the
486        // unnumbered one at the end of the file rather than where they were written. Refused rather
487        // than merged in place, because merging is right only for a file that never goes back to a
488        // lower number and wrong silently for one that does.
489        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    /// `.section name[, "flags"[, @type]]`.
501    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        // No flags means the name decides, which is what makes `.section .text` the same section as
510        // `.text` rather than an unallocated one that happens to share its name.
511        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                    // Mergeable, with or without strings in it, and part of a group. All three are
522                    // about what a linker may do with two copies of the section, and taking them as
523                    // an ordinary section of the same bytes is correct and merely larger.
524                    '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    /// Go to a section, making it if this is the first time the file has named it.
553    ///
554    /// The flags are taken from the first mention. A second `.section .text,"ax"` after a plain
555    /// `.text` says the same thing gas already worked out, and a file that really does contradict
556    /// itself is one gas warns about and keeps the first answer for.
557    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    /// Go to a section that exists, remembering where this came from for `.previous`.
576    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    /// `.byte`, `.long` and the rest, at the width each of them means.
584    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            // A name, so the bytes are the linker's answer and not this one's. Zeroes go down to
596            // hold the place, which is what the addend of the relocation is counted from.
597            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    /// `.ascii` and the two that add the terminator.
612    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    /// `.align`, `.balign` and `.p2align`, which differ only in what the first number means.
624    ///
625    /// On this machine `.align` counts bytes, which is the trap: on some other machines the same
626    /// directive counts bits, and a file written for one read by the other is off by a factor it
627    /// never says out loud.
628    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        // The default filling is a no-op instruction in a section that holds instructions, because
647        // what is being aligned there is the next instruction and the processor may walk into the
648        // padding from the one before it.
649        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        // The third operand is how much padding is worth it. More than that and the alignment is
658        // skipped entirely, which is how a file asks for an alignment only where it is cheap.
659        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    /// `.globl` and the two others that say who can see a name.
671    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    /// `.hidden` and the rest of how far one reaches.
680    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    /// `.type name,@function` and the other spellings of the same thing.
689    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    /// `.comm` and `.lcomm`, which are two different things under names that look alike.
708    ///
709    /// `.comm` asks the linker for the space and lets every object that asks for the same name
710    /// share one piece of it, which is what a tentative definition in C becomes. `.lcomm` asks for
711    /// nothing of the kind: it puts the bytes in this file's own `.bss` under a name nothing outside
712    /// can see, and two files that use it for the same name get two pieces of storage.
713    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            // What gas picks when nothing said: the natural boundary for something that size, up to
728            // a machine word.
729            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        // Both spellings ask for storage, so both name data, and gas records that whether or not
737        // the file also wrote a `.type` for it. A `.type` afterwards still overrides this, since
738        // this is only what the directive itself says.
739        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    /// How far into the current section the file has got.
765    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    /// Bytes into the current section.
771    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                // A run of zeroes is exactly what such a section holds, so asking for one is not a
776                // mistake and there is nothing to write down but the length.
777                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    /// That many copies of one byte.
789    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    /// The index of a name, making the entry if this is the first time the file has said it.
801    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            // Local until something says otherwise, which is what a plain label is. A name that
812            // turns out to be undefined is made global at the end, since a local one the linker is
813            // asked to find is a contradiction.
814            binding: Binding::Local,
815            visibility: Visibility::Default,
816            // Read off the name, since the one byte no source file can write is exactly what says
817            // this entry came from a numbered local label rather than from something a file named.
818            numbered: name.contains('\u{1}'),
819        });
820        self.known.insert(name.to_owned(), at);
821        at
822    }
823
824    /// Two operands, said the same way wherever a directive wants exactly two.
825    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    /// An expression whose value has to be known now rather than at the end.
834    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    /// One of those that has to fit in a byte.
843    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    /// One of those that has to be a length rather than a negative number.
852    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    /// Parse one, with `.` meaning where the file has got to.
860    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        // Every name it mentioned gets a symbol table entry, so that a relocation against one has
865        // something to point at and so that an undefined one is asked of the linker.
866        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    /// A message about this line.
876    fn bad(&self, why: &str) -> Trouble {
877        Trouble { line: self.line, why: why.to_owned() }
878    }
879
880    /// Work out everything that was waiting for the end of the file.
881    fn finish(mut self) -> Result<Assembled, Trouble> {
882        self.resolve_sets()?;
883        self.resolve_sizes()?;
884        self.resolve_fixups()?;
885        // A section the file only ever mentioned is dropped, so that a `.section` in a macro that
886        // turned out to be unused does not put an empty header in the object. `.text` at the top is
887        // the common case of one.
888        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        // In front, which is where gas puts them and where a reader expects the name of the file to
906        // be before anything that is in it.
907        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            // A numbered local label is a place and not a name. Everything that went to one has been
919            // resolved to a number in the bytes by now, and gas writes no symbol for one either, so
920            // an object this assembles has the same table as an object gas assembles from the same
921            // file rather than a table with a made up name in it.
922            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    /// `.set` and its spellings, which may name each other and so are worked at until they stop
946    /// moving rather than in the order they were written.
947    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            // Backwards, so that removing one does not move the next one out from under its index.
967            for (at, _, _) in done.iter().rev() {
968                self.sets.remove(*at);
969            }
970        }
971        Ok(())
972    }
973
974    /// What one `.set` came out as.
975    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            // `.set alias, real`, which is how a file gives something a second name without a
979            // second copy of it. The two end up at the same place in the same section.
980            [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    /// `.size`, which has to come out as a number because that is what ELF records.
992    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    /// The places whose bytes name something.
1012    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            // A name reached through the global offset table, or through the one entry of it a
1017            // thread-local variable has, is a relocation whatever else is true of it. What goes in
1018            // the bytes is the distance to a word the linker makes, and the linker only knows where
1019            // it put that word, so working the sum out here would answer a different question. The
1020            // sum is the one the instruction made two paragraphs up, which is the name minus the
1021            // end of the instruction, so the addend comes out the way it does for every other
1022            // rip-relative reference and is minus four.
1023            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                    // A distance a branch carries is signed and nothing else, so a byte of it
1050                    // reaches a hundred and twenty seven forwards and a hundred and twenty eight
1051                    // back. A number a directive writes down is counted both ways, because a byte
1052                    // holds two hundred and fifty five as well as minus one and a file writing
1053                    // either means it. Either way what does not fit is refused: a branch out of
1054                    // reach cut down to its low byte goes somewhere nobody wrote, and so does a
1055                    // table of offsets whose entries were quietly truncated.
1056                    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                // The address of something, which is the whole of what a table of pointers holds.
1077                [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1078                    let kind = Reference::Address { bytes: fixup.width };
1079                    (name.clone(), kind, residue.constant, 0)
1080                }
1081                // The distance from these bytes to something, which is what a position independent
1082                // table of offsets holds and what `.long foo - .` is asking for. The subtracted
1083                // side has to be these bytes or somewhere else in the same section, because a
1084                // distance to another section is not a number until the linker has laid both out.
1085                [
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                    // A linker writes `symbol + addend - here`, and what was asked for is
1107                    // `symbol + constant - there`, so the addend is the constant plus however far
1108                    // these bytes are past the place the distance is counted from. That is zero
1109                    // for `.long foo - .`, which is why the two are easy to write down the wrong
1110                    // way round, and it is minus four for a call, whose four bytes are counted
1111                    // from the end of the instruction they are the last of.
1112                    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                    // The same distance said the other way, for the format that wants it apart
1119                    // from the addend rather than folded into it. See `rucc_object::Reloc`.
1120                    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            // A numbered local label that got this far was never written, which for `1f` is the one
1139            // way of getting it wrong that nothing above can see: the file said go to the next `1:`
1140            // and there was no next one. It is not a name, so there is nothing to ask the linker.
1141            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    /// Take an expression down to a constant and whatever names would not cancel.
1168    ///
1169    /// The algebra is the ordinary one and worth saying once. A sum of terms over the same section
1170    /// is `sum(c * x)`, every `x` is that section's address plus a known offset, and the section's
1171    /// address is the only unknown in it. Rewriting each term as its distance from one chosen term
1172    /// in the group leaves `sum(c * (offset - chosen))`, which is a number, plus `sum(c)` times the
1173    /// chosen one. So a group whose coefficients add to zero disappears into the constant however
1174    /// many terms it had, which is what makes `.-foo` a number.
1175    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                        // Not defined here and not a place here, so nothing about it cancels with
1196                        // anything and the linker is the one that knows.
1197                        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/// What an expression came out as: a number, and the names that would not cancel.
1234#[derive(Debug, Clone)]
1235struct Residue {
1236    constant: i64,
1237    left: Vec<Left>,
1238}
1239
1240/// One name an expression would not get rid of.
1241#[derive(Debug, Clone)]
1242struct Left {
1243    /// How many times it is counted, which is one for everything a relocation can say.
1244    coeff: i64,
1245    /// Which name it is, which is what a relocation points at.
1246    what: What,
1247    /// Which section it is in and how far into it, when this file is the one that knows. Nothing
1248    /// for a name the linker has to find, which has no place here to be at.
1249    at: Option<(usize, i64)>,
1250}
1251
1252/// An expression, kept as a sum so that it survives until the names in it have values.
1253#[derive(Debug, Clone, Default, PartialEq, Eq)]
1254struct Sum {
1255    constant: i64,
1256    terms: Vec<Term>,
1257}
1258
1259/// One name in one, and how many times it is counted.
1260#[derive(Debug, Clone, PartialEq, Eq)]
1261struct Term {
1262    coeff: i64,
1263    what: What,
1264}
1265
1266/// What a term is about.
1267#[derive(Debug, Clone, PartialEq, Eq)]
1268enum What {
1269    /// A name, which may or may not turn out to be in this file.
1270    Symbol(String),
1271    /// `.`, which is a place and never a name. Worked out as the expression is parsed, because it
1272    /// means where the file had got to when it was written and not where it got to in the end.
1273    Here { part: usize, at: i64 },
1274}
1275
1276impl Sum {
1277    /// A plain number, and nothing for one that names something.
1278    fn flat(&self) -> Option<i64> {
1279        self.terms.is_empty().then_some(self.constant)
1280    }
1281
1282    /// One name on its own.
1283    fn of(what: What) -> Sum {
1284        Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1285    }
1286
1287    /// A number on its own.
1288    fn just(value: i64) -> Sum {
1289        Sum { constant: value, terms: Vec::new() }
1290    }
1291
1292    /// Two of them added, which is the one operation that always works.
1293    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    /// One of them counted backwards.
1300    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    /// One of them counted a number of times, which only means anything when the number is one.
1312    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
1324/// One expression, being read.
1325struct Parser<'a> {
1326    text: &'a str,
1327    at: usize,
1328    here: (usize, i64),
1329}
1330
1331impl Parser<'_> {
1332    /// The whole of it, and nothing left over.
1333    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    /// The loosest binding of them, which is why it is the outermost.
1346    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    /// Shifts, which bind tighter than the bitwise operators and looser than addition.
1357    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    /// Addition and subtraction, which are the two that keep working when names are involved.
1368    fn sum(&mut self) -> Result<Sum, String> {
1369        let mut left = self.product()?;
1370        loop {
1371            self.space();
1372            // Not the start of `<<` or `>>`, and not a `-` that belongs to nothing.
1373            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    /// Multiplication and the two that go with it.
1380    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            // A name times a number is still a name counted that many times, which is worth keeping
1387            // because `foo*2 - foo` is a thing a macro produces. Everything else here wants two
1388            // numbers, and a name in one of them is a mistake rather than something to guess at.
1389            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    /// A sign or a complement in front of something.
1399    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    /// A number, a name, a character, `.`, or the whole thing again in brackets.
1425    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            // `.` on its own is where the file has got to, and `.L1` is a name that starts with one.
1449            if name == "." {
1450                let (part, at) = self.here;
1451                return Ok(Sum::of(What::Here { part, at }));
1452            }
1453            // What follows an `@` says which table the linker should reach the name through, and
1454            // none of them is a thing a directive can hold, so one here is a file that wants the
1455            // instruction assembler rather than this.
1456            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    /// A number in any of the bases a file may write one in.
1467    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        // Wrapping round rather than refusing, because a file writes `0xffffffffffffffff` for a word
1485        // of ones and means the bits rather than the value.
1486        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        // A suffix, which a file written for more than one assembler carries and which says nothing
1490        // this needs: the width is the directive's business here.
1491        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    /// `'a'` or `'a`, which are both a character and both what gas takes.
1498    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        // The closing quote is optional in gas and a file written by hand often leaves it out, so
1513        // one is taken when it is there and not asked for when it is not.
1514        if self.text[self.at..].starts_with('\'') {
1515            self.at += 1;
1516        }
1517        Ok(Sum::just(i64::from(value)))
1518    }
1519
1520    /// An operator on two things that both have to be numbers.
1521    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    /// The same, once both are numbers.
1529    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    /// One name, as far as it runs.
1546    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    /// Whichever of these is next, and nothing if none of them is.
1555    ///
1556    /// In the order given, which matters: `<<` has to be looked for in front of anything that starts
1557    /// with `<`, or the second half of it is left behind as an operator of its own.
1558    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    /// One exact string, if it is next.
1569    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    /// Past any blanks.
1578    fn space(&mut self) {
1579        while self.text[self.at..].starts_with([' ', '\t']) {
1580            self.at += 1;
1581        }
1582    }
1583}
1584
1585impl Reader {
1586    /// A quoted string, as its bytes.
1587    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
1614/// How far to shift by, which has to be a count and not a number that happens to be negative.
1615fn shift(by: i64) -> Result<u32, String> {
1616    u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
1617}
1618
1619/// What one backslash and what follows it mean, and how much of the text that took.
1620///
1621/// The count is of what came after the backslash, so a caller adds one for the backslash itself.
1622fn 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        // Only the last two digits, which is what gas keeps: the escape is one byte however many
1653        // digits were written.
1654        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    // gas takes an unknown escape as the character itself and warns. Refused here, because the two
1667    // things it is likely to be are a typo and a file meant for another assembler, and both are
1668    // better said than guessed.
1669    Err(format!("'\\{}' is not an escape this compiler knows", first as char))
1670}
1671
1672/// The name of the label at the start of this text, if it starts with one.
1673///
1674/// A colon after a name and nothing else. `.L1:` is one, so is `foo:`, and so is `1:`, which is a
1675/// numbered local label and is a place rather than a name: it may be written as many times in a file
1676/// as the file likes and what refers to it is `1b` for the last one above and `1f` for the next one
1677/// below.
1678fn 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    // Not `::`, which is a different thing in gas, and not a bare name with nothing after it.
1685    if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
1686        return None;
1687    }
1688    Some(text[..end].to_owned())
1689}
1690
1691/// Whether a name may start with this.
1692fn starts(byte: u8) -> bool {
1693    byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
1694}
1695
1696/// Whether a name may go on with this.
1697fn carries_on(byte: u8) -> bool {
1698    starts(byte) || byte.is_ascii_digit()
1699}
1700
1701/// The name a numbered local label is kept under while the file is being read.
1702///
1703/// A file writes `1:` over and over and each one is a different place, so what goes in the table has
1704/// to say which of them this is. The byte in the middle is one no name in a source file can hold, so
1705/// nothing a file writes its own way can collide with one of these, and none of them reaches the
1706/// symbol table at the end.
1707fn counted(number: &str, nth: usize) -> String {
1708    format!("{number}\u{1}{nth}")
1709}
1710
1711/// The text with its quotes taken off, if it had any.
1712fn unquoted(text: &str) -> String {
1713    text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
1714}
1715
1716/// Split on a separator that is outside every string and every bracket.
1717///
1718/// The brackets matter as much as the quotes: `.long (1 + 2), 3` is two operands and splitting on
1719/// every comma would be right here and wrong the moment one turns up inside brackets.
1720pub(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    /// The file, read, with a failure reported as a panic naming the line it was on.
1772    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    /// The bytes of the section of that name.
1780    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    /// The name of that name.
1790    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    /// What a file this could not read said about it.
1799    fn refused(text: &str) -> Trouble {
1800        read(text).err().unwrap_or_else(|| panic!("this was read and should not have been"))
1801    }
1802
1803    /// A numbered local label, which is a place a file may write as often as it likes.
1804    ///
1805    /// `1:` three times is three places and the jumps between them say which by counting, so `1b`
1806    /// is the one above and `1f` is the one below. None of the three is a name, which is why the
1807    /// symbol table at the end holds the one thing this file actually called something.
1808    #[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        // `nop`, then a jump back over both of them, then `nop`, then a jump forward over the
1814        // `nop` behind it, then that `nop`, then `ret`.
1815        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        // One name, and it is the one the file wrote as a name.
1821        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    /// A prefix written on a line of its own, which is how gas takes one and how GMP writes them.
1834    ///
1835    /// `rep;bsf %rdx, %rcx` is two statements on one line, and the first of them is an instruction
1836    /// with no operands whose whole encoding is the byte that goes in front of the next one. The
1837    /// reader needs nothing for this beyond the rows, because a statement is already a statement
1838    /// whether a semicolon or a newline ended the one before it.
1839    #[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    /// A name reached through the global offset table, which is a relocation however near it is.
1850    ///
1851    /// What the four bytes hold is the distance to a slot the linker makes, so there is nothing for
1852    /// the reader to work out even when the name is defined three lines further down. That is the
1853    /// difference from a plain rip-relative reference, which cancels to a number whenever both ends
1854    /// are in the same section.
1855    #[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        // The four bytes are the last four of the instruction and the machine counts them from the
1863        // end of it, so the addend is minus four.
1864        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    /// A name reached with something added to it, which is a table indexed by a value that does not
1870    /// start at zero.
1871    ///
1872    /// The number belongs to the linker along with the name, so it lands in the addend rather than
1873    /// in the bytes, and the minus four the machine already wanted is on top of it.
1874    #[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        // And the name is the name, rather than the whole of what was written in front of the
1882        // bracket, which is what a symbol table full of things nothing defines used to look like.
1883        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        // There is no relocation for the distance back from something, so this is a mistake rather
1890        // than a thing to hand on to the linker.
1891        refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
1892    }
1893
1894    #[test]
1895    fn the_probe_gmp_writes() {
1896        // The case the whole crate exists for. Four lines, no instruction, and the answer configure
1897        // is after is the value of the symbol: four, because the `.long` in front of it took four
1898        // bytes. It seds that number out of `nm` and writes it into a header.
1899        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        // Two's complement in that many bytes, not a refusal, because `.short -1` is how a file
1919        // says two bytes of ones and every table of small offsets somewhere has one in it.
1920        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        // `.align` on this machine is a byte count and `.p2align` is a power of two, which is the
1939        // one thing about them somebody porting a file from another assembler gets wrong.
1940        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        // `.size foo, .-foo` is on the end of nearly every function gas ever wrote. Both ends are in
1986        // the same section, so the difference is known here and there is nothing to ask the linker.
1987        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        // Nothing can be worked out as it is parsed, which is why an expression is kept as a sum
1998        // until the end. `table_end` does not exist yet on the line that subtracts it.
1999        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        // The other shape a reduced expression can have, and the one whose addend is not zero: the
2033        // four bytes sit at offset four, and a relocation counts from where it starts.
2034        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        // The case that says which way round the addend goes, which `message - .` cannot because
2045        // both halves of it are the same number. A linker writes `symbol + addend - here`, and
2046        // what was asked for is `symbol - start`, so the addend is how far these bytes are past
2047        // the label rather than how far the label is behind them.
2048        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        // `.lcomm` is space in `.bss` under a local name, which is a different thing from `.comm`
2067        // however much the two names look alike.
2068        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        // And not one that can collide with something in the file, which is why it is kept apart
2086        // from the rest until the end.
2087        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        // `.file 1 "foo.c"` is the DWARF form and names an entry in a line table, which is a
2097        // different directive wearing the same word.
2098        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        // The failure this crate is written to prevent. An assembler that skipped what it did not
2105        // recognise would write an object that links, and what would be wrong with it is a run of
2106        // missing bytes in the middle of a function.
2107        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        // The whole of what a hand written file is, end to end: a section, a name, three
2115        // instructions and a size counted back to the label.
2116        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        // Because both ends are here, so there is nothing for a linker to work out. The distance
2128        // is counted from the end of the jump, which is why jumping over nothing is zero and not
2129        // minus five.
2130        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        // Which is the whole difference between this and the test below it. A call is allowed to
2144        // reach further than four bytes by way of something the linker writes, and a load of a
2145        // datum is not, so they are two relocations and the shape of the instruction is what says
2146        // which. The addend is minus four because the four bytes are the last of the instruction
2147        // and the machine counts them from the end of it.
2148        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        // Three bytes of opcode and addressing in front of the four, and nothing after them.
2163        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        // `jrcxz` has no longer form, so what goes in is a byte and the byte is all there is. A
2170        // fixup that assumed four would write over the two instructions behind this one.
2171        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        // The other half of the same thing. There is no relaxing a `jrcxz` into something longer,
2178        // so a destination out of its reach is a mistake in the file, and quietly keeping the low
2179        // byte of the distance would send the program somewhere nobody wrote.
2180        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        // Not about instructions at all, and found on the way to the two above: a distance between
2188        // two labels written into a `.byte` was being cut down to its low eight bits. Counted both
2189        // ways, so a byte takes anything from minus a hundred and twenty eight to two hundred and
2190        // fifty five and refuses what is outside that.
2191        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        // The `#` one is why the output of the preprocessor can be handed straight to this: a
2213        // `# 42 "foo.h"` line marker is a comment and nothing has to know it is one.
2214        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        // Every file starts in `.text` whether or not it says so, and a `.section` inside a macro
2242        // that turned out to be unused should not leave a header behind either.
2243        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        // Because the name has to point somewhere, and dropping the section under it would leave a
2251        // symbol pointing at a section that is not there.
2252        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}