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_mir::CfiOp;
34use rucc_object::{
35    Array, Assembled, Binding, Extent, Held, Name, Part, Reference, Reloc, Shape, Sort, Visibility,
36};
37use rucc_target::ObjectFormat;
38use rucc_target::x86_64::{SYSV, gpr_named};
39
40/// What an instruction says about the place in it that names something, under a name that does not
41/// collide with the [`Sort`] an ELF symbol has.
42use crate::instruction::Sort as Reach;
43
44/// A file this could not read, and where in it.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Trouble {
47    /// Which line, counting from one, so that it can be put in front of a message the way every
48    /// other diagnostic in this compiler is.
49    pub line: usize,
50    /// What was wrong with it, already formatted and without the line number in it.
51    pub why: String,
52}
53
54impl std::fmt::Display for Trouble {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}: {}", self.line, self.why)
57    }
58}
59
60impl std::error::Error for Trouble {}
61
62/// What a file of assembly says, as the sections and names an object file is written from.
63///
64/// # Errors
65///
66/// [`Trouble`] for a directive this does not know, an instruction it has no bytes for, an operand
67/// it cannot read, an expression that does not reduce to something a relocation can say, or a file
68/// that is malformed. Every one of them carries the line it was on.
69pub fn read(text: &str) -> Result<Assembled, Trouble> {
70    let mut reader = Reader::default();
71    reader.run(text)?;
72    reader.finish()
73}
74
75/// One name, while the file is still being read.
76///
77/// Held apart from [`Name`] because two of its fields are not answers yet. A `.set` is an expression
78/// that may name something further down the file, and so is the second operand of `.size`, and both
79/// have to wait for the end.
80#[derive(Debug, Clone)]
81struct Sym {
82    name: String,
83    at: Held,
84    size: u64,
85    sort: Sort,
86    binding: Binding,
87    visibility: Visibility,
88    /// Whether this is a numbered local label, which is a place in the file rather than a name and
89    /// so is resolved like one and then left out of the symbol table.
90    numbered: bool,
91}
92
93/// A place in a section whose bytes are an expression that could not be worked out yet.
94#[derive(Debug, Clone)]
95struct Fixup {
96    part: usize,
97    at: u64,
98    width: u8,
99    sum: Sum,
100    /// Which of the four things these bytes are, since a jump is allowed to go through a stub and a
101    /// load of a datum is not, and a name reached through a table is a relocation however near it
102    /// turns out to be. A directive writes [`Reach::Near`], which is the plain one.
103    reach: Reach,
104    line: usize,
105}
106
107/// One function's frame rules, as `.cfi_` directives said them.
108#[derive(Debug)]
109struct Frame {
110    part: usize,
111    start: u64,
112    len: u64,
113    /// The entry the record points at, made where `.cfi_startproc` was written, since that is the
114    /// first instruction the rules are about whether or not a label is there.
115    sym: usize,
116    rows: crate::unwind::Rows,
117    /// How far the end of the frame is from the register it is counted from, which a directive
118    /// that says a slot relative to that register or adjusts the distance has to know.
119    cfa: i32,
120    /// What `.cfi_remember_state` put away, for `.cfi_restore_state` to bring back.
121    remembered: Vec<i32>,
122}
123
124/// The file, as it is being read.
125#[derive(Debug, Default)]
126struct Reader {
127    parts: Vec<Part>,
128    /// Which index each section name is at, so that a second `.text` continues the first one.
129    named: HashMap<String, usize>,
130    /// The section being written to.
131    here: usize,
132    /// What `.pushsection` stacked up.
133    stack: Vec<usize>,
134    /// What `.previous` goes back to.
135    before: Option<usize>,
136    syms: Vec<Sym>,
137    known: HashMap<String, usize>,
138    /// How many times each numbered local label has been written so far, which is what `1b` counts
139    /// back from and what `1f` counts forward from.
140    counts: HashMap<String, usize>,
141    /// Which sections have a name pointing into them, so that an empty one that something is
142    /// defined in survives and an empty one nothing mentions does not.
143    labelled: std::collections::HashSet<usize>,
144    fixups: Vec<Fixup>,
145    /// `.set` and `.equ`, as the symbol they name and the expression they were given.
146    sets: Vec<(usize, Sum, usize)>,
147    /// `.size`, the same way.
148    sizes: Vec<(usize, Sum, usize)>,
149    /// Which entry a name that has been set means from here on. A file may set one name as many
150    /// times as it likes, and each use means the value it had where the use was written, so a
151    /// second setting is a second entry and this says which one is current.
152    current: HashMap<String, String>,
153    /// The numbered entries a relocation names, which are kept in the symbol table so that the
154    /// relocation has something to point at. That is a numbered local label or a set name reached
155    /// from another section, and is rare.
156    relocated: std::collections::HashSet<usize>,
157    /// The function whose frame rules are being read, between `.cfi_startproc` and `.cfi_endproc`.
158    frame: Option<Frame>,
159    /// Every function that has had its frame rules read, in the order the file wrote them.
160    frames: Vec<Frame>,
161    /// Whether `.cfi_sections` left the unwind table out, which a file does when it wants the rules
162    /// for a debugger only.
163    no_unwind: bool,
164    /// What the file said it was called. Kept apart from the rest because it is not a name anything
165    /// refers to, and a file whose own name is also the name of something in it would otherwise be
166    /// one symbol where it should be two.
167    files: Vec<String>,
168    line: usize,
169}
170
171impl Reader {
172    /// Read the whole file.
173    fn run(&mut self, text: &str) -> Result<(), Trouble> {
174        // Before anything else, so that a file which never names a section still has one and a
175        // stray directive has somewhere to go. gas starts in `.text` and so does this.
176        self.section(".text", Shape::of(".text"));
177        let mut commenting = false;
178        for (index, raw) in text.lines().enumerate() {
179            self.line = index + 1;
180            let line = self.strip(raw, &mut commenting)?;
181            for statement in split(&line, ';') {
182                self.statement(statement.trim())?;
183            }
184        }
185        if commenting {
186            return Err(self.bad("a block comment was opened and never closed"));
187        }
188        Ok(())
189    }
190
191    /// One line without its comments.
192    ///
193    /// Three kinds, because gas takes three on this machine: `/* */` which may run over the end of
194    /// a line, `//` to the end of one, and `#` to the end of one. The last is why the output of the
195    /// preprocessor can be read directly: a `# 42 "foo.h"` line marker is a comment and nothing has
196    /// to know it is one.
197    fn strip(&self, raw: &str, commenting: &mut bool) -> Result<String, Trouble> {
198        let mut out = String::with_capacity(raw.len());
199        let bytes = raw.as_bytes();
200        let mut i = 0;
201        let mut quote = None;
202        while i < bytes.len() {
203            let rest = &raw[i..];
204            if *commenting {
205                if let Some(end) = rest.find("*/") {
206                    *commenting = false;
207                    // A space, because a comment between two words is a separator and pasting the
208                    // two together would make one word out of them.
209                    out.push(' ');
210                    i += end + 2;
211                } else {
212                    return Ok(out);
213                }
214                continue;
215            }
216            let ch = bytes[i] as char;
217            if let Some(mark) = quote {
218                out.push(ch);
219                if ch == '\\' && i + 1 < bytes.len() {
220                    out.push(bytes[i + 1] as char);
221                    i += 2;
222                    continue;
223                }
224                if ch == mark {
225                    quote = None;
226                }
227                i += 1;
228                continue;
229            }
230            if ch == '"' {
231                quote = Some('"');
232                out.push(ch);
233                i += 1;
234                continue;
235            }
236            if rest.starts_with("/*") {
237                *commenting = true;
238                i += 2;
239                continue;
240            }
241            if rest.starts_with("//") || ch == '#' {
242                return Ok(out);
243            }
244            out.push(ch);
245            i += 1;
246        }
247        if quote.is_some() {
248            return Err(self.bad("a string was opened and the line ended before it closed"));
249        }
250        Ok(out)
251    }
252
253    /// One statement, which is any number of labels and then at most one directive.
254    fn statement(&mut self, mut text: &str) -> Result<(), Trouble> {
255        loop {
256            text = text.trim_start();
257            let Some(name) = labelled(text) else { break };
258            self.label(&name)?;
259            text = &text[name.len() + 1..];
260        }
261        let text = text.trim();
262        if text.is_empty() {
263            return Ok(());
264        }
265        let (word, rest) = match text.find(char::is_whitespace) {
266            Some(cut) => (&text[..cut], text[cut..].trim()),
267            None => (text, ""),
268        };
269        if let Some((name, what)) = assigned(text) {
270            return self.assign(name, what);
271        }
272        if let Some(directive) = word.strip_prefix('.') {
273            return self.directive(directive, rest);
274        }
275        if let Some((word, rest)) = repeated(word, rest) {
276            return self.instruction(&word, rest);
277        }
278        self.instruction(word, rest)
279    }
280
281    /// One instruction, as the bytes of it.
282    ///
283    /// What an instruction is is [`crate::instruction`]'s business and what it refers to is this
284    /// one's, which is the same division as everywhere else in this file: the bytes come back with
285    /// the places in them that name something, and a name is the whole file's question because the
286    /// label a jump goes to is usually further down than the jump is.
287    ///
288    /// Each of those places becomes the same kind of fixup `.long foo - .` makes, written as the
289    /// name minus where the instruction ends, since that is what the machine counts a branch and a
290    /// rip-relative address from. Then the arithmetic already here does the rest: a target in this
291    /// section cancels down to a number and is written into the bytes, and one that does not is a
292    /// relocation with the right addend on it. A branch says so, because a call to a name another
293    /// object defines is allowed to go through a stub and a load of a datum is not.
294    fn instruction(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
295        let args = if rest.is_empty() { Vec::new() } else { split(rest, ',') };
296        let written = crate::instruction::one(word, &args).map_err(|why| self.bad(&why))?;
297        let part = self.here;
298        let at = self.at();
299        self.put(&written.bytes)?;
300        let end = at + written.bytes.len() as u64;
301        for hole in written.holes {
302            // `.` in an instruction is where the instruction starts, which is what gas means by it
303            // and what `mov .-4(%rip), %eax` counts back from.
304            let here = (part, at as i64);
305            let sum = if hole.sort == Reach::Value {
306                // The number itself, with nothing taken off for where the instruction ends.
307                self.expression_at(&hole.name, here)?
308            } else {
309                let what = if hole.name == "." {
310                    What::Here { part, at: here.1 }
311                } else {
312                    // Written down as a name the file mentions, which is what a call to something
313                    // in another object is and the only way it gets into the symbol table at all.
314                    let name = self.named(&hole.name)?;
315                    self.sym(&name);
316                    What::Symbol(name)
317                };
318                Sum {
319                    constant: hole.addend,
320                    terms: vec![
321                        Term { coeff: 1, what },
322                        Term { coeff: -1, what: What::Here { part, at: end as i64 } },
323                    ],
324                }
325            };
326            self.fixups.push(Fixup {
327                part,
328                at: at + hole.at as u64,
329                width: hole.width,
330                sum,
331                reach: hole.sort,
332                line: self.line,
333            });
334        }
335        Ok(())
336    }
337
338    /// A name defined here, at wherever the current section has got to.
339    fn label(&mut self, name: &str) -> Result<(), Trouble> {
340        let at = self.at();
341        let part = self.here;
342        // A numbered one is a place and not a name, so each writing of it is its own entry and
343        // writing the same number again is what the file is for rather than a mistake.
344        let numbered = name.bytes().all(|byte| byte.is_ascii_digit());
345        let held = if numbered {
346            let count = self.counts.entry(name.to_owned()).or_insert(0);
347            *count += 1;
348            counted(name, *count)
349        } else {
350            name.to_owned()
351        };
352        let sym = self.sym(&held);
353        if self.syms[sym].at != Held::Undefined {
354            let what = format!("'{name}' is defined twice");
355            return Err(self.bad(&what));
356        }
357        self.syms[sym].at = Held::In { part, offset: at };
358        self.labelled.insert(part);
359        Ok(())
360    }
361
362    /// The place `1b` or `2f` means, if the word is one of those.
363    ///
364    /// Backwards is the last writing of that number above this line and forwards is the next one
365    /// below it, which is why a file can use the same number over and over and why neither spelling
366    /// says anything on its own. Backwards with nothing above it is refused here. Forwards with
367    /// nothing below it cannot be seen yet, so it is refused where the places are worked out.
368    fn numbered(&self, word: &str) -> Result<Option<String>, Trouble> {
369        let Some(number) = word.strip_suffix(['b', 'f']) else {
370            return Ok(None);
371        };
372        if number.is_empty() || !number.bytes().all(|byte| byte.is_ascii_digit()) {
373            return Ok(None);
374        }
375        let count = self.counts.get(number).copied().unwrap_or(0);
376        if word.ends_with('b') {
377            if count == 0 {
378                let what =
379                    format!("'{word}' goes back to a '{number}:' and there is none above it");
380                return Err(self.bad(&what));
381            }
382            return Ok(Some(counted(number, count)));
383        }
384        Ok(Some(counted(number, count + 1)))
385    }
386
387    /// The entry a name the file wrote means where it was written.
388    ///
389    /// That is the place a numbered label refers to, the current setting of a name that has been
390    /// set more than once, and otherwise the name.
391    fn named(&self, word: &str) -> Result<String, Trouble> {
392        if let Some(place) = self.numbered(word)? {
393            return Ok(place);
394        }
395        Ok(self.current.get(word).cloned().unwrap_or_else(|| word.to_owned()))
396    }
397
398    /// `name = value`, and `.set` and `.equ` which say the same thing.
399    ///
400    /// The first setting is the name itself, so that a use further up the file which reached
401    /// forward to it finds it. A setting after that is a new entry, because a use written between
402    /// the two means the value the name had then: gas does the same by copying the symbol when it
403    /// is set again, and a file can count on it. The value is read before the new entry is made,
404    /// so `x = x + 1` means the one before.
405    fn assign(&mut self, name: &str, what: &str) -> Result<(), Trouble> {
406        let sum = self.expression(what)?;
407        let held = match self.current.get(name) {
408            Some(_) => format!("{name}\u{1}={}", self.syms.len()),
409            None => name.to_owned(),
410        };
411        let sym = self.sym(&held);
412        if self.syms[sym].at != Held::Undefined {
413            let what = format!("'{name}' is defined twice");
414            return Err(self.bad(&what));
415        }
416        self.current.insert(name.to_owned(), held);
417        self.sets.push((sym, sum, self.line));
418        Ok(())
419    }
420
421    /// A frame rule, which says what an unwinder standing at this instruction should believe.
422    ///
423    /// What the rules say is the same [`CfiOp`] the compiler's own functions are described with,
424    /// and the table is written from them by the same code, so a function read from text and the
425    /// same function compiled straight to an object unwind the same way. The directives that say
426    /// something this table has no row for, a personality routine and the rest, are passed over as
427    /// they were before any of this was read, which leaves those functions described as well as a
428    /// C function needs.
429    fn cfi(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
430        match word {
431            "cfi_startproc" => {
432                if self.frame.is_some() {
433                    return Err(self.bad("a '.cfi_startproc' inside another one"));
434                }
435                let sym = self.sym(&format!("\u{1}frame{}", self.frames.len()));
436                let (part, start) = (self.here, self.at());
437                self.syms[sym].at = Held::In { part, offset: start };
438                // Where every function starts, which is what the table's header says: the frame
439                // ends one word above the stack pointer because the call pushed a return address.
440                let frame = Frame {
441                    part,
442                    start,
443                    len: 0,
444                    sym,
445                    rows: Vec::new(),
446                    cfa: 8,
447                    remembered: Vec::new(),
448                };
449                self.frame = Some(frame);
450                return Ok(());
451            }
452            "cfi_sections" => {
453                self.no_unwind = !args.iter().any(|arg| arg.trim() == ".eh_frame");
454                return Ok(());
455            }
456            "cfi_endproc"
457            | "cfi_def_cfa"
458            | "cfi_def_cfa_offset"
459            | "cfi_adjust_cfa_offset"
460            | "cfi_def_cfa_register"
461            | "cfi_offset"
462            | "cfi_rel_offset"
463            | "cfi_restore"
464            | "cfi_remember_state"
465            | "cfi_restore_state" => {}
466            _ => return Ok(()),
467        }
468        let (here, at) = (self.here, self.at());
469        let line = self.line;
470        let bad = |why: &str| Trouble { line, why: why.to_owned() };
471        let Some(mut frame) = self.frame.take() else {
472            return Err(bad("a frame rule outside '.cfi_startproc' and '.cfi_endproc'"));
473        };
474        if frame.part != here {
475            return Err(bad("a frame rule in another section from the function it is about"));
476        }
477        let op = match word {
478            "cfi_endproc" => {
479                frame.len = at - frame.start;
480                self.frames.push(frame);
481                return Ok(());
482            }
483            "cfi_def_cfa" => {
484                let [reg, offset] = self.two(args, ".cfi_def_cfa")?;
485                frame.cfa = self.distance(&offset)?;
486                CfiOp::DefCfa { reg: self.dwarf(&reg)?, offset: frame.cfa }
487            }
488            "cfi_def_cfa_offset" | "cfi_adjust_cfa_offset" => {
489                let by = self.distance(args.first().map_or("", |arg| arg.as_str()))?;
490                frame.cfa = if word == "cfi_def_cfa_offset" { by } else { frame.cfa + by };
491                CfiOp::DefCfaOffset(frame.cfa)
492            }
493            "cfi_def_cfa_register" => {
494                CfiOp::DefCfaRegister(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
495            }
496            "cfi_offset" | "cfi_rel_offset" => {
497                let [reg, offset] = self.two(args, &format!(".{word}"))?;
498                let mut offset = self.distance(&offset)?;
499                // Counted from the register the frame is counted from rather than from the end of
500                // the frame, which is the same slot once the distance between the two is taken off.
501                if word == "cfi_rel_offset" {
502                    offset -= frame.cfa;
503                }
504                if offset >= 0 || offset % 8 != 0 {
505                    return Err(bad(
506                        "a register saved somewhere that is not a whole slot below the end of the \
507                         frame, which is the only place this writes a rule for",
508                    ));
509                }
510                CfiOp::Offset { reg: self.dwarf(&reg)?, offset }
511            }
512            "cfi_restore" => {
513                CfiOp::Restore(self.dwarf(args.first().map_or("", |arg| arg.as_str()))?)
514            }
515            "cfi_remember_state" => {
516                frame.remembered.push(frame.cfa);
517                CfiOp::RememberState
518            }
519            "cfi_restore_state" => {
520                frame.cfa = frame.remembered.pop().ok_or_else(|| {
521                    bad("a '.cfi_restore_state' with nothing remembered to restore")
522                })?;
523                CfiOp::RestoreState
524            }
525            _ => unreachable!("every other word returned above"),
526        };
527        frame.rows.push(((at - frame.start) as usize, op));
528        self.frame = Some(frame);
529        Ok(())
530    }
531
532    /// A distance in a frame rule, which is a number and not negative for the end of the frame.
533    fn distance(&mut self, text: &str) -> Result<i32, Trouble> {
534        let value = self.number(text)?;
535        i32::try_from(value).map_err(|_| self.bad(&format!("{value} is not a distance in a frame")))
536    }
537
538    /// The number DWARF gives a register a frame rule names, which a file may write either way.
539    fn dwarf(&self, text: &str) -> Result<u16, Trouble> {
540        let text = text.trim();
541        if let Ok(number) = text.parse::<u16>() {
542            return Ok(number);
543        }
544        let name = text.strip_prefix('%').unwrap_or(text);
545        if name == "rip" {
546            return Ok(SYSV.dwarf_return_address);
547        }
548        gpr_named(name)
549            .and_then(|(reg, _)| SYSV.dwarf(SYSV.int_class, reg))
550            .ok_or_else(|| self.bad(&format!("'{text}' is not a register a frame rule can name")))
551    }
552
553    /// Everything that starts with a dot.
554    #[allow(clippy::too_many_lines)]
555    fn directive(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
556        let args = split(rest, ',');
557        match word {
558            "text" | "data" | "bss" | "rodata" => {
559                self.plain(word, rest)?;
560            }
561            "section" => self.section_directive(&args)?,
562            "pushsection" => {
563                self.stack.push(self.here);
564                self.section_directive(&args)?;
565            }
566            "popsection" => {
567                let Some(back) = self.stack.pop() else {
568                    return Err(self.bad(".popsection with nothing pushed"));
569                };
570                self.go(back);
571            }
572            "previous" => {
573                let Some(back) = self.before else {
574                    return Err(self.bad(".previous with no section before this one"));
575                };
576                self.go(back);
577            }
578
579            "byte" => self.data(&args, 1)?,
580            "short" | "word" | "hword" | "value" | "2byte" => self.data(&args, 2)?,
581            "long" | "int" | "4byte" => self.data(&args, 4)?,
582            "quad" | "8byte" => self.data(&args, 8)?,
583
584            "ascii" => self.text_bytes(&args, false)?,
585            "asciz" | "string" => self.text_bytes(&args, true)?,
586
587            "space" | "skip" | "zero" => {
588                if args.is_empty() || args.len() > 2 {
589                    return Err(self.bad(&format!(".{word} wants a size and an optional fill")));
590                }
591                let size = self.number(&args[0])?;
592                let size = self.count(size)?;
593                let fill = match args.get(1) {
594                    Some(arg) => self.byte(arg)?,
595                    None => 0,
596                };
597                self.pad(size, fill)?;
598            }
599            "fill" => {
600                // The middle operand is the width of one item and the last is its value, and the
601                // default width is one byte, which is why `.fill 8` is eight zero bytes and not
602                // eight of anything else.
603                if args.is_empty() || args.len() > 3 {
604                    return Err(self.bad(".fill wants a count and an optional width and value"));
605                }
606                let count = self.number(&args[0])?;
607                let count = self.count(count)?;
608                let width = match args.get(1) {
609                    Some(arg) => {
610                        let width = self.number(arg)?;
611                        self.count(width)?
612                    }
613                    None => 1,
614                };
615                let value = match args.get(2) {
616                    Some(arg) => self.number(arg)?,
617                    None => 0,
618                };
619                if width > 8 {
620                    return Err(self.bad(".fill of items wider than eight bytes is not written"));
621                }
622                let one = value.to_le_bytes();
623                for _ in 0..count {
624                    self.put(&one[..width as usize])?;
625                }
626            }
627
628            "align" | "balign" | "p2align" => self.align(word, &args)?,
629            "org" => {
630                let Some(first) = args.first() else {
631                    return Err(self.bad(".org with nothing after it"));
632                };
633                let to = self.number(first)?;
634                let to = self.count(to)?;
635                let fill = match args.get(1) {
636                    Some(arg) => self.byte(arg)?,
637                    None => 0,
638                };
639                let at = self.at();
640                if to < at {
641                    let what = format!(".org back to {to} from {at}, which would overwrite bytes");
642                    return Err(self.bad(&what));
643                }
644                self.pad(to - at, fill)?;
645            }
646
647            "globl" | "global" => self.bind(&args, Binding::Global)?,
648            "weak" => self.bind(&args, Binding::Weak)?,
649            "local" => self.bind(&args, Binding::Local)?,
650            "hidden" => self.sight(&args, Visibility::Hidden)?,
651            "protected" => self.sight(&args, Visibility::Protected)?,
652            // Hidden and not in any dynamic table at all. Nothing this writes can say the second
653            // half, and the first half is the part a link depends on.
654            "internal" => self.sight(&args, Visibility::Hidden)?,
655
656            "type" => self.type_directive(&args)?,
657            "err" | "error" => {
658                let what = unquoted(args.first().map_or("", |arg| arg.trim()));
659                return Err(self.bad(&format!("the file says so itself: {what}")));
660            }
661            "size" => {
662                let [name, what] = self.two(&args, ".size")?;
663                let sum = self.expression(&what)?;
664                let sym = self.sym(&name);
665                self.sizes.push((sym, sum, self.line));
666            }
667            "set" | "equ" | "equiv" => {
668                let [name, what] = self.two(&args, &format!(".{word}"))?;
669                self.assign(&name, &what)?;
670            }
671            "comm" | "lcomm" => self.common(&args, word == "lcomm")?,
672
673            // Two directives under one name. `.file "foo.c"` says what this was assembled from and
674            // becomes a symbol, and `.file 1 "foo.c"` is a line table entry which says the same
675            // thing to a debugger and does not. The number in front is the whole difference.
676            "file" => {
677                let what = args.first().map_or("", |arg| arg.trim());
678                if what.starts_with('"') {
679                    self.files.push(unquoted(what));
680                }
681            }
682
683            // Said for a debugger or a reader and holding nothing a link depends on. Passed over
684            // rather than refused, because a file that carries them is otherwise readable and
685            // refusing would turn a note into a failure.
686            "ident" | "loc" | "loc_mark_labels" | "version" | "arch" | "code64" | "att_syntax"
687            | "intel_syntax" | "warning" => {}
688            _ if word.starts_with("cfi_") => self.cfi(word, &args)?,
689
690            _ => {
691                let what = format!(
692                    "'.{word}' is a directive this compiler does not know, so nothing was written \
693                     for it"
694                );
695                return Err(self.bad(&what));
696            }
697        }
698        Ok(())
699    }
700
701    /// `.text`, `.data`, `.bss` and `.rodata`, which name a section this already knows the flags of.
702    fn plain(&mut self, word: &str, rest: &str) -> Result<(), Trouble> {
703        // A number after one of these is a subsection, and gas lays the numbered ones out after the
704        // unnumbered one at the end of the file rather than where they were written. Refused rather
705        // than merged in place, because merging is right only for a file that never goes back to a
706        // lower number and wrong silently for one that does.
707        if !rest.trim().is_empty() && rest.trim() != "0" {
708            let what =
709                format!("'.{word} {}' is a subsection, which is not written yet", rest.trim());
710            return Err(self.bad(&what));
711        }
712        let name = format!(".{word}");
713        let shape = Shape::of(&name);
714        self.section(&name, shape);
715        Ok(())
716    }
717
718    /// `.section name[, "flags"[, @type]]`.
719    fn section_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
720        let Some(name) = args.first() else {
721            return Err(self.bad(".section with no name"));
722        };
723        let name = unquoted(name.trim());
724        if name.is_empty() {
725            return Err(self.bad(".section with no name"));
726        }
727        // No flags means the name decides, which is what makes `.section .text` the same section as
728        // `.text` rather than an unallocated one that happens to share its name.
729        let mut shape = Shape::of(&name);
730        if let Some(flags) = args.get(1) {
731            let letters = unquoted(flags.trim());
732            shape = Shape { bits: true, ..Shape::default() };
733            for letter in letters.chars() {
734                match letter {
735                    'a' => shape.alloc = true,
736                    'w' => shape.write = true,
737                    'x' => shape.exec = true,
738                    'T' => shape.thread = true,
739                    // Mergeable, with or without strings in it, and part of a group. All three are
740                    // about what a linker may do with two copies of the section, and taking them as
741                    // an ordinary section of the same bytes is correct and merely larger.
742                    'M' | 'S' | 'G' | 'o' | 'e' | 'R' | 'd' => {}
743                    _ => {
744                        let what = format!("'{letter}' is not a section flag this compiler knows");
745                        return Err(self.bad(&what));
746                    }
747                }
748            }
749        }
750        if let Some(kind) = args.get(2) {
751            let kind = kind.trim().trim_start_matches(['@', '%']);
752            let kind = unquoted(kind);
753            match kind.as_str() {
754                "progbits" => shape.bits = true,
755                "nobits" => shape.bits = false,
756                "init_array" => shape.array = Some(Array::Init),
757                "fini_array" => shape.array = Some(Array::Fini),
758                "preinit_array" => shape.array = Some(Array::Preinit),
759                "note" => shape.bits = true,
760                _ => {
761                    let what = format!("'{kind}' is not a section type this compiler writes");
762                    return Err(self.bad(&what));
763                }
764            }
765        }
766        self.section(&name, shape);
767        Ok(())
768    }
769
770    /// Go to a section, making it if this is the first time the file has named it.
771    ///
772    /// The flags are taken from the first mention. A second `.section .text,"ax"` after a plain
773    /// `.text` says the same thing gas already worked out, and a file that really does contradict
774    /// itself is one gas warns about and keeps the first answer for.
775    fn section(&mut self, name: &str, shape: Shape) {
776        if let Some(&at) = self.named.get(name) {
777            self.go(at);
778            return;
779        }
780        let at = self.parts.len();
781        self.parts.push(Part {
782            name: name.to_owned(),
783            bytes: Vec::new(),
784            size: 0,
785            align: 1,
786            shape,
787            relocs: Vec::new(),
788        });
789        self.named.insert(name.to_owned(), at);
790        self.go(at);
791    }
792
793    /// Go to a section that exists, remembering where this came from for `.previous`.
794    fn go(&mut self, at: usize) {
795        if at != self.here {
796            self.before = Some(self.here);
797            self.here = at;
798        }
799    }
800
801    /// `.byte`, `.long` and the rest, at the width each of them means.
802    fn data(&mut self, args: &[String], width: u8) -> Result<(), Trouble> {
803        if args.is_empty() {
804            return Err(self.bad("a data directive with nothing after it"));
805        }
806        for arg in args {
807            let sum = self.expression(arg)?;
808            let at = self.at();
809            if let Some(value) = sum.flat() {
810                self.put(&value.to_le_bytes()[..width as usize])?;
811                continue;
812            }
813            // A name, so the bytes are the linker's answer and not this one's. Zeroes go down to
814            // hold the place, which is what the addend of the relocation is counted from.
815            let part = self.here;
816            if !self.parts[part].shape.bits {
817                let what = format!(
818                    "'{}' holds no bytes and this asks the linker to write some into it",
819                    self.parts[part].name
820                );
821                return Err(self.bad(&what));
822            }
823            self.put(&vec![0u8; width as usize])?;
824            self.fixups.push(Fixup { part, at, width, sum, reach: Reach::Near, line: self.line });
825        }
826        Ok(())
827    }
828
829    /// `.ascii` and the two that add the terminator.
830    fn text_bytes(&mut self, args: &[String], terminated: bool) -> Result<(), Trouble> {
831        for arg in args {
832            let mut bytes = self.string(arg.trim())?;
833            if terminated {
834                bytes.push(0);
835            }
836            self.put(&bytes)?;
837        }
838        Ok(())
839    }
840
841    /// `.align`, `.balign` and `.p2align`, which differ only in what the first number means.
842    ///
843    /// On this machine `.align` counts bytes, which is the trap: on some other machines the same
844    /// directive counts bits, and a file written for one read by the other is off by a factor it
845    /// never says out loud.
846    fn align(&mut self, word: &str, args: &[String]) -> Result<(), Trouble> {
847        let Some(head) = args.first() else {
848            return Err(self.bad(&format!(".{word} with nothing after it")));
849        };
850        let first = self.number(head)?;
851        let first = self.count(first)?;
852        let boundary = if word == "p2align" {
853            if first > 31 {
854                return Err(self.bad(".p2align of more than two gigabytes"));
855            }
856            1u64 << first
857        } else {
858            first
859        };
860        if boundary == 0 || !boundary.is_power_of_two() {
861            let what = format!("an alignment of {boundary}, which is not a power of two");
862            return Err(self.bad(&what));
863        }
864        // The default filling is a no-op instruction in a section that holds instructions, because
865        // what is being aligned there is the next instruction and the processor may walk into the
866        // padding from the one before it.
867        let default = if self.parts[self.here].shape.exec { 0x90 } else { 0 };
868        let fill = match args.get(1) {
869            Some(arg) if !arg.trim().is_empty() => self.byte(arg)?,
870            _ => default,
871        };
872        let at = self.at();
873        let over = at % boundary;
874        let need = if over == 0 { 0 } else { boundary - over };
875        // The third operand is how much padding is worth it. More than that and the alignment is
876        // skipped entirely, which is how a file asks for an alignment only where it is cheap.
877        if let Some(most) = args.get(2).filter(|arg| !arg.trim().is_empty()) {
878            let most = self.number(&most.clone())?;
879            if need > self.count(most)? {
880                return Ok(());
881            }
882        }
883        let part = &mut self.parts[self.here];
884        part.align = part.align.max(boundary);
885        self.pad(need, fill)
886    }
887
888    /// `.globl` and the two others that say who can see a name.
889    fn bind(&mut self, args: &[String], binding: Binding) -> Result<(), Trouble> {
890        for arg in args {
891            let sym = self.sym(arg.trim());
892            self.syms[sym].binding = binding;
893        }
894        Ok(())
895    }
896
897    /// `.hidden` and the rest of how far one reaches.
898    fn sight(&mut self, args: &[String], visibility: Visibility) -> Result<(), Trouble> {
899        for arg in args {
900            let sym = self.sym(arg.trim());
901            self.syms[sym].visibility = visibility;
902        }
903        Ok(())
904    }
905
906    /// `.type name,@function` and the other spellings of the same thing.
907    fn type_directive(&mut self, args: &[String]) -> Result<(), Trouble> {
908        let [name, what] = self.two(args, ".type")?;
909        let what = unquoted(what.trim().trim_start_matches(['@', '%']));
910        let sort = match what.trim_start_matches("STT_").to_ascii_lowercase().as_str() {
911            "func" | "function" => Sort::Func,
912            "object" | "gnu_unique_object" => Sort::Object,
913            "tls_object" | "tls" => Sort::Thread,
914            "notype" | "" => Sort::Untyped,
915            other => {
916                let what = format!("'{other}' is not a symbol type this compiler writes");
917                return Err(self.bad(&what));
918            }
919        };
920        let sym = self.sym(name.trim());
921        self.syms[sym].sort = sort;
922        Ok(())
923    }
924
925    /// `.comm` and `.lcomm`, which are two different things under names that look alike.
926    ///
927    /// `.comm` asks the linker for the space and lets every object that asks for the same name
928    /// share one piece of it, which is what a tentative definition in C becomes. `.lcomm` asks for
929    /// nothing of the kind: it puts the bytes in this file's own `.bss` under a name nothing outside
930    /// can see, and two files that use it for the same name get two pieces of storage.
931    fn common(&mut self, args: &[String], local: bool) -> Result<(), Trouble> {
932        if !(2..=3).contains(&args.len()) {
933            return Err(
934                self.bad("a common directive wants a name, a size and an optional alignment")
935            );
936        }
937        let name = args[0].trim().to_owned();
938        let size = self.number(&args[1])?;
939        let size = self.count(size)?;
940        let align = match args.get(2) {
941            Some(arg) => {
942                let align = self.number(&arg.clone())?;
943                self.count(align)?.max(1)
944            }
945            // What gas picks when nothing said: the natural boundary for something that size, up to
946            // a machine word.
947            None => size.next_power_of_two().clamp(1, 16),
948        };
949        if !align.is_power_of_two() {
950            let what = format!("an alignment of {align}, which is not a power of two");
951            return Err(self.bad(&what));
952        }
953        let sym = self.sym(&name);
954        // Both spellings ask for storage, so both name data, and gas records that whether or not
955        // the file also wrote a `.type` for it. A `.type` afterwards still overrides this, since
956        // this is only what the directive itself says.
957        self.syms[sym].sort = Sort::Object;
958        if local {
959            let was = self.here;
960            self.section(".bss", Shape::of(".bss"));
961            let part = &mut self.parts[self.here];
962            part.align = part.align.max(align);
963            let over = part.size % align;
964            if over != 0 {
965                part.size += align - over;
966            }
967            let offset = self.parts[self.here].size;
968            self.parts[self.here].size += size;
969            let at = self.here;
970            self.syms[sym].at = Held::In { part: at, offset };
971            self.syms[sym].size = size;
972            self.syms[sym].binding = Binding::Local;
973            self.go(was);
974        } else {
975            self.syms[sym].at = Held::Common { size, align };
976            self.syms[sym].size = size;
977            self.syms[sym].binding = Binding::Global;
978        }
979        Ok(())
980    }
981
982    /// How far into the current section the file has got.
983    fn at(&self) -> u64 {
984        let part = &self.parts[self.here];
985        if part.shape.bits { part.bytes.len() as u64 } else { part.size }
986    }
987
988    /// Bytes into the current section.
989    fn put(&mut self, bytes: &[u8]) -> Result<(), Trouble> {
990        let part = &mut self.parts[self.here];
991        if !part.shape.bits {
992            if bytes.iter().all(|byte| *byte == 0) {
993                // A run of zeroes is exactly what such a section holds, so asking for one is not a
994                // mistake and there is nothing to write down but the length.
995                part.size += bytes.len() as u64;
996                return Ok(());
997            }
998            let what = format!("'{}' holds no bytes and this puts some in it", part.name);
999            return Err(Trouble { line: self.line, why: what });
1000        }
1001        part.bytes.extend_from_slice(bytes);
1002        part.size = part.bytes.len() as u64;
1003        Ok(())
1004    }
1005
1006    /// That many copies of one byte.
1007    fn pad(&mut self, count: u64, fill: u8) -> Result<(), Trouble> {
1008        let part = &mut self.parts[self.here];
1009        if !part.shape.bits {
1010            part.size += count;
1011            return Ok(());
1012        }
1013        part.bytes.resize(part.bytes.len() + usize::try_from(count).unwrap_or(usize::MAX), fill);
1014        part.size = part.bytes.len() as u64;
1015        Ok(())
1016    }
1017
1018    /// The index of a name, making the entry if this is the first time the file has said it.
1019    fn sym(&mut self, name: &str) -> usize {
1020        if let Some(&at) = self.known.get(name) {
1021            return at;
1022        }
1023        let at = self.syms.len();
1024        self.syms.push(Sym {
1025            name: name.to_owned(),
1026            at: Held::Undefined,
1027            size: 0,
1028            sort: Sort::Untyped,
1029            // Local until something says otherwise, which is what a plain label is. A name that
1030            // turns out to be undefined is made global at the end, since a local one the linker is
1031            // asked to find is a contradiction.
1032            binding: Binding::Local,
1033            visibility: Visibility::Default,
1034            // Read off the name, since the one byte no source file can write is exactly what says
1035            // this entry came from a numbered local label rather than from something a file named.
1036            numbered: name.contains('\u{1}'),
1037        });
1038        self.known.insert(name.to_owned(), at);
1039        at
1040    }
1041
1042    /// Two operands, said the same way wherever a directive wants exactly two.
1043    fn two(&self, args: &[String], what: &str) -> Result<[String; 2], Trouble> {
1044        if args.len() != 2 {
1045            let why = format!("{what} wants two operands and was given {}", args.len());
1046            return Err(Trouble { line: self.line, why });
1047        }
1048        Ok([args[0].trim().to_owned(), args[1].trim().to_owned()])
1049    }
1050
1051    /// An expression whose value has to be known now rather than at the end.
1052    fn number(&mut self, text: &str) -> Result<i64, Trouble> {
1053        let sum = self.expression(text)?;
1054        sum.flat().ok_or_else(|| Trouble {
1055            line: self.line,
1056            why: format!("'{}' has to be a number here and it names something", text.trim()),
1057        })
1058    }
1059
1060    /// One of those that has to fit in a byte.
1061    fn byte(&mut self, text: &str) -> Result<u8, Trouble> {
1062        let value = self.number(text)?;
1063        u8::try_from(value & 0xff).map_err(|_| Trouble {
1064            line: self.line,
1065            why: format!("{value} does not fit in a byte"),
1066        })
1067    }
1068
1069    /// One of those that has to be a length rather than a negative number.
1070    fn count(&self, value: i64) -> Result<u64, Trouble> {
1071        u64::try_from(value).map_err(|_| Trouble {
1072            line: self.line,
1073            why: format!("{value} is negative and this is a length"),
1074        })
1075    }
1076
1077    /// Parse one, with `.` meaning where the file has got to.
1078    fn expression(&mut self, text: &str) -> Result<Sum, Trouble> {
1079        self.expression_at(text, (self.here, self.at() as i64))
1080    }
1081
1082    /// The same, with `.` meaning `here`.
1083    fn expression_at(&mut self, text: &str, here: (usize, i64)) -> Result<Sum, Trouble> {
1084        let mut parser = Parser { text: text.trim(), at: 0, here };
1085        let mut sum = parser.whole().map_err(|why| Trouble { line: self.line, why })?;
1086        // Every name it mentioned gets a symbol table entry, so that a relocation against one has
1087        // something to point at and so that an undefined one is asked of the linker.
1088        for term in &mut sum.terms {
1089            if let What::Symbol(name) = &term.what {
1090                let name = self.named(name)?;
1091                self.sym(&name);
1092                term.what = What::Symbol(name);
1093            }
1094        }
1095        Ok(sum)
1096    }
1097
1098    /// A message about this line.
1099    fn bad(&self, why: &str) -> Trouble {
1100        Trouble { line: self.line, why: why.to_owned() }
1101    }
1102
1103    /// Work out everything that was waiting for the end of the file.
1104    fn finish(mut self) -> Result<Assembled, Trouble> {
1105        if self.frame.is_some() {
1106            return Err(self.bad("a '.cfi_startproc' that is never ended"));
1107        }
1108        self.unwind_table();
1109        self.resolve_sets()?;
1110        self.resolve_sizes()?;
1111        self.resolve_fixups()?;
1112        // A section the file only ever mentioned is dropped, so that a `.section` in a macro that
1113        // turned out to be unused does not put an empty header in the object. `.text` at the top is
1114        // the common case of one.
1115        let keep: Vec<bool> = self
1116            .parts
1117            .iter()
1118            .enumerate()
1119            .map(|(at, part)| {
1120                part.size > 0 || !part.relocs.is_empty() || self.labelled.contains(&at)
1121            })
1122            .collect();
1123        let mut moved = vec![0usize; self.parts.len()];
1124        let mut parts = Vec::with_capacity(self.parts.len());
1125        for (at, part) in self.parts.into_iter().enumerate() {
1126            if keep[at] {
1127                moved[at] = parts.len();
1128                parts.push(part);
1129            }
1130        }
1131        let mut names = Vec::with_capacity(self.syms.len() + self.files.len());
1132        // In front, which is where gas puts them and where a reader expects the name of the file to
1133        // be before anything that is in it.
1134        for file in self.files {
1135            names.push(Name {
1136                name: file,
1137                at: Held::Absolute(0),
1138                size: 0,
1139                sort: Sort::File,
1140                binding: Binding::Local,
1141                visibility: Visibility::Default,
1142            });
1143        }
1144        for (index, sym) in self.syms.into_iter().enumerate() {
1145            // A numbered local label is a place and not a name. Everything that went to one has been
1146            // resolved to a number in the bytes by now, and gas writes no symbol for one either, so
1147            // an object this assembles has the same table as an object gas assembles from the same
1148            // file rather than a table with a made up name in it.
1149            if sym.numbered && !self.relocated.contains(&index) {
1150                continue;
1151            }
1152            let at = match sym.at {
1153                Held::In { part, offset } => Held::In { part: moved[part], offset },
1154                other => other,
1155            };
1156            let binding = match (at, sym.binding) {
1157                (Held::Undefined, Binding::Local) => Binding::Global,
1158                (_, binding) => binding,
1159            };
1160            names.push(Name {
1161                name: sym.name,
1162                at,
1163                size: sym.size,
1164                sort: sym.sort,
1165                binding,
1166                visibility: sym.visibility,
1167            });
1168        }
1169        Ok(Assembled { parts, names })
1170    }
1171
1172    /// The unwind table the frame rules describe, as a section of its own.
1173    ///
1174    /// Written only when a file said some rules, which is every function the compiler emits and
1175    /// every function gcc does. A file of assembly written by hand with none gets no table, the same
1176    /// as it does from gas.
1177    fn unwind_table(&mut self) {
1178        if self.frames.is_empty() || self.no_unwind {
1179            return;
1180        }
1181        let funcs: Vec<Extent> = self
1182            .frames
1183            .iter()
1184            .map(|frame| Extent {
1185                name: self.syms[frame.sym].name.clone(),
1186                start: frame.start as usize,
1187                len: frame.len as usize,
1188                align: 1,
1189                binding: Binding::Local,
1190                visibility: Visibility::Default,
1191                patch: None,
1192            })
1193            .collect();
1194        let rows: Vec<_> = self.frames.iter().map(|frame| frame.rows.clone()).collect();
1195        let Ok(table) = crate::unwind::table(&funcs, &rows, &SYSV, ObjectFormat::Elf) else {
1196            return;
1197        };
1198        for frame in &self.frames {
1199            self.relocated.insert(frame.sym);
1200        }
1201        let size = table.bytes.len() as u64;
1202        self.parts.push(Part {
1203            name: ".eh_frame".to_owned(),
1204            bytes: table.bytes,
1205            size,
1206            align: 8,
1207            shape: Shape { alloc: true, bits: true, ..Shape::default() },
1208            relocs: table.relocs,
1209        });
1210    }
1211
1212    /// `.set` and its spellings, which may name each other and so are worked at until they stop
1213    /// moving rather than in the order they were written.
1214    fn resolve_sets(&mut self) -> Result<(), Trouble> {
1215        while !self.sets.is_empty() {
1216            let mut done = Vec::new();
1217            for (at, (sym, sum, line)) in self.sets.iter().enumerate() {
1218                if let Ok(residue) = self.reduce(sum) {
1219                    done.push((at, *sym, self.settled(&residue, *line)?));
1220                }
1221            }
1222            if done.is_empty() {
1223                let (sym, _, line) = &self.sets[0];
1224                let why = format!(
1225                    "'{}' is set to something that is set to it, so neither has a value",
1226                    self.syms[*sym].name
1227                );
1228                return Err(Trouble { line: *line, why });
1229            }
1230            for (_, sym, held) in &done {
1231                self.syms[*sym].at = *held;
1232            }
1233            // Backwards, so that removing one does not move the next one out from under its index.
1234            for (at, _, _) in done.iter().rev() {
1235                self.sets.remove(*at);
1236            }
1237        }
1238        Ok(())
1239    }
1240
1241    /// What one `.set` came out as.
1242    fn settled(&self, residue: &Residue, line: usize) -> Result<Held, Trouble> {
1243        match residue.left.as_slice() {
1244            [] => Ok(Held::Absolute(residue.constant as u64)),
1245            // `.set alias, real`, which is how a file gives something a second name without a
1246            // second copy of it. The two end up at the same place in the same section.
1247            [Left { coeff: 1, at: Some((part, offset)), .. }] => {
1248                Ok(Held::In { part: *part, offset: (*offset + residue.constant) as u64 })
1249            }
1250            _ => Err(Trouble {
1251                line,
1252                why: "a set to something that is neither a number nor a place in this file"
1253                    .to_owned(),
1254            }),
1255        }
1256    }
1257
1258    /// `.size`, which has to come out as a number because that is what ELF records.
1259    fn resolve_sizes(&mut self) -> Result<(), Trouble> {
1260        for (sym, sum, line) in std::mem::take(&mut self.sizes) {
1261            let residue = self.reduce(&sum).map_err(|why| Trouble { line, why })?;
1262            if !residue.left.is_empty() {
1263                let why = format!(
1264                    "the size of '{}' is not a number, and a size has to be one",
1265                    self.syms[sym].name
1266                );
1267                return Err(Trouble { line, why });
1268            }
1269            let size = self.count(residue.constant).map_err(|_| Trouble {
1270                line,
1271                why: format!("'{}' is given a negative size", self.syms[sym].name),
1272            })?;
1273            self.syms[sym].size = size;
1274        }
1275        Ok(())
1276    }
1277
1278    /// The places whose bytes name something.
1279    fn resolve_fixups(&mut self) -> Result<(), Trouble> {
1280        for fixup in std::mem::take(&mut self.fixups) {
1281            let line = fixup.line;
1282            let bad = |why: String| Trouble { line, why };
1283            // A name reached through the global offset table, or through the one entry of it a
1284            // thread-local variable has, is a relocation whatever else is true of it. What goes in
1285            // the bytes is the distance to a word the linker makes, and the linker only knows where
1286            // it put that word, so working the sum out here would answer a different question. The
1287            // sum is the one the instruction made two paragraphs up, which is the name minus the
1288            // end of the instruction, so the addend comes out the way it does for every other
1289            // rip-relative reference and is minus four.
1290            if matches!(fixup.reach, Reach::Table | Reach::Thread) {
1291                let [
1292                    Term { coeff: 1, what: What::Symbol(name) },
1293                    Term { coeff: -1, what: What::Here { at: end, .. } },
1294                ] = fixup.sum.terms.as_slice()
1295                else {
1296                    return Err(bad(
1297                        "a reach through the global offset table in something other than an \
1298                         instruction, which is not an expression this compiler writes"
1299                            .to_owned(),
1300                    ));
1301                };
1302                let kind =
1303                    if fixup.reach == Reach::Table { Reference::Got } else { Reference::Thread };
1304                self.parts[fixup.part].relocs.push(Reloc {
1305                    at: fixup.at as usize,
1306                    symbol: name.clone(),
1307                    kind,
1308                    addend: fixup.sum.constant + fixup.at as i64 - end,
1309                    after: (end - fixup.at as i64 - 4).max(0) as u8,
1310                });
1311                continue;
1312            }
1313            let residue = self.reduce(&fixup.sum).map_err(|why| Trouble { line, why })?;
1314            if fixup.reach == Reach::Value && !residue.left.is_empty() {
1315                return Err(bad(
1316                    "a number in an instruction that names something outside this section, \
1317                     which wants a relocation this compiler does not write yet"
1318                        .to_owned(),
1319                ));
1320            }
1321            let (symbol, kind, addend, after) = match residue.left.as_slice() {
1322                [] => {
1323                    // A distance a branch carries is signed and nothing else, so a byte of it
1324                    // reaches a hundred and twenty seven forwards and a hundred and twenty eight
1325                    // back. A number a directive writes down is counted both ways, because a byte
1326                    // holds two hundred and fifty five as well as minus one and a file writing
1327                    // either means it. Either way what does not fit is refused: a branch out of
1328                    // reach cut down to its low byte goes somewhere nobody wrote, and so does a
1329                    // table of offsets whose entries were quietly truncated.
1330                    let width = fixup.width as usize;
1331                    let room = 8 * width as u32;
1332                    let low = -(1i64 << (room - 1));
1333                    let high = if fixup.reach == Reach::Branch {
1334                        (1i64 << (room - 1)) - 1
1335                    } else {
1336                        (1i64 << room) - 1
1337                    };
1338                    if width < 8 && (residue.constant < low || residue.constant > high) {
1339                        return Err(bad(format!(
1340                            "{} written into {width} bytes, which does not reach it",
1341                            residue.constant
1342                        )));
1343                    }
1344                    let bytes = residue.constant.to_le_bytes();
1345                    let at = fixup.at as usize;
1346                    let part = &mut self.parts[fixup.part];
1347                    part.bytes[at..at + width].copy_from_slice(&bytes[..width]);
1348                    continue;
1349                }
1350                // The address of something, which is the whole of what a table of pointers holds.
1351                [Left { coeff: 1, what: What::Symbol(name), .. }] => {
1352                    let kind = Reference::Address { bytes: fixup.width };
1353                    (name.clone(), kind, residue.constant, 0)
1354                }
1355                // The distance from these bytes to something, which is what a position independent
1356                // table of offsets holds and what `.long foo - .` is asking for. The subtracted
1357                // side has to be these bytes or somewhere else in the same section, because a
1358                // distance to another section is not a number until the linker has laid both out.
1359                [
1360                    Left { coeff: 1, what: What::Symbol(name), .. },
1361                    Left { coeff: -1, at: Some((part, offset)), .. },
1362                ]
1363                | [
1364                    Left { coeff: -1, at: Some((part, offset)), .. },
1365                    Left { coeff: 1, what: What::Symbol(name), .. },
1366                ] => {
1367                    if *part != fixup.part {
1368                        return Err(bad(
1369                            "a distance that is subtracted from somewhere in another section"
1370                                .to_owned(),
1371                        ));
1372                    }
1373                    if fixup.width != 4 {
1374                        return Err(bad(format!(
1375                            "a distance written into {} bytes, and four is the only width a \
1376                             relocation says one at",
1377                            fixup.width
1378                        )));
1379                    }
1380                    // A linker writes `symbol + addend - here`, and what was asked for is
1381                    // `symbol + constant - there`, so the addend is the constant plus however far
1382                    // these bytes are past the place the distance is counted from. That is zero
1383                    // for `.long foo - .`, which is why the two are easy to write down the wrong
1384                    // way round, and it is minus four for a call, whose four bytes are counted
1385                    // from the end of the instruction they are the last of.
1386                    let addend = residue.constant + fixup.at as i64 - offset;
1387                    let kind = if fixup.reach == Reach::Branch {
1388                        Reference::Call
1389                    } else {
1390                        Reference::Data
1391                    };
1392                    // The same distance said the other way, for the format that wants it apart
1393                    // from the addend rather than folded into it. See `rucc_object::Reloc`.
1394                    let after = (offset - fixup.at as i64 - 4).max(0);
1395                    (name.clone(), kind, addend, after as u8)
1396                }
1397                [Left { coeff: 1, what: What::Here { .. }, .. }] => {
1398                    return Err(bad(
1399                        "the address of these bytes themselves, which has no symbol to be \
1400                         relocated against"
1401                            .to_owned(),
1402                    ));
1403                }
1404                _ => {
1405                    return Err(bad(
1406                        "an expression that does not come out as a number, an address, or a \
1407                         distance, and those are what a relocation can say"
1408                            .to_owned(),
1409                    ));
1410                }
1411            };
1412            // A numbered local label that got this far was never written, which for `1f` is the one
1413            // way of getting it wrong that nothing above can see: the file said go to the next `1:`
1414            // and there was no next one. It is not a name, so there is nothing to ask the linker.
1415            if let Some(&sym) = self.known.get(&symbol) {
1416                if self.syms[sym].numbered && self.syms[sym].at != Held::Undefined {
1417                    self.relocated.insert(sym);
1418                } else if self.syms[sym].numbered {
1419                    let number = symbol.split('\u{1}').next().unwrap_or(&symbol);
1420                    return Err(bad(format!(
1421                        "'{number}f' goes on to a '{number}:' and there is none below it"
1422                    )));
1423                }
1424            }
1425            if matches!(kind, Reference::Address { bytes } if bytes != 4 && bytes != 8) {
1426                return Err(bad(format!(
1427                    "the address of '{symbol}' written into {} bytes, and this machine relocates \
1428                     an address at four or eight",
1429                    fixup.width
1430                )));
1431            }
1432            self.parts[fixup.part].relocs.push(Reloc {
1433                at: fixup.at as usize,
1434                symbol,
1435                kind,
1436                addend,
1437                after,
1438            });
1439        }
1440        Ok(())
1441    }
1442
1443    /// Take an expression down to a constant and whatever names would not cancel.
1444    ///
1445    /// The algebra is the ordinary one and worth saying once. A sum of terms over the same section
1446    /// is `sum(c * x)`, every `x` is that section's address plus a known offset, and the section's
1447    /// address is the only unknown in it. Rewriting each term as its distance from one chosen term
1448    /// in the group leaves `sum(c * (offset - chosen))`, which is a number, plus `sum(c)` times the
1449    /// chosen one. So a group whose coefficients add to zero disappears into the constant however
1450    /// many terms it had, which is what makes `.-foo` a number.
1451    fn reduce(&self, sum: &Sum) -> Result<Residue, String> {
1452        let mut constant = sum.constant;
1453        let mut placed: BTreeMap<usize, Vec<(i64, What, i64)>> = BTreeMap::new();
1454        let mut outside: Vec<(i64, String)> = Vec::new();
1455        for term in &sum.terms {
1456            match &term.what {
1457                What::Here { part, at } => {
1458                    placed.entry(*part).or_default().push((term.coeff, term.what.clone(), *at));
1459                }
1460                What::Symbol(name) => {
1461                    let Some(&at) = self.known.get(name) else {
1462                        return Err(format!("'{name}' is named and never said"));
1463                    };
1464                    match self.syms[at].at {
1465                        Held::Absolute(value) => constant += term.coeff * value as i64,
1466                        Held::In { part, offset } => placed.entry(part).or_default().push((
1467                            term.coeff,
1468                            term.what.clone(),
1469                            offset as i64,
1470                        )),
1471                        // Not defined here and not a place here, so nothing about it cancels with
1472                        // anything and the linker is the one that knows.
1473                        Held::Undefined | Held::Common { .. } => {
1474                            if !self.sets.iter().any(|(sym, _, _)| *sym == at) {
1475                                outside.push((term.coeff, name.clone()));
1476                            } else {
1477                                return Err(format!("'{name}' is not worked out yet"));
1478                            }
1479                        }
1480                    }
1481                }
1482            }
1483        }
1484        let mut left: Vec<Left> = Vec::new();
1485        for (part, terms) in placed {
1486            let (_, chosen, base) = terms[0].clone();
1487            let mut net = 0;
1488            for (coeff, _, offset) in &terms {
1489                net += coeff;
1490                constant += coeff * (offset - base);
1491            }
1492            if net != 0 {
1493                left.push(Left { coeff: net, what: chosen, at: Some((part, base)) });
1494            }
1495        }
1496        let mut together: BTreeMap<String, i64> = BTreeMap::new();
1497        for (coeff, name) in outside {
1498            *together.entry(name).or_default() += coeff;
1499        }
1500        for (name, coeff) in together {
1501            if coeff != 0 {
1502                left.push(Left { coeff, what: What::Symbol(name), at: None });
1503            }
1504        }
1505        Ok(Residue { constant, left })
1506    }
1507}
1508
1509/// What an expression came out as: a number, and the names that would not cancel.
1510#[derive(Debug, Clone)]
1511struct Residue {
1512    constant: i64,
1513    left: Vec<Left>,
1514}
1515
1516/// One name an expression would not get rid of.
1517#[derive(Debug, Clone)]
1518struct Left {
1519    /// How many times it is counted, which is one for everything a relocation can say.
1520    coeff: i64,
1521    /// Which name it is, which is what a relocation points at.
1522    what: What,
1523    /// Which section it is in and how far into it, when this file is the one that knows. Nothing
1524    /// for a name the linker has to find, which has no place here to be at.
1525    at: Option<(usize, i64)>,
1526}
1527
1528/// An expression, kept as a sum so that it survives until the names in it have values.
1529#[derive(Debug, Clone, Default, PartialEq, Eq)]
1530struct Sum {
1531    constant: i64,
1532    terms: Vec<Term>,
1533}
1534
1535/// One name in one, and how many times it is counted.
1536#[derive(Debug, Clone, PartialEq, Eq)]
1537struct Term {
1538    coeff: i64,
1539    what: What,
1540}
1541
1542/// What a term is about.
1543#[derive(Debug, Clone, PartialEq, Eq)]
1544enum What {
1545    /// A name, which may or may not turn out to be in this file.
1546    Symbol(String),
1547    /// `.`, which is a place and never a name. Worked out as the expression is parsed, because it
1548    /// means where the file had got to when it was written and not where it got to in the end.
1549    Here { part: usize, at: i64 },
1550}
1551
1552impl Sum {
1553    /// A plain number, and nothing for one that names something.
1554    fn flat(&self) -> Option<i64> {
1555        self.terms.is_empty().then_some(self.constant)
1556    }
1557
1558    /// One name on its own.
1559    fn of(what: What) -> Sum {
1560        Sum { constant: 0, terms: vec![Term { coeff: 1, what }] }
1561    }
1562
1563    /// A number on its own.
1564    fn just(value: i64) -> Sum {
1565        Sum { constant: value, terms: Vec::new() }
1566    }
1567
1568    /// Two of them added, which is the one operation that always works.
1569    fn plus(mut self, other: Sum) -> Sum {
1570        self.constant = self.constant.wrapping_add(other.constant);
1571        self.terms.extend(other.terms);
1572        self
1573    }
1574
1575    /// One of them counted backwards.
1576    fn minus(self) -> Sum {
1577        Sum {
1578            constant: self.constant.wrapping_neg(),
1579            terms: self
1580                .terms
1581                .into_iter()
1582                .map(|term| Term { coeff: term.coeff.wrapping_neg(), what: term.what })
1583                .collect(),
1584        }
1585    }
1586
1587    /// One of them counted a number of times, which only means anything when the number is one.
1588    fn times(self, factor: i64) -> Sum {
1589        Sum {
1590            constant: self.constant.wrapping_mul(factor),
1591            terms: self
1592                .terms
1593                .into_iter()
1594                .map(|term| Term { coeff: term.coeff.wrapping_mul(factor), what: term.what })
1595                .collect(),
1596        }
1597    }
1598}
1599
1600/// One expression, being read.
1601struct Parser<'a> {
1602    text: &'a str,
1603    at: usize,
1604    here: (usize, i64),
1605}
1606
1607impl Parser<'_> {
1608    /// The whole of it, and nothing left over.
1609    fn whole(&mut self) -> Result<Sum, String> {
1610        let sum = self.bitwise()?;
1611        self.space();
1612        if self.at < self.text.len() {
1613            return Err(format!(
1614                "'{}' is left over at the end of an expression",
1615                &self.text[self.at..]
1616            ));
1617        }
1618        Ok(sum)
1619    }
1620
1621    /// The loosest binding of them, which is why it is the outermost.
1622    fn bitwise(&mut self) -> Result<Sum, String> {
1623        let mut left = self.shift()?;
1624        loop {
1625            self.space();
1626            let Some(op) = self.one_of(&["|", "^", "&"]) else { return Ok(left) };
1627            let right = self.shift()?;
1628            left = self.arithmetic(left, right, op)?;
1629        }
1630    }
1631
1632    /// Shifts, which bind tighter than the bitwise operators and looser than addition.
1633    fn shift(&mut self) -> Result<Sum, String> {
1634        let mut left = self.sum()?;
1635        loop {
1636            self.space();
1637            let Some(op) = self.one_of(&["<<", ">>"]) else { return Ok(left) };
1638            let right = self.sum()?;
1639            left = self.arithmetic(left, right, op)?;
1640        }
1641    }
1642
1643    /// Addition and subtraction, which are the two that keep working when names are involved.
1644    fn sum(&mut self) -> Result<Sum, String> {
1645        let mut left = self.product()?;
1646        loop {
1647            self.space();
1648            // Not the start of `<<` or `>>`, and not a `-` that belongs to nothing.
1649            let Some(op) = self.one_of(&["+", "-"]) else { return Ok(left) };
1650            let right = self.product()?;
1651            left = if op == "+" { left.plus(right) } else { left.plus(right.minus()) };
1652        }
1653    }
1654
1655    /// Multiplication and the two that go with it.
1656    fn product(&mut self) -> Result<Sum, String> {
1657        let mut left = self.unary()?;
1658        loop {
1659            self.space();
1660            let Some(op) = self.one_of(&["*", "/", "%"]) else { return Ok(left) };
1661            let right = self.unary()?;
1662            // A name times a number is still a name counted that many times, which is worth keeping
1663            // because `foo*2 - foo` is a thing a macro produces. Everything else here wants two
1664            // numbers, and a name in one of them is a mistake rather than something to guess at.
1665            left = match (op, left.flat(), right.flat()) {
1666                ("*", _, Some(factor)) => left.times(factor),
1667                ("*", Some(factor), _) => right.times(factor),
1668                (_, Some(a), Some(b)) => Sum::just(self.arithmetic_number(a, b, op)?),
1669                _ => return Err(format!("'{op}' of something that names a symbol")),
1670            };
1671        }
1672    }
1673
1674    /// A sign or a complement in front of something.
1675    fn unary(&mut self) -> Result<Sum, String> {
1676        self.space();
1677        if self.eat("-") {
1678            return Ok(self.unary()?.minus());
1679        }
1680        if self.eat("+") {
1681            return self.unary();
1682        }
1683        if self.eat("~") {
1684            let inner = self.unary()?;
1685            let value = inner
1686                .flat()
1687                .ok_or_else(|| "a complement of something that names a symbol".to_owned())?;
1688            return Ok(Sum::just(!value));
1689        }
1690        if self.eat("!") {
1691            let inner = self.unary()?;
1692            let value = inner
1693                .flat()
1694                .ok_or_else(|| "a negation of something that names a symbol".to_owned())?;
1695            return Ok(Sum::just(i64::from(value == 0)));
1696        }
1697        self.primary()
1698    }
1699
1700    /// A number, a name, a character, `.`, or the whole thing again in brackets.
1701    fn primary(&mut self) -> Result<Sum, String> {
1702        self.space();
1703        let rest = &self.text[self.at..];
1704        if rest.is_empty() {
1705            return Err("an expression that stops before it says anything".to_owned());
1706        }
1707        if self.eat("(") {
1708            let inner = self.bitwise()?;
1709            self.space();
1710            if !self.eat(")") {
1711                return Err("a bracket that was opened and never closed".to_owned());
1712            }
1713            return Ok(inner);
1714        }
1715        let first = rest.as_bytes()[0];
1716        if first == b'\'' {
1717            return self.character();
1718        }
1719        if first.is_ascii_digit() {
1720            // `1b` and `2f`, which are a numbered label above and below rather than a number.
1721            // Told apart from `0b1010` by what comes after the letter, which ends a label and
1722            // carries on a binary number.
1723            let end = rest.find(|ch: char| !ch.is_ascii_digit()).unwrap_or(rest.len());
1724            let bytes = rest.as_bytes();
1725            if matches!(bytes.get(end), Some(b'b' | b'f'))
1726                && !bytes.get(end + 1).is_some_and(|byte| carries_on(*byte))
1727            {
1728                self.at += end + 1;
1729                return Ok(Sum::of(What::Symbol(rest[..=end].to_owned())));
1730            }
1731            return self.digits();
1732        }
1733        if starts(first) {
1734            let name = self.word();
1735            // `.` on its own is where the file has got to, and `.L1` is a name that starts with one.
1736            if name == "." {
1737                let (part, at) = self.here;
1738                return Ok(Sum::of(What::Here { part, at }));
1739            }
1740            // What follows an `@` says which table the linker should reach the name through, and
1741            // none of them is a thing a directive can hold, so one here is a file that wants the
1742            // instruction assembler rather than this.
1743            if self.text[self.at..].starts_with('@') {
1744                return Err(format!(
1745                    "'{name}@' asks for a relocation only an instruction can carry"
1746                ));
1747            }
1748            return Ok(Sum::of(What::Symbol(name)));
1749        }
1750        Err(format!("'{rest}' is not the start of an expression"))
1751    }
1752
1753    /// A number in any of the bases a file may write one in.
1754    fn digits(&mut self) -> Result<Sum, String> {
1755        let rest = &self.text[self.at..];
1756        let (radix, skip) = if rest.starts_with("0x") || rest.starts_with("0X") {
1757            (16, 2)
1758        } else if rest.starts_with("0b") || rest.starts_with("0B") {
1759            (2, 2)
1760        } else if rest.len() > 1 && rest.starts_with('0') {
1761            (8, 1)
1762        } else {
1763            (10, 0)
1764        };
1765        let body = &rest[skip..];
1766        let end = body.find(|ch: char| !ch.is_digit(radix) && ch != '_').unwrap_or(body.len());
1767        if end == 0 {
1768            return Err(format!("'{rest}' starts like a number and is not one"));
1769        }
1770        let text: String = body[..end].chars().filter(|ch| *ch != '_').collect();
1771        // Wrapping round rather than refusing, because a file writes `0xffffffffffffffff` for a word
1772        // of ones and means the bits rather than the value.
1773        let value = u64::from_str_radix(&text, radix)
1774            .map_err(|_| format!("'{text}' does not fit in sixty four bits"))?;
1775        self.at += skip + end;
1776        // A suffix, which a file written for more than one assembler carries and which says nothing
1777        // this needs: the width is the directive's business here.
1778        while self.text[self.at..].starts_with(['u', 'U', 'l', 'L']) {
1779            self.at += 1;
1780        }
1781        Ok(Sum::just(value as i64))
1782    }
1783
1784    /// `'a'` or `'a`, which are both a character and both what gas takes.
1785    fn character(&mut self) -> Result<Sum, String> {
1786        self.at += 1;
1787        let rest = &self.text[self.at..];
1788        let mut chars = rest.chars();
1789        let Some(first) = chars.next() else {
1790            return Err("a quote with no character after it".to_owned());
1791        };
1792        let (value, used) = if first == '\\' {
1793            let (value, used) = escape(&rest[1..])?;
1794            (value, used + 1)
1795        } else {
1796            (first as u8, first.len_utf8())
1797        };
1798        self.at += used;
1799        // The closing quote is optional in gas and a file written by hand often leaves it out, so
1800        // one is taken when it is there and not asked for when it is not.
1801        if self.text[self.at..].starts_with('\'') {
1802            self.at += 1;
1803        }
1804        Ok(Sum::just(i64::from(value)))
1805    }
1806
1807    /// An operator on two things that both have to be numbers.
1808    fn arithmetic(&self, left: Sum, right: Sum, op: &str) -> Result<Sum, String> {
1809        let (Some(a), Some(b)) = (left.flat(), right.flat()) else {
1810            return Err(format!("'{op}' of something that names a symbol"));
1811        };
1812        Ok(Sum::just(self.arithmetic_number(a, b, op)?))
1813    }
1814
1815    /// The same, once both are numbers.
1816    fn arithmetic_number(&self, a: i64, b: i64, op: &str) -> Result<i64, String> {
1817        Ok(match op {
1818            "|" => a | b,
1819            "^" => a ^ b,
1820            "&" => a & b,
1821            "<<" => a.wrapping_shl(shift(b)?),
1822            ">>" => a.wrapping_shr(shift(b)?),
1823            "*" => a.wrapping_mul(b),
1824            "/" if b == 0 => return Err("a division by zero".to_owned()),
1825            "%" if b == 0 => return Err("a remainder of a division by zero".to_owned()),
1826            "/" => a.wrapping_div(b),
1827            "%" => a.wrapping_rem(b),
1828            _ => return Err(format!("'{op}' is not an operator this compiler knows")),
1829        })
1830    }
1831
1832    /// One name, as far as it runs.
1833    fn word(&mut self) -> String {
1834        let body = &self.text[self.at..];
1835        let end = body.find(|ch: char| !carries_on(ch as u8)).unwrap_or(body.len());
1836        let word = body[..end].to_owned();
1837        self.at += end;
1838        word
1839    }
1840
1841    /// Whichever of these is next, and nothing if none of them is.
1842    ///
1843    /// In the order given, which matters: `<<` has to be looked for in front of anything that starts
1844    /// with `<`, or the second half of it is left behind as an operator of its own.
1845    fn one_of(&mut self, ops: &[&'static str]) -> Option<&'static str> {
1846        for op in ops {
1847            if self.text[self.at..].starts_with(op) {
1848                self.at += op.len();
1849                return Some(op);
1850            }
1851        }
1852        None
1853    }
1854
1855    /// One exact string, if it is next.
1856    fn eat(&mut self, what: &str) -> bool {
1857        if self.text[self.at..].starts_with(what) {
1858            self.at += what.len();
1859            return true;
1860        }
1861        false
1862    }
1863
1864    /// Past any blanks.
1865    fn space(&mut self) {
1866        while self.text[self.at..].starts_with([' ', '\t']) {
1867            self.at += 1;
1868        }
1869    }
1870}
1871
1872impl Reader {
1873    /// A quoted string, as its bytes.
1874    fn string(&self, text: &str) -> Result<Vec<u8>, Trouble> {
1875        let bad = |why: &str| Trouble { line: self.line, why: why.to_owned() };
1876        let body = text
1877            .strip_prefix('"')
1878            .and_then(|rest| rest.strip_suffix('"'))
1879            .ok_or_else(|| bad("a string directive whose operand is not in quotes"))?;
1880        let mut out = Vec::with_capacity(body.len());
1881        let mut at = 0;
1882        while at < body.len() {
1883            let rest = &body[at..];
1884            let first = rest.as_bytes()[0];
1885            if first == b'\\' {
1886                let (value, used) =
1887                    escape(&rest[1..]).map_err(|why| Trouble { line: self.line, why })?;
1888                out.push(value);
1889                at += used + 1;
1890                continue;
1891            }
1892            let ch = rest.chars().next().unwrap_or('\0');
1893            let mut buffer = [0u8; 4];
1894            out.extend_from_slice(ch.encode_utf8(&mut buffer).as_bytes());
1895            at += ch.len_utf8();
1896        }
1897        Ok(out)
1898    }
1899}
1900
1901/// How far to shift by, which has to be a count and not a number that happens to be negative.
1902fn shift(by: i64) -> Result<u32, String> {
1903    u32::try_from(by).map_err(|_| "a shift by a negative amount".to_owned())
1904}
1905
1906/// What one backslash and what follows it mean, and how much of the text that took.
1907///
1908/// The count is of what came after the backslash, so a caller adds one for the backslash itself.
1909fn escape(rest: &str) -> Result<(u8, usize), String> {
1910    let bytes = rest.as_bytes();
1911    let Some(&first) = bytes.first() else {
1912        return Err("a backslash with nothing after it".to_owned());
1913    };
1914    let simple = match first {
1915        b'n' => Some(b'\n'),
1916        b't' => Some(b'\t'),
1917        b'r' => Some(b'\r'),
1918        b'f' => Some(0x0c),
1919        b'b' => Some(0x08),
1920        b'v' => Some(0x0b),
1921        b'a' => Some(0x07),
1922        b'e' => Some(0x1b),
1923        b'\\' => Some(b'\\'),
1924        b'"' => Some(b'"'),
1925        b'\'' => Some(b'\''),
1926        _ => None,
1927    };
1928    if let Some(value) = simple {
1929        return Ok((value, 1));
1930    }
1931    if first == b'x' || first == b'X' {
1932        let end = bytes[1..]
1933            .iter()
1934            .position(|byte| !byte.is_ascii_hexdigit())
1935            .map_or(bytes.len(), |at| at + 1);
1936        if end == 1 {
1937            return Err("a hex escape with no digits in it".to_owned());
1938        }
1939        // Only the last two digits, which is what gas keeps: the escape is one byte however many
1940        // digits were written.
1941        let text = &rest[1..end];
1942        let text = &text[text.len().saturating_sub(2)..];
1943        let value =
1944            u8::from_str_radix(text, 16).map_err(|_| "a hex escape that is not one".to_owned())?;
1945        return Ok((value, end));
1946    }
1947    if (b'0'..=b'7').contains(&first) {
1948        let end = bytes.iter().take(3).take_while(|byte| (b'0'..=b'7').contains(byte)).count();
1949        let value = u32::from_str_radix(&rest[..end], 8)
1950            .map_err(|_| "an octal escape that is not one".to_owned())?;
1951        return Ok(((value & 0xff) as u8, end));
1952    }
1953    // gas takes an unknown escape as the character itself and warns. Refused here, because the two
1954    // things it is likely to be are a typo and a file meant for another assembler, and both are
1955    // better said than guessed.
1956    Err(format!("'\\{}' is not an escape this compiler knows", first as char))
1957}
1958
1959/// The name of the label at the start of this text, if it starts with one.
1960///
1961/// A colon after a name and nothing else. `.L1:` is one, so is `foo:`, and so is `1:`, which is a
1962/// numbered local label and is a place rather than a name: it may be written as many times in a file
1963/// as the file likes and what refers to it is `1b` for the last one above and `1f` for the next one
1964/// below.
1965fn labelled(text: &str) -> Option<String> {
1966    let bytes = text.as_bytes();
1967    if bytes.is_empty() || !(starts(bytes[0]) || bytes[0].is_ascii_digit()) {
1968        return None;
1969    }
1970    let end = text.find(|ch: char| !carries_on(ch as u8))?;
1971    // Not `::`, which is a different thing in gas, and not a bare name with nothing after it.
1972    if bytes.get(end) != Some(&b':') || bytes.get(end + 1) == Some(&b':') {
1973        return None;
1974    }
1975    Some(text[..end].to_owned())
1976}
1977
1978/// `name = value`, as the name and the value, when the statement is one.
1979///
1980/// Not `==`, which is a comparison, and not a label, which was taken off before this is asked.
1981fn assigned(text: &str) -> Option<(&str, &str)> {
1982    let bytes = text.as_bytes();
1983    if bytes.is_empty() || !starts(bytes[0]) {
1984        return None;
1985    }
1986    let end = text.find(|ch: char| !carries_on(ch as u8)).unwrap_or(text.len());
1987    let rest = text[end..].trim_start().strip_prefix('=')?;
1988    if rest.starts_with('=') {
1989        return None;
1990    }
1991    Some((&text[..end], rest.trim()))
1992}
1993
1994/// Whether a name may start with this.
1995fn starts(byte: u8) -> bool {
1996    byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'.' | b'$')
1997}
1998
1999/// Whether a name may go on with this.
2000fn carries_on(byte: u8) -> bool {
2001    starts(byte) || byte.is_ascii_digit()
2002}
2003
2004/// The name a numbered local label is kept under while the file is being read.
2005///
2006/// A file writes `1:` over and over and each one is a different place, so what goes in the table has
2007/// to say which of them this is. The byte in the middle is one no name in a source file can hold, so
2008/// nothing a file writes its own way can collide with one of these, and none of them reaches the
2009/// symbol table at the end.
2010fn counted(number: &str, nth: usize) -> String {
2011    format!("{number}\u{1}{nth}")
2012}
2013
2014/// The text with its quotes taken off, if it had any.
2015fn unquoted(text: &str) -> String {
2016    text.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')).unwrap_or(text).to_owned()
2017}
2018
2019/// Split on a separator that is outside every string and every bracket.
2020///
2021/// The brackets matter as much as the quotes: `.long (1 + 2), 3` is two operands and splitting on
2022/// every comma would be right here and wrong the moment one turns up inside brackets.
2023pub(crate) fn split(text: &str, on: char) -> Vec<String> {
2024    let mut out = Vec::new();
2025    let mut piece = String::new();
2026    let mut depth = 0i32;
2027    let mut quote = None;
2028    let mut chars = text.chars();
2029    while let Some(ch) = chars.next() {
2030        if let Some(mark) = quote {
2031            piece.push(ch);
2032            if ch == '\\' {
2033                if let Some(next) = chars.next() {
2034                    piece.push(next);
2035                }
2036                continue;
2037            }
2038            if ch == mark {
2039                quote = None;
2040            }
2041            continue;
2042        }
2043        match ch {
2044            '"' => {
2045                quote = Some(ch);
2046                piece.push(ch);
2047            }
2048            '(' => {
2049                depth += 1;
2050                piece.push(ch);
2051            }
2052            ')' => {
2053                depth -= 1;
2054                piece.push(ch);
2055            }
2056            _ if ch == on && depth == 0 => {
2057                out.push(std::mem::take(&mut piece));
2058            }
2059            _ => piece.push(ch),
2060        }
2061    }
2062    if !piece.trim().is_empty() || !out.is_empty() {
2063        out.push(piece);
2064    }
2065    out.into_iter().map(|piece| piece.trim().to_owned()).collect()
2066}
2067
2068/// A repeat prefix and the string instruction behind it, as the one mnemonic the encoder knows the
2069/// pair by, and what is left of the line after the two.
2070///
2071/// Five spellings for two bytes. `rep`, `repe` and `repz` are one byte, which is spelled `repe` in
2072/// front of a scan or a comparison and `rep` in front of anything else, and `repne` and `repnz` are
2073/// the other. A prefix in front of anything that is not a string instruction is left alone here,
2074/// so it reaches the encoder as the word it was and is refused there as a mnemonic nobody knows.
2075fn repeated<'a>(word: &str, rest: &'a str) -> Option<(String, &'a str)> {
2076    let unequal = match word {
2077        "rep" | "repe" | "repz" => false,
2078        "repne" | "repnz" => true,
2079        _ => return None,
2080    };
2081    let (next, after) = match rest.find(char::is_whitespace) {
2082        Some(cut) => (&rest[..cut], rest[cut..].trim()),
2083        None => (rest, ""),
2084    };
2085    let string = next.len() == 5 && next.ends_with(['b', 'w', 'l', 'q']);
2086    let which = if string { &next[..4] } else { "" };
2087    let prefix = match (unequal, which) {
2088        (false, "movs" | "stos") => "rep",
2089        (false, "scas" | "cmps") => "repe",
2090        (true, "scas" | "cmps") => "repne",
2091        _ => return None,
2092    };
2093    Some((format!("{prefix} {next}"), after))
2094}
2095
2096#[cfg(test)]
2097mod tests {
2098    use super::*;
2099
2100    use rucc_object::Reference;
2101
2102    /// The file, read, with a failure reported as a panic naming the line it was on.
2103    fn assembled(text: &str) -> Assembled {
2104        match read(text) {
2105            Ok(assembled) => assembled,
2106            Err(trouble) => panic!("line {}: {}", trouble.line, trouble.why),
2107        }
2108    }
2109
2110    /// The bytes of the section of that name.
2111    fn bytes(assembled: &Assembled, name: &str) -> Vec<u8> {
2112        let part = assembled
2113            .parts
2114            .iter()
2115            .find(|part| part.name == name)
2116            .unwrap_or_else(|| panic!("there is no section called '{name}'"));
2117        part.bytes.clone()
2118    }
2119
2120    /// The name of that name.
2121    fn name<'a>(assembled: &'a Assembled, want: &str) -> &'a Name {
2122        assembled
2123            .names
2124            .iter()
2125            .find(|name| name.name == want)
2126            .unwrap_or_else(|| panic!("there is no name called '{want}'"))
2127    }
2128
2129    /// What a file this could not read said about it.
2130    fn refused(text: &str) -> Trouble {
2131        read(text).err().unwrap_or_else(|| panic!("this was read and should not have been"))
2132    }
2133
2134    #[test]
2135    fn a_repeat_prefix_is_read_with_the_string_instruction_behind_it() {
2136        let assembled =
2137            assembled("\t.text\n\trep movsl\n\trepnz scasb\n\trepz cmpsb\n\trep stosq\n");
2138        assert_eq!(
2139            bytes(&assembled, ".text"),
2140            [0xF3, 0xA5, 0xF2, 0xAE, 0xF3, 0xA6, 0xF3, 0x48, 0xAB]
2141        );
2142    }
2143
2144    /// A numbered local label, which is a place a file may write as often as it likes.
2145    ///
2146    /// `1:` three times is three places and the jumps between them say which by counting, so `1b`
2147    /// is the one above and `1f` is the one below. None of the three is a name, which is why the
2148    /// symbol table at the end holds the one thing this file actually called something.
2149    #[test]
2150    fn a_number_is_a_label_a_file_may_write_as_many_times_as_it_likes() {
2151        let out =
2152            assembled("\t.text\nfoo:\n1:\tnop\n\tjmp 1b\n1:\tnop\n\tjmp 1f\n\tnop\n1:\tret\n");
2153        let text = bytes(&out, ".text");
2154        // `nop`, then a jump back over both of them, then `nop`, then a jump forward over the
2155        // `nop` behind it, then that `nop`, then `ret`.
2156        assert_eq!(
2157            text,
2158            vec![0x90, 0xe9, 0xfa, 0xff, 0xff, 0xff, 0x90, 0xe9, 0x01, 0, 0, 0, 0x90, 0xc3]
2159        );
2160        assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2161        // One name, and it is the one the file wrote as a name.
2162        let written: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2163        assert_eq!(written, vec!["foo"]);
2164    }
2165
2166    #[test]
2167    fn a_numbered_label_with_nothing_on_the_side_it_names_is_refused() {
2168        let back = refused("\t.text\n\tjmp 1b\n1:\tret\n");
2169        assert!(back.why.contains("none above it"), "{}", back.why);
2170        let forward = refused("\t.text\n1:\tnop\n\tjmp 1f\n\tret\n");
2171        assert!(forward.why.contains("none below it"), "{}", forward.why);
2172    }
2173
2174    /// A prefix written on a line of its own, which is how gas takes one and how GMP writes them.
2175    ///
2176    /// `rep;bsf %rdx, %rcx` is two statements on one line, and the first of them is an instruction
2177    /// with no operands whose whole encoding is the byte that goes in front of the next one. The
2178    /// reader needs nothing for this beyond the rows, because a statement is already a statement
2179    /// whether a semicolon or a newline ended the one before it.
2180    #[test]
2181    fn a_prefix_is_a_statement_of_its_own_and_the_byte_goes_in_front() {
2182        let out = assembled("\t.text\n\trep;bsf %rdx, %rcx\n");
2183        assert_eq!(bytes(&out, ".text"), vec![0xf3, 0x48, 0x0f, 0xbc, 0xca]);
2184        let split = assembled("\t.text\n\trep\n\tmovsq\n");
2185        assert_eq!(bytes(&split, ".text"), vec![0xf3, 0x48, 0xa5]);
2186        let lock = assembled("\t.text\n\tlock;incl (%rdi)\n");
2187        assert_eq!(bytes(&lock, ".text"), vec![0xf0, 0xff, 0x07]);
2188    }
2189
2190    /// A name reached through the global offset table, which is a relocation however near it is.
2191    ///
2192    /// What the four bytes hold is the distance to a slot the linker makes, so there is nothing for
2193    /// the reader to work out even when the name is defined three lines further down. That is the
2194    /// difference from a plain rip-relative reference, which cancels to a number whenever both ends
2195    /// are in the same section.
2196    #[test]
2197    fn a_reach_through_the_table_is_a_relocation_even_when_this_file_defines_the_name() {
2198        let out = assembled("\t.text\n\tmovq table@GOTPCREL(%rip), %rdx\ntable:\n\t.quad 0\n");
2199        let relocs = &out.parts[0].relocs;
2200        assert_eq!(relocs.len(), 1);
2201        assert_eq!(relocs[0].symbol, "table");
2202        assert_eq!(relocs[0].kind, Reference::Got);
2203        // The four bytes are the last four of the instruction and the machine counts them from the
2204        // end of it, so the addend is minus four.
2205        assert_eq!(relocs[0].addend, -4);
2206        let out = assembled("\t.text\n\tmovq counter@GOTTPOFF(%rip), %rax\n");
2207        assert_eq!(out.parts[0].relocs[0].kind, Reference::Thread);
2208    }
2209
2210    /// A name reached with something added to it, which is a table indexed by a value that does not
2211    /// start at zero.
2212    ///
2213    /// The number belongs to the linker along with the name, so it lands in the addend rather than
2214    /// in the bytes, and the minus four the machine already wanted is on top of it.
2215    #[test]
2216    fn a_number_beside_a_name_in_a_displacement_is_part_of_what_the_linker_is_asked_for() {
2217        let out = assembled("\t.text\n\tleaq -512+table(%rip), %r8\n\t.globl table\n");
2218        let relocs = &out.parts[0].relocs;
2219        assert_eq!(relocs.len(), 1);
2220        assert_eq!(relocs[0].symbol, "table");
2221        assert_eq!(relocs[0].addend, -516);
2222        // And the name is the name, rather than the whole of what was written in front of the
2223        // bracket, which is what a symbol table full of things nothing defines used to look like.
2224        let named: Vec<&str> = out.names.iter().map(|name| name.name.as_str()).collect();
2225        assert_eq!(named, ["table"]);
2226    }
2227
2228    #[test]
2229    fn a_name_taken_away_from_something_in_a_displacement_is_refused() {
2230        // There is no relocation for the distance back from something, so this is a mistake rather
2231        // than a thing to hand on to the linker.
2232        refused("\t.text\n\tleaq 512-table(%rip), %r8\n");
2233    }
2234
2235    #[test]
2236    fn the_probe_gmp_writes() {
2237        // The case the whole crate exists for. Four lines, no instruction, and the answer configure
2238        // is after is the value of the symbol: four, because the `.long` in front of it took four
2239        // bytes. It seds that number out of `nm` and writes it into a header.
2240        let out = assembled("\t.data\n\t.globl foo\n\t.long 0\nfoo:\n\t.byte 0\n");
2241        assert_eq!(bytes(&out, ".data"), vec![0, 0, 0, 0, 0]);
2242        let foo = name(&out, "foo");
2243        assert_eq!(foo.at, Held::In { part: 0, offset: 4 });
2244        assert_eq!(foo.binding, Binding::Global);
2245    }
2246
2247    #[test]
2248    fn every_width_of_number_is_the_bytes_it_says_it_is() {
2249        let out = assembled(
2250            "\t.data\n\t.byte 1\n\t.short 2\n\t.long 3\n\t.quad 4\n\t.byte 0x7f, 0377, 'a', '\\n'\n",
2251        );
2252        let mut want = vec![1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0];
2253        want.extend_from_slice(&[0x7f, 0xff, b'a', b'\n']);
2254        assert_eq!(bytes(&out, ".data"), want);
2255    }
2256
2257    #[test]
2258    fn a_number_that_is_negative_is_written_as_the_width_asked_for() {
2259        // Two's complement in that many bytes, not a refusal, because `.short -1` is how a file
2260        // says two bytes of ones and every table of small offsets somewhere has one in it.
2261        let out = assembled("\t.data\n\t.short -1\n\t.long -2\n");
2262        assert_eq!(bytes(&out, ".data"), vec![0xff, 0xff, 0xfe, 0xff, 0xff, 0xff]);
2263    }
2264
2265    #[test]
2266    fn the_three_kinds_of_string_differ_only_in_the_zero_on_the_end() {
2267        let out = assembled("\t.data\n\t.ascii \"ab\"\n\t.asciz \"cd\"\n\t.string \"e\\tf\"\n");
2268        assert_eq!(bytes(&out, ".data"), b"abcd\0e\tf\0".to_vec());
2269    }
2270
2271    #[test]
2272    fn space_and_fill_put_that_many_bytes_there() {
2273        let out = assembled("\t.data\n\t.byte 1\n\t.zero 3\n\t.space 2, 0x41\n\t.fill 2, 1, 7\n");
2274        assert_eq!(bytes(&out, ".data"), vec![1, 0, 0, 0, 0x41, 0x41, 7, 7]);
2275    }
2276
2277    #[test]
2278    fn aligning_moves_on_to_the_boundary_and_no_further() {
2279        // `.align` on this machine is a byte count and `.p2align` is a power of two, which is the
2280        // one thing about them somebody porting a file from another assembler gets wrong.
2281        let out = assembled("\t.data\n\t.byte 1\n\t.align 8\n\t.byte 2\n\t.p2align 4\n\t.byte 3\n");
2282        let data = bytes(&out, ".data");
2283        assert_eq!(data.len(), 17);
2284        assert_eq!(data[0], 1);
2285        assert_eq!(data[8], 2);
2286        assert_eq!(data[16], 3);
2287        assert_eq!(out.parts[0].align, 16, "the section has to start where the widest ask does");
2288    }
2289
2290    #[test]
2291    fn a_section_that_holds_no_bytes_counts_them_rather_than_carrying_them() {
2292        let out = assembled("\t.bss\n\t.globl room\nroom:\n\t.zero 4096\n");
2293        let part = &out.parts[0];
2294        assert_eq!(part.name, ".bss");
2295        assert_eq!(part.size, 4096);
2296        assert!(part.bytes.is_empty(), "the zeroes were carried after all");
2297        assert!(!part.shape.bits);
2298    }
2299
2300    #[test]
2301    fn what_a_section_directive_said_about_a_section_is_what_it_is() {
2302        let out = assembled("\t.section .init.text,\"ax\",@progbits\n\t.byte 0x90\n");
2303        let part = out.parts.iter().find(|part| part.name == ".init.text").expect("the section");
2304        assert!(part.shape.alloc && part.shape.exec && part.shape.bits);
2305        assert!(!part.shape.write, "nothing said it was writable");
2306    }
2307
2308    #[test]
2309    fn the_same_section_named_twice_is_one_section_and_the_bytes_run_on() {
2310        let out = assembled("\t.data\n\t.byte 1\n\t.text\n\t.byte 0x90\n\t.data\n\t.byte 2\n");
2311        assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2312        assert_eq!(bytes(&out, ".text"), vec![0x90]);
2313    }
2314
2315    #[test]
2316    fn pushing_a_section_and_coming_back_leaves_the_first_one_where_it_was() {
2317        let out = assembled(
2318            "\t.data\n\t.byte 1\n\t.pushsection .rodata\n\t.byte 9\n\t.popsection\n\t.byte 2\n",
2319        );
2320        assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2321        assert_eq!(bytes(&out, ".rodata"), vec![9]);
2322    }
2323
2324    #[test]
2325    fn a_size_that_counts_from_here_back_to_a_label_is_a_number() {
2326        // `.size foo, .-foo` is on the end of nearly every function gas ever wrote. Both ends are in
2327        // the same section, so the difference is known here and there is nothing to ask the linker.
2328        let out = assembled(
2329            "\t.text\n\t.globl f\n\t.type f, @function\nf:\n\t.byte 0,0,0,0,0\n\t.size f, .-f\n",
2330        );
2331        let f = name(&out, "f");
2332        assert_eq!(f.size, 5);
2333        assert_eq!(f.sort, Sort::Func);
2334    }
2335
2336    #[test]
2337    fn a_set_may_name_something_further_down_the_file() {
2338        // Nothing can be worked out as it is parsed, which is why an expression is kept as a sum
2339        // until the end. `table_end` does not exist yet on the line that subtracts it.
2340        let out = assembled(
2341            "\t.data\ntable:\n\t.long 1, 2, 3\ntable_end:\n\t.globl width\n\t.set width, \
2342             table_end - table\n",
2343        );
2344        assert_eq!(name(&out, "width").at, Held::Absolute(12));
2345    }
2346
2347    #[test]
2348    fn a_set_that_names_another_set_is_worked_at_until_it_stops_moving() {
2349        let out = assembled("\t.set a, b + 1\n\t.set b, c * 2\n\t.set c, 5\n");
2350        assert_eq!(name(&out, "a").at, Held::Absolute(11));
2351        assert_eq!(name(&out, "b").at, Held::Absolute(10));
2352    }
2353
2354    #[test]
2355    fn two_sets_that_name_each_other_are_refused_rather_than_looped_over() {
2356        let why = refused("\t.set a, b\n\t.set b, a\n");
2357        assert!(why.why.contains("neither has a value"), "{why}");
2358    }
2359
2360    #[test]
2361    fn a_pointer_to_something_else_is_a_relocation_for_the_whole_address() {
2362        let out = assembled("\t.data\n\t.quad message\n");
2363        let reloc = &out.parts[0].relocs[0];
2364        assert_eq!(reloc.at, 0);
2365        assert_eq!(reloc.symbol, "message");
2366        assert_eq!(reloc.kind, Reference::Address { bytes: 8 });
2367        assert_eq!(reloc.addend, 0);
2368        assert_eq!(name(&out, "message").at, Held::Undefined);
2369    }
2370
2371    #[test]
2372    fn a_distance_from_here_to_something_else_is_a_relocation_relative_to_here() {
2373        // The other shape a reduced expression can have, and the one whose addend is not zero: the
2374        // four bytes sit at offset four, and a relocation counts from where it starts.
2375        let out = assembled("\t.data\n\t.quad 0\n\t.long message - .\n");
2376        let reloc = &out.parts[0].relocs[0];
2377        assert_eq!(reloc.at, 8);
2378        assert_eq!(reloc.symbol, "message");
2379        assert_eq!(reloc.kind, Reference::Data);
2380        assert_eq!(reloc.addend, 0);
2381    }
2382
2383    #[test]
2384    fn a_distance_counted_from_somewhere_that_is_not_here_carries_the_difference() {
2385        // The case that says which way round the addend goes, which `message - .` cannot because
2386        // both halves of it are the same number. A linker writes `symbol + addend - here`, and
2387        // what was asked for is `symbol - start`, so the addend is how far these bytes are past
2388        // the label rather than how far the label is behind them.
2389        let out = assembled("\t.data\nstart:\n\t.quad 0\n\t.long message - start\n");
2390        let reloc = &out.parts[0].relocs[0];
2391        assert_eq!(reloc.at, 8);
2392        assert_eq!(reloc.kind, Reference::Data);
2393        assert_eq!(reloc.addend, 8);
2394    }
2395
2396    #[test]
2397    fn a_number_added_to_a_name_rides_along_in_the_addend() {
2398        let out = assembled("\t.data\n\t.quad message + 16\n");
2399        assert_eq!(out.parts[0].relocs[0].addend, 16);
2400    }
2401
2402    #[test]
2403    fn comm_and_lcomm_ask_the_linker_for_room_rather_than_carrying_it() {
2404        let out = assembled("\t.comm shared, 8, 8\n\t.lcomm mine, 32, 16\n");
2405        assert_eq!(name(&out, "shared").at, Held::Common { size: 8, align: 8 });
2406        assert_eq!(name(&out, "shared").binding, Binding::Global);
2407        // `.lcomm` is space in `.bss` under a local name, which is a different thing from `.comm`
2408        // however much the two names look alike.
2409        assert_eq!(name(&out, "mine").binding, Binding::Local);
2410        assert!(matches!(name(&out, "mine").at, Held::In { .. }));
2411    }
2412
2413    #[test]
2414    fn what_a_file_says_about_who_can_see_a_name_is_kept() {
2415        let out = assembled(
2416            "\t.text\n\t.globl seen\n\t.weak maybe\n\t.hidden inside\n\t.globl \
2417             inside\nseen:\nmaybe:\ninside:\n\t.byte 0\n",
2418        );
2419        assert_eq!(name(&out, "seen").binding, Binding::Global);
2420        assert_eq!(name(&out, "maybe").binding, Binding::Weak);
2421        assert_eq!(name(&out, "inside").visibility, Visibility::Hidden);
2422    }
2423
2424    #[test]
2425    fn the_name_of_the_file_is_a_symbol_of_its_own() {
2426        // And not one that can collide with something in the file, which is why it is kept apart
2427        // from the rest until the end.
2428        let out = assembled("\t.file \"big.s\"\n\t.data\nbig:\n\t.byte 0\n");
2429        assert_eq!(out.names[0].name, "big.s");
2430        assert_eq!(out.names[0].sort, Sort::File);
2431        assert_eq!(out.names[0].binding, Binding::Local);
2432        assert!(out.names.iter().any(|name| name.name == "big"), "the label was lost");
2433    }
2434
2435    #[test]
2436    fn a_numbered_file_is_a_note_for_a_debugger_and_not_a_name() {
2437        // `.file 1 "foo.c"` is the DWARF form and names an entry in a line table, which is a
2438        // different directive wearing the same word.
2439        let out = assembled("\t.file 1 \"foo.c\"\n\t.data\n\t.byte 0\n");
2440        assert!(out.names.is_empty(), "{:?}", out.names);
2441    }
2442
2443    #[test]
2444    fn an_instruction_this_has_no_bytes_for_is_refused_by_name_and_by_line() {
2445        // The failure this crate is written to prevent. An assembler that skipped what it did not
2446        // recognise would write an object that links, and what would be wrong with it is a run of
2447        // missing bytes in the middle of a function.
2448        let why = refused("\t.text\nf:\n\tmovq %rdi, %rax\n\tpopcnt %rax, %rdx\n\tret\n");
2449        assert_eq!(why.line, 4);
2450        assert!(why.why.contains("popcnt"), "{why}");
2451    }
2452
2453    #[test]
2454    fn a_function_of_instructions_is_its_bytes_and_its_size() {
2455        // The whole of what a hand written file is, end to end: a section, a name, three
2456        // instructions and a size counted back to the label.
2457        let out = assembled(
2458            "\t.text\n\t.globl id\n\t.type id, @function\nid:\n\tmovq %rdi, %rax\n\tret\n\t.size \
2459             id, .-id\n",
2460        );
2461        assert_eq!(bytes(&out, ".text"), vec![0x48, 0x89, 0xf8, 0xc3]);
2462        assert_eq!(name(&out, "id").size, 4);
2463        assert_eq!(name(&out, "id").at, Held::In { part: 0, offset: 0 });
2464    }
2465
2466    #[test]
2467    fn a_jump_to_a_label_in_this_section_is_a_number_and_not_a_relocation() {
2468        // Because both ends are here, so there is nothing for a linker to work out. The distance
2469        // is counted from the end of the jump, which is why jumping over nothing is zero and not
2470        // minus five.
2471        let out = assembled("\t.text\n\tjmp over\nover:\n\tret\n");
2472        assert_eq!(bytes(&out, ".text"), vec![0xe9, 0, 0, 0, 0, 0xc3]);
2473        assert!(out.parts[0].relocs.is_empty(), "{:?}", out.parts[0].relocs);
2474    }
2475
2476    #[test]
2477    fn a_jump_backwards_is_the_negative_distance_to_it() {
2478        let out = assembled("\t.text\nagain:\n\tjmp again\n");
2479        assert_eq!(bytes(&out, ".text"), vec![0xe9, 0xfb, 0xff, 0xff, 0xff]);
2480    }
2481
2482    #[test]
2483    fn a_call_to_a_name_this_file_does_not_define_may_go_through_a_stub() {
2484        // Which is the whole difference between this and the test below it. A call is allowed to
2485        // reach further than four bytes by way of something the linker writes, and a load of a
2486        // datum is not, so they are two relocations and the shape of the instruction is what says
2487        // which. The addend is minus four because the four bytes are the last of the instruction
2488        // and the machine counts them from the end of it.
2489        let out = assembled("\t.text\n\tcall puts\n");
2490        let reloc = &out.parts[0].relocs[0];
2491        assert_eq!(reloc.at, 1);
2492        assert_eq!(reloc.symbol, "puts");
2493        assert_eq!(reloc.kind, Reference::Call);
2494        assert_eq!(reloc.addend, -4);
2495    }
2496
2497    #[test]
2498    fn a_datum_reached_from_the_instruction_pointer_is_a_relocation_that_may_not() {
2499        let out = assembled("\t.text\n\tmovq message(%rip), %rax\n");
2500        let reloc = &out.parts[0].relocs[0];
2501        assert_eq!(reloc.symbol, "message");
2502        assert_eq!(reloc.kind, Reference::Data);
2503        // Three bytes of opcode and addressing in front of the four, and nothing after them.
2504        assert_eq!(reloc.at, 3);
2505        assert_eq!(reloc.addend, -4);
2506    }
2507
2508    #[test]
2509    fn a_branch_with_one_byte_of_reach_is_filled_in_at_one_byte() {
2510        // `jrcxz` has no longer form, so what goes in is a byte and the byte is all there is. A
2511        // fixup that assumed four would write over the two instructions behind this one.
2512        let out = assembled("\t.text\nagain:\n\tdec %rcx\n\tjrcxz again\n\tret\n");
2513        assert_eq!(bytes(&out, ".text"), vec![0x48, 0xff, 0xc9, 0xe3, 0xfb, 0xc3]);
2514    }
2515
2516    #[test]
2517    fn a_branch_to_somewhere_the_bytes_it_has_cannot_reach_is_refused() {
2518        // The other half of the same thing. There is no relaxing a `jrcxz` into something longer,
2519        // so a destination out of its reach is a mistake in the file, and quietly keeping the low
2520        // byte of the distance would send the program somewhere nobody wrote.
2521        let why = refused("\t.text\n\tjrcxz away\n\t.zero 200\naway:\n\tret\n");
2522        assert_eq!(why.line, 2);
2523        assert!(why.why.contains("does not reach"), "{why}");
2524    }
2525
2526    #[test]
2527    fn a_number_too_big_for_the_bytes_it_is_written_into_is_refused() {
2528        // Not about instructions at all, and found on the way to the two above: a distance between
2529        // two labels written into a `.byte` was being cut down to its low eight bits. Counted both
2530        // ways, so a byte takes anything from minus a hundred and twenty eight to two hundred and
2531        // fifty five and refuses what is outside that.
2532        let out = assembled("\t.data\nhere:\n\t.zero 200\nthere:\n\t.byte there - here\n");
2533        assert_eq!(bytes(&out, ".data")[200], 200);
2534        let why = refused("\t.data\nhere:\n\t.zero 300\nthere:\n\t.byte there - here\n");
2535        assert!(why.why.contains("does not reach"), "{why}");
2536    }
2537
2538    #[test]
2539    fn an_instruction_in_a_section_that_holds_no_bytes_is_refused() {
2540        let why = refused("\t.bss\n\tret\n");
2541        assert!(why.why.contains("holds no bytes"), "{why}");
2542    }
2543
2544    #[test]
2545    fn a_directive_this_does_not_know_is_refused_by_name_and_by_line() {
2546        let why = refused("\t.text\n\t.byte 0\n\t.reloc 0, R_X86_64_NONE, f\n");
2547        assert_eq!(why.line, 3);
2548        assert!(why.why.contains(".reloc"), "{why}");
2549    }
2550
2551    #[test]
2552    fn the_comments_the_three_ways_of_writing_one_make_are_not_read() {
2553        // The `#` one is why the output of the preprocessor can be handed straight to this: a
2554        // `# 42 "foo.h"` line marker is a comment and nothing has to know it is one.
2555        let out = assembled(
2556            "# 1 \"foo.S\"\n\t.data\n\t.byte 1 # one\n\t.byte 2 // two\n\t/* a\n\tcomment */\t.byte \
2557             3\n",
2558        );
2559        assert_eq!(bytes(&out, ".data"), vec![1, 2, 3]);
2560    }
2561
2562    #[test]
2563    fn a_comment_left_open_at_the_end_of_the_file_is_said_rather_than_ignored() {
2564        let why = refused("\t.data\n\t/* and then nothing\n");
2565        assert!(why.why.contains("never closed"), "{why}");
2566    }
2567
2568    #[test]
2569    fn a_string_with_a_comment_character_in_it_is_a_string() {
2570        let out = assembled("\t.data\n\t.ascii \"a#b/*c\"\n");
2571        assert_eq!(bytes(&out, ".data"), b"a#b/*c".to_vec());
2572    }
2573
2574    #[test]
2575    fn several_statements_on_one_line_are_several_statements() {
2576        let out = assembled("\t.data; .byte 1; .byte 2\n");
2577        assert_eq!(bytes(&out, ".data"), vec![1, 2]);
2578    }
2579
2580    #[test]
2581    fn a_section_nothing_was_ever_put_in_is_dropped() {
2582        // Every file starts in `.text` whether or not it says so, and a `.section` inside a macro
2583        // that turned out to be unused should not leave a header behind either.
2584        let out = assembled("\t.data\n\t.byte 1\n");
2585        assert_eq!(out.parts.len(), 1);
2586        assert_eq!(out.parts[0].name, ".data");
2587    }
2588
2589    #[test]
2590    fn a_section_with_nothing_in_it_but_a_name_is_kept() {
2591        // Because the name has to point somewhere, and dropping the section under it would leave a
2592        // symbol pointing at a section that is not there.
2593        let out = assembled("\t.text\n\t.globl marker\nmarker:\n");
2594        assert_eq!(out.parts.len(), 1);
2595        assert_eq!(name(&out, "marker").at, Held::In { part: 0, offset: 0 });
2596    }
2597
2598    #[test]
2599    fn an_error_directive_is_the_file_saying_it_refuses_itself() {
2600        let why = refused("\t.error \"this is not the machine for it\"\n");
2601        assert!(why.why.contains("not the machine for it"), "{why}");
2602    }
2603
2604    #[test]
2605    fn a_jump_counted_from_itself_is_the_short_one_gas_writes() {
2606        // What tcc's own tests do: jump over four bytes of data and load them back by counting
2607        // from the load. Both only land where they mean to if the jump is two bytes long.
2608        let out = assembled("\tjmp .+6\n\t.int 123\n\tmov .-4(%rip), %eax\n");
2609        assert_eq!(
2610            bytes(&out, ".text"),
2611            vec![0xeb, 0x04, 123, 0, 0, 0, 0x8b, 0x05, 0xf6, 0xff, 0xff, 0xff]
2612        );
2613    }
2614
2615    #[test]
2616    fn a_numbered_label_in_an_expression_is_a_place() {
2617        let out =
2618            assembled("2:\n\tjmp .+6\n1:\n\t.pushsection .data\n\t.long 1b - 2b\n\t.popsection\n");
2619        assert_eq!(bytes(&out, ".data"), vec![2, 0, 0, 0]);
2620        // And a binary number is still a number, because the digits go on after the letter.
2621        let out = assembled("\t.data\n\t.byte 0b101\n");
2622        assert_eq!(bytes(&out, ".data"), vec![5]);
2623    }
2624
2625    #[test]
2626    fn a_number_an_instruction_carries_may_be_an_expression_over_labels() {
2627        let out = assembled("3:\tmov $4f-3b, %eax\n4:\n");
2628        assert_eq!(bytes(&out, ".text"), vec![0xb8, 5, 0, 0, 0]);
2629    }
2630
2631    #[test]
2632    fn a_number_an_instruction_carries_may_not_name_something_elsewhere() {
2633        let why = refused("\tmov $elsewhere, %eax\n");
2634        assert!(why.why.contains("relocation"), "{why}");
2635    }
2636
2637    #[test]
2638    fn a_name_set_twice_means_what_it_was_where_it_is_used() {
2639        let out = assembled(
2640            "\t.data\n\t.byte early\n\tearly = 3\n\tx = 1\n\t.byte x\n\tx = x + 1\n\t.byte x\n",
2641        );
2642        assert_eq!(bytes(&out, ".data"), vec![3, 1, 2]);
2643    }
2644
2645    #[test]
2646    fn a_place_set_twice_and_reached_from_another_section_is_relocated_against() {
2647        let out = assembled(
2648            "\t.data\n\tx = .\n\t.int 1\n\tx = .\n\t.int 2\n\t.text\n\tmov x(%rip), %eax\n",
2649        );
2650        let reloc = &out.parts.iter().find(|part| part.name == ".text").unwrap().relocs[0];
2651        let target = name(&out, &reloc.symbol);
2652        let data = out.parts.iter().position(|part| part.name == ".data").unwrap();
2653        assert_eq!(target.at, Held::In { part: data, offset: 4 });
2654    }
2655
2656    #[test]
2657    fn frame_rules_are_an_unwind_table_pointing_at_the_function() {
2658        let out = assembled(
2659            "f:\n\t.cfi_startproc\n\tpush %rbp\n\t.cfi_def_cfa_offset 16\n\t.cfi_offset %rbp, \
2660             -16\n\tpop %rbp\n\t.cfi_def_cfa_offset 8\n\tret\n\t.cfi_endproc\n",
2661        );
2662        let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
2663        // One byte in, the push: the frame is sixteen deep and the caller's rbp is at the bottom.
2664        // One byte later, the pop, and it is eight deep again.
2665        let rows = [0x41, 0x0e, 0x10, 0x86, 0x02, 0x41, 0x0e, 0x08];
2666        assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
2667        let [reloc] = table.relocs.as_slice() else { panic!("one record, one relocation") };
2668        let text = out.parts.iter().position(|part| part.name == ".text").unwrap();
2669        assert_eq!(name(&out, &reloc.symbol).at, Held::In { part: text, offset: 0 });
2670    }
2671
2672    #[test]
2673    fn a_frame_rule_relative_to_the_register_is_the_same_slot() {
2674        let out = assembled(
2675            "\t.cfi_startproc\n\tpush %rbx\n\t.cfi_adjust_cfa_offset 8\n\t.cfi_rel_offset \
2676             %rbx, 0\n\t.cfi_endproc\n",
2677        );
2678        let table = out.parts.iter().find(|part| part.name == ".eh_frame").expect("a table");
2679        let rows = [0x41, 0x0e, 0x10, 0x83, 0x02];
2680        assert!(table.bytes.windows(rows.len()).any(|at| at == rows), "{:x?}", table.bytes);
2681    }
2682
2683    #[test]
2684    fn frame_rules_for_a_debugger_only_are_no_unwind_table() {
2685        let out =
2686            assembled("\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\tret\n\t.cfi_endproc\n");
2687        assert!(out.parts.iter().all(|part| part.name != ".eh_frame"));
2688    }
2689
2690    #[test]
2691    fn a_frame_rule_outside_a_function_or_a_function_never_ended_is_refused() {
2692        let why = refused("\t.cfi_def_cfa_offset 16\n");
2693        assert!(why.why.contains("outside"), "{why}");
2694        let why = refused("\t.cfi_startproc\n\tret\n");
2695        assert!(why.why.contains("never ended"), "{why}");
2696    }
2697}