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