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