Skip to main content

rucc_object/
source.rs

1//! An object file written from what a file of assembly says, rather than from a compilation.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.1, the paragraph that says we also accept
4//! assembly as input.
5//!
6//! # Why this is not [`crate::Text`] and [`crate::Data`]
7//!
8//! Those two are the compiler's view of a file and they are the right view of one. A function is a
9//! run of bytes with a name and a length, a variable is an image with a name and a place worked out
10//! from what the variable is, and neither carries a section name because where a thing goes is an
11//! answer rather than a question. That is exactly what makes them the wrong shape for assembly.
12//!
13//! A file of assembly says the section, so the place is a question again, and it may say a section
14//! this compiler would never have chosen and flags that go with it. It puts names at offsets rather
15//! than around images, so `.long 0` followed by `foo:` is four bytes belonging to nothing with a
16//! name after them, which no list of named variables can hold. It defines names that are not at any
17//! offset at all, which is what `.set` and `.equ` produce. And it may name a symbol in the middle of
18//! a section, with a size the program stated rather than one worked out from the bytes.
19//!
20//! So this is the assembler's view: a list of sections that each know their own name, flags and
21//! bytes, and a list of names that point into them. Bending one into the other would mean deciding
22//! here what a program already said, and a wrong answer about which section something is in is not
23//! visible until a link or a load.
24//!
25//! The two views meet at the [`object`] crate's writer, which is what both call, and at the short
26//! list of format opinions beside it, which is what both ask where the formats differ. So
27//! there is one place that knows how an object file is laid out and one that knows what each format
28//! calls the things in it.
29
30use object::write::{Object as Writer, Relocation, Symbol, SymbolSection};
31use object::{Architecture, Endianness, RelocationFlags, SectionKind, SymbolFlags, elf, pe};
32use rucc_target::aarch64::Fixup;
33use rucc_target::{ObjectFormat, TargetInfo};
34use rucc_tuple::Arch;
35
36use crate::file::{Error, Flavour};
37use crate::section::{Array, Binding, Info, Reloc, Visibility};
38
39/// One section, as a file of assembly describes one.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Part {
42    /// What it is called, with the leading dot the source wrote.
43    pub name: String,
44    /// Its bytes, which are empty for a section that says how big it is and holds none of them.
45    pub bytes: Vec<u8>,
46    /// How long it is. The same as the length of the bytes for every section that has any, and the
47    /// whole of what a `@nobits` section says about itself.
48    pub size: u64,
49    /// The boundary it starts on, which is the largest any directive in it asked for.
50    pub align: u64,
51    /// The flags and the type, which the source states and this does not work out.
52    pub shape: Shape,
53    /// Every place in it that names something, counted from the start of the section.
54    pub relocs: Vec<Reloc>,
55    /// The COMDAT it is, on COFF, where `.section name,"flags",discard,symbol` makes a section one
56    /// the linker keeps a single copy of out of every object that has one about the same symbol.
57    /// [`None`] for every other section and on every other format.
58    pub group: Option<Group>,
59}
60
61/// A COFF section the linker keeps one copy of, which is what the third and fourth operands of
62/// `.section` say on that format.
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct Group {
65    /// The name the group is about, which the file defines in the section.
66    pub symbol: String,
67    /// Which copy the linker keeps.
68    pub keep: Keep,
69}
70
71/// How the linker picks the copy of a [`Group`] it keeps, in the words gas and llvm-mc take for
72/// each of COFF's selection numbers.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum Keep {
75    /// `one_only`: there must be only one, and two is an error.
76    One,
77    /// `discard`: any one, and the rest are dropped. What a `.refptr.` pointer is.
78    Any,
79    /// `same_size`: any one, and two of different sizes are an error.
80    SameSize,
81    /// `same_contents`: any one, and two with different bytes are an error.
82    SameContents,
83    /// `largest`: the biggest one.
84    Largest,
85    /// `newest`: the newest one, which no toolchain writes and link.exe does not implement.
86    Newest,
87}
88
89impl Keep {
90    /// The word for it, as `.section` spells it.
91    #[must_use]
92    pub fn of(word: &str) -> Option<Keep> {
93        Some(match word {
94            "one_only" => Keep::One,
95            "discard" => Keep::Any,
96            "same_size" => Keep::SameSize,
97            "same_contents" => Keep::SameContents,
98            "largest" => Keep::Largest,
99            "newest" => Keep::Newest,
100            _ => return None,
101        })
102    }
103
104    pub(crate) const fn kind(self) -> object::ComdatKind {
105        match self {
106            Keep::One => object::ComdatKind::NoDuplicates,
107            Keep::Any => object::ComdatKind::Any,
108            Keep::SameSize => object::ComdatKind::SameSize,
109            Keep::SameContents => object::ComdatKind::ExactMatch,
110            Keep::Largest => object::ComdatKind::Largest,
111            Keep::Newest => object::ComdatKind::Newest,
112        }
113    }
114}
115
116/// What a section is, which on ELF is a handful of flag letters and a type.
117///
118/// Held as the separate facts rather than as one of a fixed list of kinds, because the list is not
119/// fixed: a program may write `.section .init.text,"ax",@progbits` and mean a section this compiler
120/// has no name for, and the letters are the whole of what it said about it. The writer underneath
121/// takes a [`SectionKind`], so `Shape::kind` is the one place that turns these back into one, and
122/// the cases it cannot say are written as flags directly.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
124pub struct Shape {
125    /// `a`: the section takes space in the loaded image. A section without this is for a debugger
126    /// or a linker to read and is not in the program at run time.
127    pub alloc: bool,
128    /// `w`: the program may write to it.
129    pub write: bool,
130    /// `x`: the processor may execute it.
131    pub exec: bool,
132    /// `T`: one copy per thread rather than one copy per program.
133    pub thread: bool,
134    /// Whether the file carries the bytes. False is `@nobits`, which is what `.bss` is.
135    pub bits: bool,
136    /// Which kind of table of function addresses this is, for the three ELF has a type for.
137    pub array: Option<Array>,
138    /// `M`: how long each entry is in a section of constants the linker may keep one copy of
139    /// wherever two objects hold the same one, and zero for a section that is not one of those.
140    /// gcc puts a `double` it loads from memory in `.rodata.cst8`, which is one of these.
141    pub merge: u64,
142    /// `S`: the entries are strings ended by a zero rather than all of one length, which is where
143    /// gcc puts every string literal. Only means anything beside `merge`.
144    pub strings: bool,
145    /// The type and attributes of a Mach-O section, in the one word the format keeps them in,
146    /// which is what [`Shape::mach`] works out. Zero on the other two formats, where the fields
147    /// above are the whole answer, and zero is also an ordinary Mach-O section with nothing said.
148    pub mach: u32,
149    /// The characteristics of a COFF section whose `.section` gave COFF's own letters, which is
150    /// what [`Shape::coff`] works out. Zero when it gave none, and then the writer works them out
151    /// from the name and the kind, as it does for a section this compiler made.
152    pub coff: u32,
153}
154
155impl Shape {
156    /// What a section of this name is when the source named it and said nothing else.
157    ///
158    /// `.text`, `.data` and the rest are names an assembler already knows the flags of, which is
159    /// why a program may write `.data` on its own and why `.section .data` without letters is the
160    /// same section rather than an unallocated one. A name nothing here knows gets the flags of an
161    /// ordinary allocated writable section, which is what gas does with one.
162    #[must_use]
163    pub fn of(name: &str) -> Shape {
164        let base = Shape { alloc: true, bits: true, ..Shape::default() };
165        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
166        let head = head.split_once('.').map_or(head, |(first, _)| first);
167        match head {
168            "text" | "init" | "fini" => Shape { exec: true, ..base },
169            "rodata" | "eh_frame_hdr" => base,
170            "bss" => Shape { write: true, bits: false, ..base },
171            "tbss" => Shape { write: true, thread: true, bits: false, ..base },
172            "tdata" => Shape { write: true, thread: true, ..base },
173            // The three the linker gathers and the startup code walks. The type is what makes one
174            // of them that, rather than the name: a section of the ordinary type under the same
175            // name is gathered into the same run and called by nobody.
176            _ if Array::of(name).is_some() => Shape { write: true, array: Array::of(name), ..base },
177            // Not allocated, because nothing in the running program reads it. A debugger reads it
178            // out of the file, and a section marked allocated would take space in every process.
179            "debug_info" | "debug_abbrev" | "debug_line" | "debug_str" | "comment" => {
180                Shape { alloc: false, bits: true, ..Shape::default() }
181            }
182            _ => Shape { write: true, ..base },
183        }
184    }
185
186    /// The flags a section of this name has whatever letters the source gave it.
187    ///
188    /// gas adds these to the letters rather than taking the letters alone, so
189    /// `.section .data.rel.ro.local,"a"` is writable all the same. GMP names its jump tables that
190    /// way, and a linker making a position independent program refuses an address it would have to
191    /// fix up in a section it may not write. Only the names gas treats as a family are here, which
192    /// is fewer than [`Shape::of`] knows: `.init.data` is not executable just because `.init` is.
193    #[must_use]
194    pub fn implied(name: &str) -> Shape {
195        let base = Shape { alloc: true, ..Shape::default() };
196        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
197        let head = head.split_once('.').map_or(head, |(first, _)| first);
198        match head {
199            "text" => Shape { exec: true, ..base },
200            "rodata" => base,
201            "data" | "bss" => Shape { write: true, ..base },
202            "tdata" | "tbss" => Shape { write: true, thread: true, ..base },
203            _ if Array::of(name).is_some() => Shape { write: true, ..base },
204            _ => Shape::default(),
205        }
206    }
207
208    /// What a Mach-O section is, from its segment, its section and the type and attributes a
209    /// `.section` directive gave after them.
210    ///
211    /// The word the format keeps is the answer and the fields beside it are filled in from it, so
212    /// that what reads a shape without knowing the format still sees code as code and a zero
213    /// filled section as one that holds no bytes.
214    ///
215    /// # Errors
216    ///
217    /// A type or an attribute Apple's assembler does not take, as a sentence.
218    pub fn mach(
219        segment: &str,
220        section: &str,
221        kind: Option<&str>,
222        attributes: &[&str],
223    ) -> Result<Shape, String> {
224        let mach = crate::macho::section_flags(segment, section, kind, attributes)?;
225        let exec = mach & object::macho::S_ATTR_PURE_INSTRUCTIONS.0 != 0;
226        let typ = object::macho::SectionFlags(mach).typ();
227        let thread = matches!(
228            typ,
229            object::macho::S_THREAD_LOCAL_REGULAR | object::macho::S_THREAD_LOCAL_ZEROFILL
230        );
231        Ok(Shape {
232            alloc: true,
233            write: segment != "__TEXT",
234            exec,
235            thread,
236            bits: !crate::macho::zero_filled(mach),
237            mach,
238            ..Shape::default()
239        })
240    }
241
242    /// What a COFF section is, from the letters after its name in `.section`.
243    ///
244    /// COFF's letters are not ELF's, and `d`, `r` and `n` mean nothing to ELF at all. These are
245    /// read the way llvm-mc reads them, which is also how gas reads them: `x` is code, `d` is data,
246    /// `b` is zero filled, `r` takes away writing and `w` gives it back, `n` is a section the
247    /// linker drops, `i` holds options for the linker, `y` is not readable, `D` may be discarded,
248    /// `s` is shared, and `a` is taken and means nothing. A section with no letters at all is
249    /// readable and writable data. `.drectve,"yni"` is the one a DLL's exports are said in.
250    ///
251    /// # Errors
252    ///
253    /// A letter that is not one of those, as the letter.
254    pub fn coff(letters: &str) -> Result<Shape, char> {
255        let (mut code, mut data, mut zero, mut drop, mut info) =
256            (false, false, false, false, false);
257        let (mut read, mut write, mut shared, mut discard) = (true, true, false, false);
258        let mut writable = false;
259        for letter in letters.chars() {
260            match letter {
261                'a' => {}
262                'b' => zero = true,
263                'd' => {
264                    data = true;
265                    write = true;
266                }
267                'n' => drop = true,
268                'D' => discard = true,
269                'r' => {
270                    writable = false;
271                    write = false;
272                    data |= !code;
273                }
274                's' => {
275                    shared = true;
276                    data = true;
277                    write = true;
278                }
279                'w' => {
280                    write = true;
281                    writable = true;
282                }
283                'x' => {
284                    code = true;
285                    write &= writable;
286                }
287                'y' => {
288                    read = false;
289                    write = false;
290                }
291                'i' => info = true,
292                other => return Err(other),
293            }
294        }
295        let mut flags = 0;
296        if code {
297            flags |= pe::IMAGE_SCN_CNT_CODE.0 | pe::IMAGE_SCN_MEM_EXECUTE.0;
298        }
299        if data {
300            flags |= pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0;
301        }
302        if zero && !data {
303            flags |= pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA.0;
304        }
305        if drop {
306            flags |= pe::IMAGE_SCN_LNK_REMOVE.0;
307        }
308        if read {
309            flags |= pe::IMAGE_SCN_MEM_READ.0;
310        }
311        if write {
312            flags |= pe::IMAGE_SCN_MEM_WRITE.0;
313        }
314        if discard {
315            flags |= pe::IMAGE_SCN_MEM_DISCARDABLE.0;
316        }
317        if shared {
318            flags |= pe::IMAGE_SCN_MEM_SHARED.0;
319        }
320        if info {
321            flags |= pe::IMAGE_SCN_LNK_INFO.0;
322        }
323        Ok(Shape {
324            alloc: !drop && !info,
325            write,
326            exec: code,
327            bits: !(zero && !data),
328            coff: flags,
329            ..Shape::default()
330        })
331    }
332
333    /// The flag word ELF holds these in.
334    ///
335    /// Not public, and neither are the two below it. The fields above are the whole of what a
336    /// caller says about a section, and how ELF spells them is this crate's business: a reader that
337    /// had to name an ELF constant to describe an executable section would be one that could not
338    /// describe one for any other format.
339    pub(crate) fn sh_flags(self) -> elf::SectionFlags {
340        let mut flags = 0;
341        if self.alloc {
342            flags |= elf::SHF_ALLOC.0;
343        }
344        if self.write {
345            flags |= elf::SHF_WRITE.0;
346        }
347        if self.exec {
348            flags |= elf::SHF_EXECINSTR.0;
349        }
350        if self.thread {
351            flags |= elf::SHF_TLS.0;
352        }
353        if self.merge != 0 {
354            flags |= elf::SHF_MERGE.0;
355            if self.strings {
356                flags |= elf::SHF_STRINGS.0;
357            }
358        }
359        elf::SectionFlags(flags)
360    }
361
362    /// The type ELF holds in the header beside those flags.
363    pub(crate) fn sh_type(self) -> elf::SectionType {
364        match self.array {
365            _ if !self.bits => elf::SHT_NOBITS,
366            Some(Array::Init) => elf::SHT_INIT_ARRAY,
367            Some(Array::Fini) => elf::SHT_FINI_ARRAY,
368            Some(Array::Preinit) => elf::SHT_PREINIT_ARRAY,
369            None => elf::SHT_PROGBITS,
370        }
371    }
372
373    /// What the writer underneath calls the nearest thing to this.
374    ///
375    /// It is told the flags in full afterwards, so this only has to be close enough that nothing
376    /// else the writer decides from the kind comes out wrong, which is the default alignment and
377    /// whether it appends bytes or counts them.
378    pub(crate) const fn kind(self) -> SectionKind {
379        match self {
380            Shape { bits: false, thread: true, .. } => SectionKind::UninitializedTls,
381            Shape { bits: false, .. } => SectionKind::UninitializedData,
382            Shape { thread: true, .. } => SectionKind::Tls,
383            Shape { exec: true, .. } => SectionKind::Text,
384            Shape { alloc: false, .. } => SectionKind::Other,
385            Shape { write: false, .. } => SectionKind::ReadOnlyData,
386            Shape { .. } => SectionKind::Data,
387        }
388    }
389}
390
391/// One name in the symbol table, as a file of assembly defines one.
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub struct Name {
394    /// The name, spelled as the source spelled it.
395    pub name: String,
396    /// Where it is.
397    pub at: Held,
398    /// How long the thing it names is, which is what `.size` said and is zero when nothing did.
399    pub size: u64,
400    /// What kind of thing it names, which is what `.type` said.
401    pub sort: Sort,
402    /// Who can see it.
403    pub binding: Binding,
404    /// How far outside a shared library it reaches.
405    pub visibility: Visibility,
406}
407
408/// Where a name is, which is four different things and not an offset with special cases.
409#[derive(Debug, Clone, Copy, PartialEq, Eq)]
410pub enum Held {
411    /// At an offset into one of the sections, which is what a label is.
412    In {
413        /// Which section, as an index into the list given alongside.
414        part: usize,
415        /// How far into it.
416        offset: u64,
417    },
418    /// A number rather than a place, which is what `.set` and `.equ` produce. The linker resolves
419    /// a reference to one to the number itself and there is nothing for it to be relative to.
420    Absolute(u64),
421    /// That much zeroed space asked of the linker under this name, which is `.comm` and `.lcomm`.
422    /// Every definition of the name across every object is merged into one.
423    Common {
424        /// How much space.
425        size: u64,
426        /// What boundary it has to start on. ELF records this where an ordinary symbol records its
427        /// address, which is why the two cannot both be said.
428        align: u64,
429    },
430    /// Named and not defined here, which the linker has to find somewhere else.
431    Undefined,
432}
433
434/// What kind of thing a name names, which is what `.type` says.
435#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
436pub enum Sort {
437    /// `@function`. A call through the procedure linkage table may be made to it.
438    Func,
439    /// `@object`. Data.
440    Object,
441    /// `@tls_object`. A thread-local variable, which a linker checks relocations against.
442    Thread,
443    /// `.file`, which names the source this was assembled from rather than anything in it.
444    ///
445    /// Not a thing `.type` can say, and here because it is a symbol and there is nowhere else for
446    /// it. A debugger reads it and so does `nm`, and gas writes one for every file that says its
447    /// own name, which is every file gcc produces.
448    File,
449    /// Nothing was said, which is what a plain label gets and is a real answer rather than a
450    /// missing one: gas writes `STT_NOTYPE` for a label nobody stated a type for.
451    #[default]
452    Untyped,
453}
454
455/// Everything an assembled file holds: its sections, and the names that point into them.
456#[derive(Debug, Clone, Default, PartialEq, Eq)]
457pub struct Assembled {
458    /// The sections, in the order the file first mentioned each of them.
459    pub parts: Vec<Part>,
460    /// The names, in the order the file defined or first referred to each of them.
461    pub names: Vec<Name>,
462    /// Whether the file said `.subsections_via_symbols`, which tells a Mach-O linker it may cut
463    /// every section at every symbol in it. Nothing on the other two formats.
464    pub subsections: bool,
465}
466
467/// That, as a relocatable object in whichever of the two formats the target wants.
468///
469/// Both formats, the same two the module that writes a compilation writes it into, and the
470/// differences between them are the same answers there. That is the whole reason this is not two
471/// functions: a file of assembly names its own sections and a compilation does not, but what a
472/// relocation is called and whether a symbol has anywhere to keep a visibility are facts about the
473/// format rather than about where the bytes came from, and a second set of answers to them would
474/// be a second set to get wrong.
475///
476/// What a [`Part`] carries is the section type and flags the source wrote in as many words. ELF has
477/// a field for each of them and they are written down as they stand. COFF has no field they map
478/// onto, so what the section is comes from the kind on the shape and the writer underneath turns it
479/// into the characteristics every other Windows assembler writes. A program that means a Windows
480/// section to be something other than what its name says is a program that has to say so some other
481/// way, which is what `.section` with COFF's own letters is for and what tamnd/rucc#1514 left open.
482///
483/// # Errors
484///
485/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for a
486/// relocation against a name the list does not hold or one this format has no relocation for.
487pub fn assembled(input: &Assembled, target: &TargetInfo) -> Result<Vec<u8>, Error> {
488    assembled_described(input, target, &Info::default())
489}
490
491/// The same object as [`assembled`], with the debug sections in `info` added to it.
492///
493/// For a compilation that went through a listing and asked for debug information, where the line
494/// table and the entries are built from the compilation rather than read from the file. A
495/// relocation in a chunk names another chunk or a name the file defines, and the second is written
496/// against the section the name is in, for the reason [`crate::write`] gives: a distance to a
497/// global name is not one a linker can work out.
498///
499/// # Errors
500///
501/// As for [`assembled`], and [`Error::Refused`] for a chunk that names something the file does
502/// not define.
503pub fn assembled_described(
504    input: &Assembled,
505    target: &TargetInfo,
506    info: &Info,
507) -> Result<Vec<u8>, Error> {
508    // Both machines on ELF and COFF, and AArch64 on Mach-O, which is a function of its own since
509    // what it answers differently is most of what is below.
510    let (flavour, machine) = match (Flavour::of(target), target.tuple.arch()) {
511        (Some(flavour), Arch::X86_64) => (flavour, Architecture::X86_64),
512        (Some(flavour), Arch::Aarch64) => (flavour, Architecture::Aarch64),
513        (None, Arch::Aarch64) if target.object_format == ObjectFormat::MachO => {
514            return crate::macho::write(input, target, info);
515        }
516        _ => return Err(Error::Format { triple: target.tuple.to_string() }),
517    };
518    let flags_of = |kind, after| match (machine, flavour) {
519        (Architecture::Aarch64, Flavour::Coff) => {
520            crate::coff::arm64(kind).map(|typ| RelocationFlags::Coff { typ })
521        }
522        (Architecture::Aarch64, _) => {
523            crate::elf::r_type_aarch64(kind).map(|r_type| RelocationFlags::Elf { r_type })
524        }
525        _ => flavour.reloc(kind, after),
526    };
527    // Whether the addend of a field of an instruction goes into the field rather than into the
528    // relocation, which is what a format without addends does. See [`crate::coff::carry`].
529    let carried = machine == Architecture::Aarch64 && flavour == Flavour::Coff;
530    let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
531
532    // Every section first, because a symbol says which one it is in and a relocation says which one
533    // it is written into, so both need the whole list before either can be added.
534    let mut made = Vec::with_capacity(input.parts.len());
535    for part in &input.parts {
536        let id = obj.add_section(Vec::new(), part.name.clone().into_bytes(), part.shape.kind());
537        // The flags in full rather than whatever the kind implied, because the kind is a summary of
538        // them and the source said them exactly. A section the program wrote `"ax"` on is executable
539        // whether or not its name is one this compiler would have made executable. Only where the
540        // format has the fields: see [`Flavour::stated`].
541        if let Some(flags) = flavour.stated(part.shape) {
542            obj.section_mut(id).flags = flags;
543        }
544        let align = part.align.max(1);
545        if part.shape.bits {
546            obj.append_section_data(id, &part.bytes, align);
547        } else {
548            obj.append_section_bss(id, part.size, align);
549        }
550        // A COMDAT's section symbol comes before the symbol the group is about, which is the
551        // order the COFF writer wants the two in, so it is asked for here and not left to the
552        // first relocation that happens to need it.
553        if part.group.is_some() {
554            obj.section_symbol(id);
555        }
556        made.push(id);
557    }
558
559    // Which relocations point at the section a name is in rather than at the name, and which names
560    // are then asked for by nothing and left out, before either is written down.
561    let defined: std::collections::HashMap<&str, &Name> =
562        input.names.iter().map(|name| (name.name.as_str(), name)).collect();
563    let onto = |reloc: &Reloc| moved(flavour, input, &defined, reloc);
564    let wanted: std::collections::HashSet<&str> = input
565        .parts
566        .iter()
567        .flat_map(|part| &part.relocs)
568        .filter(|reloc| onto(reloc).is_none())
569        .map(|reloc| reloc.symbol.as_str())
570        .collect();
571
572    // Then every name. A relocation names one, and the writer wants the symbol before the
573    // relocation that points at it, so this whole pass is in front of the one below.
574    let mut symbols = std::collections::BTreeMap::new();
575    for name in &input.names {
576        if flavour == Flavour::Elf && unseen(name) && !wanted.contains(name.name.as_str()) {
577            continue;
578        }
579        let (section, value, size) = match name.at {
580            Held::In { part, offset } => {
581                let Some(id) = made.get(part) else {
582                    let why = format!(
583                        "'{}' is in section {part} and there is no such section",
584                        name.name
585                    );
586                    return Err(Error::Refused { why });
587                };
588                (SymbolSection::Section(*id), offset, name.size)
589            }
590            Held::Absolute(value) => (SymbolSection::Absolute, value, name.size),
591            // A common symbol says what it wants rather than where it is, and ELF records the
592            // boundary it wants where an ordinary symbol records its address.
593            Held::Common { size, align } => (SymbolSection::Common, align, size),
594            Held::Undefined => (SymbolSection::Undefined, 0, 0),
595        };
596        let id = obj.add_symbol(Symbol {
597            name: name.name.clone().into_bytes(),
598            value,
599            size,
600            kind: flavour.sort(name.sort, name.binding),
601            scope: crate::file::scope_of(name.binding),
602            weak: name.binding == Binding::Weak,
603            section,
604            flags: SymbolFlags::None,
605        });
606        flavour.see(&mut obj, id, name.binding, name.visibility);
607        // The writer underneath records a common symbol as `STT_COMMON` and gas records the same
608        // symbol as `STT_OBJECT`. Both are a request for storage and a linker reads either, and the
609        // one gas writes is written here, because an object that says the same thing a different
610        // way is the kind of difference that turns up years later in a tool that only ever saw the
611        // other one. A common symbol is global by definition, so there is no binding to preserve.
612        if matches!(name.at, Held::Common { .. }) {
613            if let SymbolFlags::Elf { st_info, .. } = obj.symbol_flags_mut(id) {
614                *st_info = elf::STB_GLOBAL | elf::STT_OBJECT;
615            }
616        }
617        symbols.insert(name.name.clone(), id);
618    }
619
620    for (part, id) in input.parts.iter().zip(&made) {
621        let Some(group) = &part.group else { continue };
622        let Some(&symbol) = symbols.get(&group.symbol) else {
623            let why = format!(
624                "section '{}' is a COMDAT about '{}', which the file does not define",
625                part.name, group.symbol
626            );
627            return Err(Error::Refused { why });
628        };
629        obj.add_comdat(object::write::Comdat {
630            kind: group.keep.kind(),
631            symbol,
632            sections: vec![*id],
633        });
634    }
635
636    for (part, id) in input.parts.iter().zip(&made) {
637        for reloc in &part.relocs {
638            let (symbol, addend) = match onto(reloc) {
639                Some((part, offset)) => {
640                    (obj.section_symbol(made[part]), reloc.addend + offset as i64)
641                }
642                None => {
643                    let Some(&symbol) = symbols.get(&reloc.symbol) else {
644                        let why = format!(
645                            "'{}' is named by a relocation and by nothing else",
646                            reloc.symbol
647                        );
648                        return Err(Error::Refused { why });
649                    };
650                    (symbol, reloc.addend)
651                }
652            };
653            let flags = flags_of(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
654                why: format!("no relocation is {:?}", reloc.kind),
655            })?;
656            let addend = match reloc.kind {
657                crate::section::Reference::Field(fixup) if carried => {
658                    let data = obj.section_mut(*id).data_mut();
659                    let Some(bytes) = data.get_mut(reloc.at..reloc.at + 4) else {
660                        let why = format!("a field at {} is past the end of its section", reloc.at);
661                        return Err(Error::Refused { why });
662                    };
663                    let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
664                    let word = crate::coff::carry(fixup, word, addend)
665                        .map_err(|why| Error::Refused { why })?;
666                    bytes.copy_from_slice(&word.to_le_bytes());
667                    0
668                }
669                _ => addend,
670            };
671            obj.add_relocation(*id, Relocation { offset: reloc.at as u64, symbol, addend, flags })
672                .map_err(|why| Error::Refused { why: why.to_string() })?;
673        }
674    }
675
676    // The debug information, every section before any relocation because a relocation in one of
677    // them names another as often as it names a function.
678    let mut named = std::collections::HashMap::new();
679    for chunk in &info.chunks {
680        let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
681        obj.append_section_data(id, &chunk.bytes, 1);
682        named.insert(chunk.name.as_str(), id);
683    }
684    for chunk in &info.chunks {
685        let section = named[chunk.name.as_str()];
686        for reloc in &chunk.relocs {
687            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
688                Some(&id) => (obj.section_symbol(id), reloc.addend),
689                None => match defined.get(reloc.symbol.as_str()).map(|name| name.at) {
690                    Some(Held::In { part, offset }) => {
691                        (obj.section_symbol(made[part]), reloc.addend + offset as i64)
692                    }
693                    _ => match symbols.get(&reloc.symbol) {
694                        Some(&symbol) => (symbol, reloc.addend),
695                        None => {
696                            let why = format!(
697                                "'{}' is named by the debug information and is not defined here",
698                                reloc.symbol
699                            );
700                            return Err(Error::Refused { why });
701                        }
702                    },
703                },
704            };
705            let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
706            let flags = flags_of(kind, reloc.after)
707                .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
708            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
709            obj.add_relocation(section, record)
710                .map_err(|why| Error::Refused { why: why.to_string() })?;
711        }
712    }
713
714    // The same marker every other object this compiler writes gets, and for the same reason: a
715    // linker that does not find it in every input marks the stack executable. Not a second one if
716    // the file already said it, which a file written by hand for a linker that cares often does,
717    // and nothing at all on a format whose answer to the question is in the finished image.
718    if !input.parts.iter().any(|part| part.name == ".note.GNU-stack") {
719        flavour.marker(&mut obj);
720    }
721
722    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
723    if flavour == Flavour::Elf {
724        for part in input.parts.iter().filter(|part| part.shape.merge != 0) {
725            entry_size(&mut bytes, &part.name, part.shape.merge);
726        }
727    }
728    Ok(bytes)
729}
730
731/// Write how long an entry of a mergeable section is into its header, which the linker needs and
732/// the writer underneath has no field for. It writes one only for a section of strings it made
733/// itself. The file is a 64 bit little endian ELF one, since that is the only kind this writes, and
734/// the section is found by its name, which is unique because the assembler gave every name one
735/// section.
736fn entry_size(bytes: &mut [u8], name: &str, size: u64) {
737    let word = |bytes: &[u8], at: usize, width: usize| {
738        bytes[at..at + width].iter().rev().fold(0u64, |sum, &byte| sum << 8 | u64::from(byte))
739    };
740    let table = word(bytes, 0x28, 8) as usize;
741    let each = word(bytes, 0x3a, 2) as usize;
742    let count = word(bytes, 0x3c, 2) as usize;
743    let names = table + each * word(bytes, 0x3e, 2) as usize;
744    let names = word(bytes, names + 0x18, 8) as usize;
745    for header in (0..count).map(|nth| table + nth * each) {
746        let at = names + word(bytes, header, 4) as usize;
747        if bytes[at..].starts_with(name.as_bytes()) && bytes.get(at + name.len()) == Some(&0) {
748            bytes[header + 0x38..header + 0x40].copy_from_slice(&size.to_le_bytes());
749        }
750    }
751}
752
753/// The section and the offset into it a relocation is written against in place of the name it
754/// gave, when gas would do the same.
755///
756/// A name only this file can see is a place in a section and nothing more, so gas writes the
757/// section's own symbol and how far into it the place is, and a `.L` label then has no reason to be
758/// in the table at all. It keeps the name where the linker has to see it: a call, which may go
759/// through a stub the linker makes for that name, a slot of the global offset table, and a place in
760/// a section the linker may merge, where the offset into the section is not an offset into the
761/// merged one. The last of those is only a problem for a distance, or for an address with
762/// something added to it, since the address of the start of a string is what the linker follows.
763fn moved(
764    flavour: Flavour,
765    input: &Assembled,
766    defined: &std::collections::HashMap<&str, &Name>,
767    reloc: &Reloc,
768) -> Option<(usize, u64)> {
769    use crate::section::Reference;
770    let name = defined.get(reloc.symbol.as_str())?;
771    let Held::In { part, offset } = name.at else { return None };
772    if flavour != Flavour::Elf || name.binding != Binding::Local {
773        return None;
774    }
775    let near = matches!(reloc.kind, Reference::Data | Reference::Away);
776    let fixed = match reloc.kind {
777        Reference::Call
778        | Reference::Got
779        | Reference::GotBare
780        | Reference::GotKept
781        | Reference::Thread => false,
782        // The same for a field of an instruction that goes through a stub or a table slot, or that
783        // says where a thread-local variable is, which a linker checks against the name's type.
784        Reference::Field(
785            Fixup::Call26
786            | Fixup::Jump26
787            | Fixup::GotPage21
788            | Fixup::GotLo12
789            | Fixup::GotTprelPage21
790            | Fixup::GotTprelLo12Nc
791            | Fixup::TprelHi12
792            | Fixup::TprelLo12Nc,
793        ) => false,
794        _ if input.parts.get(part)?.shape.merge != 0 => !near && reloc.addend == 0,
795        _ => true,
796    };
797    fixed.then_some((part, offset))
798}
799
800/// Whether a name is one the assembler made up or a label only it sees, which gas leaves out of the
801/// table unless a relocation still names it. `.L` is the prefix for those that ELF assemblers agree
802/// on, and a name with a `\u{1}` in it is one this assembler made for a numbered label or a frame.
803fn unseen(name: &Name) -> bool {
804    name.binding == Binding::Local
805        && (name.name.starts_with(".L")
806            || name.name.starts_with("..")
807            || name.name.contains('\u{1}'))
808}
809
810/// Every name in it a linker can find, which is what an archive's symbol index is built from.
811///
812/// The same rule as [`crate::defines`]: a local is left out, because a name the static link has
813/// already finished with is not one an archive may offer, and an undefined one is left out because
814/// this file does not have it.
815#[must_use]
816pub fn assembled_defines(input: &Assembled) -> Vec<String> {
817    input
818        .names
819        .iter()
820        .filter(|name| name.binding != Binding::Local && name.at != Held::Undefined)
821        .map(|name| name.name.clone())
822        .collect()
823}
824
825#[cfg(test)]
826mod tests {
827    use super::*;
828
829    use object::read::elf::{FileHeader as _, Sym as _};
830    use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
831    use object::{RelocationFlags, SectionFlags};
832    use rucc_target::{Arch as TargetArch, Env, Os, Triple};
833
834    use crate::section::Reference;
835
836    /// A linux x86-64 target, which is the one most of these are written against.
837    fn target() -> TargetInfo {
838        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Linux, Env::Gnu))
839    }
840
841    /// The same machine under mingw-w64, which is the target the COFF cases below are about.
842    fn windows() -> TargetInfo {
843        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Windows, Env::Gnu))
844    }
845
846    /// One section of that name holding those bytes, with the flags the name implies.
847    fn part(name: &str, bytes: Vec<u8>) -> Part {
848        Part {
849            name: name.to_owned(),
850            size: bytes.len() as u64,
851            bytes,
852            align: 1,
853            shape: Shape::of(name),
854            relocs: Vec::new(),
855            group: None,
856        }
857    }
858
859    /// One name at an offset into the first section.
860    fn at(name: &str, offset: u64, sort: Sort, binding: Binding) -> Name {
861        Name {
862            name: name.to_owned(),
863            at: Held::In { part: 0, offset },
864            size: 0,
865            sort,
866            binding,
867            visibility: Visibility::Default,
868        }
869    }
870
871    /// The raw `st_info` and `st_value` of a symbol, as the file holds them.
872    ///
873    /// The reader's own `kind()`, `is_global()` and `address()` are a translation of these, and a
874    /// translation is what several of the cases below are about, so they ask the file rather than
875    /// the reading. A common symbol is the clearest of them: `address()` gives zero for one because
876    /// it has no address, and the field an ordinary symbol keeps its address in is where a common
877    /// one states the boundary it has to start on.
878    fn raw(bytes: &[u8], want: &str) -> (u8, u64) {
879        let header = elf::FileHeader64::<Endianness>::parse(bytes).expect("a header");
880        let endian = header.endian().expect("an endianness");
881        let table = header.sections(endian, bytes).expect("the sections");
882        let symbols = table.symbols(endian, bytes, elf::SHT_SYMTAB).expect("a symbol table");
883        for symbol in symbols.iter() {
884            if symbols.symbol_name(endian, symbol).expect("a name") == want.as_bytes() {
885                return (symbol.st_info().0, symbol.st_value(endian));
886            }
887        }
888        panic!("there is no symbol called '{want}'");
889    }
890
891    /// The first half of that.
892    fn st_info(bytes: &[u8], want: &str) -> u8 {
893        raw(bytes, want).0
894    }
895
896    #[test]
897    fn a_section_carries_the_flags_the_source_said_and_not_the_ones_its_name_suggests() {
898        // The whole reason a shape is separate facts rather than a kind. A program may write
899        // `.section .init.text,"ax"` and mean a section with a name this compiler has never heard
900        // of, and what it said about it is the letters.
901        let mut odd = part(".init.text", vec![0x90]);
902        odd.shape = Shape { alloc: true, exec: true, bits: true, ..Shape::default() };
903        let input = Assembled { parts: vec![odd], names: Vec::new(), subsections: false };
904        let bytes = assembled(&input, &target()).expect("an object");
905        let file = object::File::parse(&bytes[..]).expect("a readable object");
906        let section = file.section_by_name(".init.text").expect("the section");
907        assert_eq!(section.data().expect("the bytes"), &[0x90]);
908        let SectionFlags::Elf { sh_flags, sh_type } = section.flags() else {
909            panic!("this is an ELF file");
910        };
911        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_EXECINSTR.0);
912        assert_eq!(sh_flags.0 & elf::SHF_WRITE.0, 0, "nothing said it was writable");
913        assert_eq!(sh_type, elf::SHT_PROGBITS);
914    }
915
916    #[test]
917    fn a_section_that_holds_no_bytes_still_says_how_long_it_is() {
918        // `.bss` is a length and no bytes, and a writer that appended its data would produce a file
919        // with that much zero in it, which is the difference between an object and a big object.
920        let mut room = part(".bss", Vec::new());
921        room.size = 4096;
922        room.align = 16;
923        let input = Assembled { parts: vec![room], names: Vec::new(), subsections: false };
924        let bytes = assembled(&input, &target()).expect("an object");
925        assert!(bytes.len() < 4096, "the empty space was written out: {} bytes", bytes.len());
926        let file = object::File::parse(&bytes[..]).expect("a readable object");
927        let section = file.section_by_name(".bss").expect("the section");
928        assert_eq!(section.size(), 4096);
929        assert_eq!(section.align(), 16);
930        let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("an ELF file") };
931        assert_eq!(sh_type, elf::SHT_NOBITS);
932    }
933
934    #[test]
935    fn a_label_nobody_stated_a_type_for_is_a_symbol_with_no_type() {
936        // `STT_NOTYPE` is what gas writes for one, and it is a real answer rather than a missing
937        // one. The writer underneath refuses a defined symbol whose kind is `Unknown` outright, so
938        // this is also the case that says the mapping went to `Label` and not there.
939        let input = Assembled {
940            parts: vec![part(".text", vec![0; 8])],
941            names: vec![at("plain", 4, Sort::Untyped, Binding::Global)],
942            subsections: false,
943        };
944        let bytes = assembled(&input, &target()).expect("an object");
945        let file = object::File::parse(&bytes[..]).expect("a readable object");
946        let plain = file.symbols().find(|s| s.name() == Ok("plain")).expect("the label");
947        assert_eq!(plain.address(), 4);
948        assert_eq!(st_info(&bytes, "plain") & 0xf, elf::STT_NOTYPE.0);
949    }
950
951    #[test]
952    fn what_type_said_is_what_the_symbol_gets() {
953        let input = Assembled {
954            parts: vec![part(".text", vec![0; 8])],
955            names: vec![
956                at("run", 0, Sort::Func, Binding::Global),
957                at("held", 4, Sort::Object, Binding::Local),
958            ],
959            subsections: false,
960        };
961        let bytes = assembled(&input, &target()).expect("an object");
962        assert_eq!(st_info(&bytes, "run") & 0xf, elf::STT_FUNC.0);
963        assert_eq!(st_info(&bytes, "held") & 0xf, elf::STT_OBJECT.0);
964        assert_eq!(st_info(&bytes, "run") >> 4, elf::STB_GLOBAL.0);
965        assert_eq!(st_info(&bytes, "held") >> 4, elf::STB_LOCAL.0);
966    }
967
968    #[test]
969    fn a_common_symbol_is_written_the_way_gas_writes_one() {
970        // The writer underneath records `STT_COMMON` and gas records `STT_OBJECT` for the same
971        // `.comm`. Both are a request for storage and a linker takes either, and the one gas writes
972        // is the one written here, so an object of ours and an object of theirs do not differ in a
973        // field somebody's tool reads years from now.
974        let input = Assembled {
975            parts: Vec::new(),
976            names: vec![Name {
977                name: "shared".to_owned(),
978                at: Held::Common { size: 8, align: 8 },
979                size: 0,
980                sort: Sort::Object,
981                binding: Binding::Global,
982                visibility: Visibility::Default,
983            }],
984            subsections: false,
985        };
986        let bytes = assembled(&input, &target()).expect("an object");
987        assert_eq!(st_info(&bytes, "shared"), elf::STB_GLOBAL.0 << 4 | elf::STT_OBJECT.0);
988        let file = object::File::parse(&bytes[..]).expect("a readable object");
989        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the symbol");
990        assert!(shared.is_common(), "the linker has to be asked for the space");
991        assert_eq!(shared.size(), 8);
992        // Where an ordinary symbol keeps its address, which is why the two cannot both be said.
993        assert_eq!(raw(&bytes, "shared").1, 8, "the boundary it has to start on");
994    }
995
996    #[test]
997    fn a_set_is_a_number_rather_than_a_place() {
998        let input = Assembled {
999            parts: vec![part(".text", vec![0; 8])],
1000            names: vec![Name {
1001                name: "size_of_it".to_owned(),
1002                at: Held::Absolute(25),
1003                size: 0,
1004                sort: Sort::Untyped,
1005                binding: Binding::Global,
1006                visibility: Visibility::Default,
1007            }],
1008            subsections: false,
1009        };
1010        let bytes = assembled(&input, &target()).expect("an object");
1011        let file = object::File::parse(&bytes[..]).expect("a readable object");
1012        let sym = file.symbols().find(|s| s.name() == Ok("size_of_it")).expect("the symbol");
1013        assert_eq!(sym.address(), 25);
1014        assert_eq!(sym.section(), object::SymbolSection::Absolute, "it is not in any section");
1015    }
1016
1017    #[test]
1018    fn a_relocation_names_a_symbol_and_lands_where_the_bytes_are() {
1019        let mut data = part(".data", vec![0; 8]);
1020        data.relocs.push(Reloc {
1021            at: 0,
1022            symbol: "message".to_owned(),
1023            kind: Reference::Address { bytes: 8 },
1024            addend: 0,
1025            after: 0,
1026        });
1027        let input = Assembled {
1028            parts: vec![data],
1029            names: vec![Name {
1030                name: "message".to_owned(),
1031                at: Held::Undefined,
1032                size: 0,
1033                sort: Sort::Untyped,
1034                binding: Binding::Global,
1035                visibility: Visibility::Default,
1036            }],
1037            subsections: false,
1038        };
1039        let bytes = assembled(&input, &target()).expect("an object");
1040        let file = object::File::parse(&bytes[..]).expect("a readable object");
1041        let section = file.section_by_name(".data").expect("the section");
1042        let (at, reloc) = section.relocations().next().expect("one relocation");
1043        assert_eq!(at, 0);
1044        assert_eq!(reloc.addend(), 0);
1045        let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("an ELF file") };
1046        assert_eq!(r_type, elf::R_X86_64_64);
1047    }
1048
1049    #[test]
1050    fn a_place_only_this_file_sees_is_reached_through_its_section_as_gas_does() {
1051        // The `.L` label goes, the static function stays in the table, and both relocations are
1052        // against `.text` at their offsets. A call keeps its name, since the linker may give it a
1053        // stub, and so does a name the linker is allowed to see.
1054        let mut text = part(".text", vec![0; 32]);
1055        for (at, symbol, kind) in [
1056            (0, ".L3", Reference::Data),
1057            (4, "helper", Reference::Data),
1058            (8, "helper", Reference::Call),
1059            (12, "shared", Reference::Data),
1060        ] {
1061            let symbol = symbol.to_owned();
1062            text.relocs.push(Reloc { at, symbol, kind, addend: -4, after: 0 });
1063        }
1064        let input = Assembled {
1065            parts: vec![text],
1066            names: vec![
1067                at(".L3", 20, Sort::Untyped, Binding::Local),
1068                at("helper", 24, Sort::Func, Binding::Local),
1069                at("shared", 28, Sort::Func, Binding::Global),
1070            ],
1071            subsections: false,
1072        };
1073        let bytes = assembled(&input, &target()).expect("an object");
1074        let file = object::File::parse(&bytes[..]).expect("a readable object");
1075        let names: Vec<_> = file.symbols().filter_map(|sym| sym.name().ok()).collect();
1076        assert!(!names.contains(&".L3") && names.contains(&"helper"), "{names:?}");
1077        let section = file.section_by_name(".text").expect("the section");
1078        let reached: Vec<_> = section
1079            .relocations()
1080            .map(|(at, reloc)| {
1081                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1082                    panic!("a symbol")
1083                };
1084                let symbol = file.symbol_by_index(index).expect("the symbol");
1085                let name = if symbol.kind() == object::SymbolKind::Section {
1086                    ".text"
1087                } else {
1088                    symbol.name().expect("a name")
1089                };
1090                (at, name, reloc.addend())
1091            })
1092            .collect();
1093        assert_eq!(
1094            reached,
1095            [(0, ".text", 16), (4, ".text", 20), (8, "helper", -4), (12, "shared", -4)]
1096        );
1097    }
1098
1099    #[test]
1100    fn a_section_of_constants_may_be_merged_and_a_distance_into_it_keeps_its_name() {
1101        let mut text = part(".text", vec![0; 8]);
1102        text.relocs.push(Reloc {
1103            at: 0,
1104            symbol: ".LC0".to_owned(),
1105            kind: Reference::Data,
1106            addend: -4,
1107            after: 0,
1108        });
1109        let strings = Part {
1110            shape: Shape { merge: 1, strings: true, ..Shape::of(".rodata") },
1111            ..part(".rodata.str1.1", b"hi\0".to_vec())
1112        };
1113        let mut name = at(".LC0", 0, Sort::Untyped, Binding::Local);
1114        name.at = Held::In { part: 1, offset: 0 };
1115        let input = Assembled { parts: vec![text, strings], names: vec![name], subsections: false };
1116        let bytes = assembled(&input, &target()).expect("an object");
1117        let file = object::File::parse(&bytes[..]).expect("a readable object");
1118        let section = file.section_by_name(".rodata.str1.1").expect("the section");
1119        let SectionFlags::Elf { sh_flags, .. } = section.flags() else { panic!("an ELF file") };
1120        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_MERGE.0 | elf::SHF_STRINGS.0);
1121        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
1122        let endian = header.endian().expect("an endianness");
1123        let table = header.sections(endian, &bytes[..]).expect("the sections");
1124        let (_, found) = table.section_by_name(endian, b".rodata.str1.1").expect("the section");
1125        assert_eq!(found.sh_entsize.get(endian), 1);
1126        let text = file.section_by_name(".text").expect("the section");
1127        let (_, reloc) = text.relocations().next().expect("one relocation");
1128        let object::RelocationTarget::Symbol(index) = reloc.target() else { panic!("a symbol") };
1129        assert_eq!(file.symbol_by_index(index).and_then(|sym| sym.name()), Ok(".LC0"));
1130    }
1131
1132    #[test]
1133    fn a_relocation_against_a_name_the_file_never_mentions_is_refused() {
1134        // Rather than written against symbol zero, which is a file that links and resolves the
1135        // reference to address zero. The list of names is the whole of what the reader found, so a
1136        // relocation naming something outside it is a mistake in this compiler.
1137        let mut data = part(".data", vec![0; 8]);
1138        data.relocs.push(Reloc {
1139            at: 0,
1140            symbol: "nowhere".to_owned(),
1141            kind: Reference::Address { bytes: 8 },
1142            addend: 0,
1143            after: 0,
1144        });
1145        let input = Assembled { parts: vec![data], names: Vec::new(), subsections: false };
1146        let why = assembled(&input, &target()).expect_err("this cannot be written");
1147        assert!(format!("{why}").contains("nowhere"), "{why}");
1148    }
1149
1150    #[test]
1151    fn the_stack_is_marked_once_whoever_asked_for_it() {
1152        // A linker that does not find this marker in every input marks the stack executable, and a
1153        // file written by hand for one that cares often says it itself.
1154        let bare = Assembled {
1155            parts: vec![part(".text", vec![0x90])],
1156            names: Vec::new(),
1157            subsections: false,
1158        };
1159        let bytes = assembled(&bare, &target()).expect("an object");
1160        let file = object::File::parse(&bytes[..]).expect("a readable object");
1161        assert!(file.section_by_name(".note.GNU-stack").is_some(), "the marker was left out");
1162
1163        let said = Assembled {
1164            parts: vec![part(".text", vec![0x90]), part(".note.GNU-stack", Vec::new())],
1165            names: Vec::new(),
1166            subsections: false,
1167        };
1168        let bytes = assembled(&said, &target()).expect("an object");
1169        let file = object::File::parse(&bytes[..]).expect("a readable object");
1170        let marks = file.sections().filter(|s| s.name() == Ok(".note.GNU-stack")).count();
1171        assert_eq!(marks, 1, "the file said it and it was said again");
1172    }
1173
1174    #[test]
1175    fn only_the_names_a_linker_could_find_are_offered_to_an_archive() {
1176        let input = Assembled {
1177            parts: vec![part(".text", vec![0; 8])],
1178            names: vec![
1179                at("reachable", 0, Sort::Func, Binding::Global),
1180                at("mine", 4, Sort::Func, Binding::Local),
1181                Name {
1182                    name: "elsewhere".to_owned(),
1183                    at: Held::Undefined,
1184                    size: 0,
1185                    sort: Sort::Untyped,
1186                    binding: Binding::Global,
1187                    visibility: Visibility::Default,
1188                },
1189            ],
1190            subsections: false,
1191        };
1192        assert_eq!(assembled_defines(&input), vec!["reachable".to_owned()]);
1193    }
1194
1195    #[test]
1196    fn a_machine_this_does_not_write_is_refused_rather_than_written_wrong() {
1197        let input = Assembled {
1198            parts: vec![part(".text", vec![0x90])],
1199            names: Vec::new(),
1200            subsections: false,
1201        };
1202        let elsewhere = TargetInfo::new(Triple::new(TargetArch::Riscv64, Os::Linux, Env::Gnu));
1203        let why = assembled(&input, &elsewhere).expect_err("this cannot be written");
1204        assert!(format!("{why}").contains("riscv64"), "{why}");
1205    }
1206
1207    #[test]
1208    fn a_file_of_assembly_for_aarch64_is_written_with_that_machine_s_relocations() {
1209        // `adrp x0, table` and `add x0, x0, :lo12:table+8`, then `bl g`, then the address of
1210        // `table` in a table of its own. A field is its fixup's relocation and an address is the
1211        // AArch64 one of that width, and a label only this file sees is written against its
1212        // section, the way gas writes it.
1213        let mut text = part(".text", vec![0; 12]);
1214        let field = |at, symbol: &str, fixup, addend| Reloc {
1215            at,
1216            symbol: symbol.to_owned(),
1217            kind: Reference::Field(fixup),
1218            addend,
1219            after: 0,
1220        };
1221        text.relocs = vec![
1222            field(0, ".Ltable", Fixup::AdrPage21, 8),
1223            field(4, ".Ltable", Fixup::AddLo12, 8),
1224            field(8, "g", Fixup::Call26, 0),
1225        ];
1226        let mut data = part(".data", vec![0; 16]);
1227        data.relocs = vec![Reloc {
1228            at: 8,
1229            symbol: ".Ltable".to_owned(),
1230            kind: Reference::Address { bytes: 8 },
1231            addend: 0,
1232            after: 0,
1233        }];
1234        let mut table = at(".Ltable", 0, Sort::Object, Binding::Local);
1235        table.at = Held::In { part: 1, offset: 0 };
1236        let input = Assembled {
1237            parts: vec![text, data],
1238            names: vec![
1239                table,
1240                Name { at: Held::Undefined, ..at("g", 0, Sort::Untyped, Binding::Global) },
1241            ],
1242            subsections: false,
1243        };
1244        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Linux, Env::Gnu));
1245        let bytes = assembled(&input, &target).expect("an object");
1246        let file = object::File::parse(&bytes[..]).expect("a readable object");
1247        assert_eq!(file.architecture(), Architecture::Aarch64);
1248        let relocs = |name: &str| -> Vec<(u64, elf::RelocationType, i64)> {
1249            let section = file.section_by_name(name).expect("the section");
1250            section
1251                .relocations()
1252                .map(|(at, reloc)| {
1253                    let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1254                    (at, r_type, reloc.addend())
1255                })
1256                .collect()
1257        };
1258        assert_eq!(
1259            relocs(".text"),
1260            [
1261                (0, elf::R_AARCH64_ADR_PREL_PG_HI21, 8),
1262                (4, elf::R_AARCH64_ADD_ABS_LO12_NC, 8),
1263                (8, elf::R_AARCH64_CALL26, 0)
1264            ]
1265        );
1266        assert_eq!(relocs(".data"), [(8, elf::R_AARCH64_ABS64, 0)]);
1267        assert!(file.symbols().all(|s| s.name() != Ok(".Ltable")), "a label only this file sees");
1268    }
1269
1270    #[test]
1271    fn a_file_of_assembly_for_aarch64_windows_carries_its_addends_in_the_instructions() {
1272        // The same instructions as the ELF test above, and a load of the eighth byte of the table,
1273        // a `bl` to a name, then the two halves of an offset into `.tls`. COFF has no addend field,
1274        // so the eight goes into the page `adrp` names, into the low twelve bits `add` carries and,
1275        // divided by the size of the access, into the offset of the load. A distance written as
1276        // data is `REL32`, which counts from the end of its four bytes, so four more is in them.
1277        let adrp = 0x9000_0000u32;
1278        let add = 0x9100_0000u32;
1279        let ldr = 0xf940_0000u32;
1280        let bl = 0x9400_0000u32;
1281        let words = [adrp, add, ldr, bl, add | 1 << 22, add];
1282        let mut text = part(".text", words.iter().flat_map(|word| word.to_le_bytes()).collect());
1283        let field = |at, symbol: &str, fixup, addend| Reloc {
1284            at,
1285            symbol: symbol.to_owned(),
1286            kind: Reference::Field(fixup),
1287            addend,
1288            after: 0,
1289        };
1290        text.relocs = vec![
1291            field(0, "table", Fixup::AdrPage21, 8),
1292            field(4, "table", Fixup::AddLo12, 8),
1293            field(8, "table", Fixup::Ldst64Lo12, 8),
1294            field(12, "g", Fixup::Call26, 0),
1295            field(16, "counter", Fixup::SecrelHigh12A, 0),
1296            field(20, "counter", Fixup::SecrelLow12A, 0),
1297        ];
1298        let mut data = part(".data", vec![0; 16]);
1299        data.relocs = vec![
1300            Reloc {
1301                at: 0,
1302                symbol: "table".to_owned(),
1303                kind: Reference::Address { bytes: 8 },
1304                addend: 8,
1305                after: 0,
1306            },
1307            Reloc { at: 8, symbol: "g".to_owned(), kind: Reference::Data, addend: 0, after: 0 },
1308            Reloc {
1309                at: 12,
1310                symbol: "table".to_owned(),
1311                kind: Reference::Image,
1312                addend: 0,
1313                after: 0,
1314            },
1315        ];
1316        let mut table = at("table", 0, Sort::Object, Binding::Global);
1317        table.at = Held::In { part: 1, offset: 0 };
1318        let undefined =
1319            |name| Name { at: Held::Undefined, ..at(name, 0, Sort::Untyped, Binding::Global) };
1320        let input = Assembled {
1321            parts: vec![text, data],
1322            names: vec![table, undefined("g"), undefined("counter")],
1323            subsections: false,
1324        };
1325        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1326        let bytes = assembled(&input, &target).expect("an object");
1327        let file = object::File::parse(&bytes[..]).expect("a readable object");
1328        assert_eq!(file.format(), object::BinaryFormat::Coff);
1329        assert_eq!(file.architecture(), Architecture::Aarch64);
1330        let relocs = |name: &str| -> Vec<(u64, u16, String)> {
1331            let section = file.section_by_name(name).expect("the section");
1332            section
1333                .relocations()
1334                .map(|(at, reloc)| {
1335                    let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
1336                    let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
1337                        panic!("a symbol")
1338                    };
1339                    let symbol = file.symbol_by_index(symbol).expect("the symbol");
1340                    (at, typ.0, symbol.name().expect("a name").to_owned())
1341                })
1342                .collect()
1343        };
1344        let named = |at, typ: pe::RelocationType, name: &str| (at, typ.0, name.to_owned());
1345        assert_eq!(
1346            relocs(".text"),
1347            [
1348                named(0, pe::IMAGE_REL_ARM64_PAGEBASE_REL21, "table"),
1349                named(4, pe::IMAGE_REL_ARM64_PAGEOFFSET_12A, "table"),
1350                named(8, pe::IMAGE_REL_ARM64_PAGEOFFSET_12L, "table"),
1351                named(12, pe::IMAGE_REL_ARM64_BRANCH26, "g"),
1352                named(16, pe::IMAGE_REL_ARM64_SECREL_HIGH12A, "counter"),
1353                named(20, pe::IMAGE_REL_ARM64_SECREL_LOW12A, "counter"),
1354            ]
1355        );
1356        assert_eq!(
1357            relocs(".data"),
1358            [
1359                named(0, pe::IMAGE_REL_ARM64_ADDR64, "table"),
1360                named(8, pe::IMAGE_REL_ARM64_REL32, "g"),
1361                named(12, pe::IMAGE_REL_ARM64_ADDR32NB, "table"),
1362            ]
1363        );
1364        let text = file.section_by_name(".text").expect("the section");
1365        let text = text.data().expect("the bytes");
1366        let word = |nth: usize| u32::from_le_bytes(text[nth * 4..nth * 4 + 4].try_into().unwrap());
1367        assert_eq!(word(0), adrp | 2 << 5, "eight bytes is immhi two and immlo nothing");
1368        assert_eq!(word(1), add | 8 << 10);
1369        assert_eq!(word(2), ldr | 1 << 10, "eight bytes is one doubleword");
1370        assert_eq!([word(3), word(4), word(5)], [bl, add | 1 << 22, add]);
1371        let data = file.section_by_name(".data").expect("the section");
1372        let data = data.data().expect("the bytes");
1373        assert_eq!(data[..8], 8u64.to_le_bytes());
1374        assert_eq!(data[8..12], 4u32.to_le_bytes());
1375    }
1376
1377    #[test]
1378    fn an_addend_an_aarch64_coff_field_cannot_carry_is_refused() {
1379        // A load of four bytes cannot be told to start two bytes in, since its offset is counted in
1380        // fours, and a branch to a name and a number is not something the field can say for every
1381        // linker. Both are refused rather than written as something close.
1382        for (word, fixup, addend) in
1383            [(0xb940_0000u32, Fixup::Ldst32Lo12, 2), (0x9400_0000, Fixup::Call26, 4)]
1384        {
1385            let mut text = part(".text", word.to_le_bytes().to_vec());
1386            text.relocs = vec![Reloc {
1387                at: 0,
1388                symbol: "g".to_owned(),
1389                kind: Reference::Field(fixup),
1390                addend,
1391                after: 0,
1392            }];
1393            let input = Assembled {
1394                parts: vec![text],
1395                names: vec![Name {
1396                    at: Held::Undefined,
1397                    ..at("g", 0, Sort::Untyped, Binding::Global)
1398                }],
1399                subsections: false,
1400            };
1401            let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1402            let why = assembled(&input, &target).expect_err("a field that cannot say it");
1403            assert!(format!("{why}").contains(fixup.name()), "{why}");
1404        }
1405    }
1406
1407    #[test]
1408    fn a_file_of_assembly_for_windows_is_written_as_coff() {
1409        // What tamnd/rucc#1514 was about. `runtime/builtins/chkstk.S` is a file of assembly for a
1410        // Windows target, and until this it was refused with a message about there being no object
1411        // writer for the triple, which read as the whole back end being missing rather than this
1412        // one path through it.
1413        let input = Assembled {
1414            parts: vec![part(".text", vec![0xc3])],
1415            names: Vec::new(),
1416            subsections: false,
1417        };
1418        let bytes = assembled(&input, &windows()).expect("an object");
1419        let file = object::File::parse(&bytes[..]).expect("a readable object");
1420        assert_eq!(file.format(), object::BinaryFormat::Coff);
1421        let section = file.section_by_name(".text").expect("the section");
1422        assert_eq!(section.data().expect("the bytes"), &[0xc3]);
1423        assert_eq!(section.kind(), SectionKind::Text);
1424        assert!(
1425            file.section_by_name(".note.GNU-stack").is_none(),
1426            "a format with no marker got one anyway"
1427        );
1428    }
1429
1430    #[test]
1431    fn a_coff_section_keeps_the_letters_it_was_given_and_its_comdat() {
1432        // `.section .drectve,"yni"` in chkstk.S, and the `.refptr.` pointers mingw-w64 wants one
1433        // copy of across every object that has one.
1434        let drectve = Part {
1435            shape: Shape::coff("yni").expect("the letters"),
1436            ..part(".drectve", b" -exclude-symbols:f".to_vec())
1437        };
1438        let refptr = Part {
1439            shape: Shape::coff("dr").expect("the letters"),
1440            group: Some(Group { symbol: ".refptr.x".to_owned(), keep: Keep::Any }),
1441            ..part(".rdata$.refptr.x", vec![0; 8])
1442        };
1443        let input = Assembled {
1444            parts: vec![refptr, drectve],
1445            names: vec![at(".refptr.x", 0, Sort::Object, Binding::Global)],
1446            subsections: false,
1447        };
1448        let bytes = assembled(&input, &windows()).expect("an object");
1449        let file = object::File::parse(&bytes[..]).expect("a readable object");
1450        let flags = |name: &str| match file.section_by_name(name).expect("the section").flags() {
1451            SectionFlags::Coff { characteristics } => characteristics.0,
1452            other => panic!("{other:?}"),
1453        };
1454        let removed = pe::IMAGE_SCN_LNK_REMOVE.0 | pe::IMAGE_SCN_LNK_INFO.0;
1455        assert_eq!(flags(".drectve") & removed, removed);
1456        assert_eq!(flags(".drectve") & (pe::IMAGE_SCN_MEM_READ.0 | pe::IMAGE_SCN_MEM_WRITE.0), 0);
1457        let refptr = flags(".rdata$.refptr.x");
1458        assert_ne!(refptr & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "not a COMDAT");
1459        assert_eq!(refptr & pe::IMAGE_SCN_MEM_WRITE.0, 0, "`r` did not take writing away");
1460        let comdat = file.comdats().next().expect("the COMDAT");
1461        assert_eq!(comdat.kind(), object::ComdatKind::Any);
1462        assert_eq!(file.symbol_by_index(comdat.symbol()).unwrap().name(), Ok(".refptr.x"));
1463
1464        // And one about a name the file never defines is refused rather than written broken.
1465        let input = Assembled { names: Vec::new(), ..input };
1466        assert!(assembled(&input, &windows()).is_err());
1467    }
1468
1469    #[test]
1470    fn a_global_label_with_no_type_under_it_is_still_offered_on_coff() {
1471        // The case a `.globl` and a label is, which is most of what a hand written file says. On
1472        // ELF that is `STT_NOTYPE` and the binding is a separate field, so the name is global
1473        // whatever its type. COFF has no such split: what the writer underneath calls a label is
1474        // storage class `LABEL`, which is a name inside one file, and a symbol written that way is
1475        // one no linker resolves against. `___chkstk_ms` came out of the archive as a local under
1476        // that mapping and mingw-w64's own objects went on wanting it.
1477        let input = Assembled {
1478            parts: vec![part(".text", vec![0; 8])],
1479            names: vec![
1480                at("offered", 0, Sort::Untyped, Binding::Global),
1481                at("ours", 4, Sort::Untyped, Binding::Local),
1482            ],
1483            subsections: false,
1484        };
1485        let bytes = assembled(&input, &windows()).expect("an object");
1486        let file = object::File::parse(&bytes[..]).expect("a readable object");
1487        let offered = file.symbols().find(|s| s.name() == Ok("offered")).expect("the label");
1488        assert!(offered.is_global(), "a `.globl` label came out local");
1489        let ours = file.symbols().find(|s| s.name() == Ok("ours")).expect("the other label");
1490        assert!(!ours.is_global(), "a label nothing offered came out global");
1491        // And the same input on ELF is still what gas writes there, which is the half of this that
1492        // would otherwise have been changed to fix the other half.
1493        let bytes = assembled(&input, &target()).expect("an object");
1494        assert_eq!(st_info(&bytes, "offered") & 0xf, elf::STT_NOTYPE.0);
1495    }
1496
1497    #[test]
1498    fn a_relocation_on_coff_says_how_much_of_the_instruction_comes_after_it() {
1499        // The one real difference between the two formats' relocations. ELF folds the distance
1500        // between the hole and the end of the instruction into the addend and has one type. COFF
1501        // counts from the end of the instruction and has no addend field, so the count is in the
1502        // type: `IMAGE_REL_AMD64_REL32_4` is four bytes of immediate behind the displacement.
1503        let mut text = part(".text", vec![0; 16]);
1504        text.relocs.push(Reloc {
1505            at: 2,
1506            symbol: "elsewhere".to_owned(),
1507            kind: Reference::Data,
1508            addend: -8,
1509            after: 4,
1510        });
1511        let input = Assembled {
1512            parts: vec![text],
1513            names: vec![Name {
1514                name: "elsewhere".to_owned(),
1515                at: Held::Undefined,
1516                size: 0,
1517                sort: Sort::Untyped,
1518                binding: Binding::Global,
1519                visibility: Visibility::Default,
1520            }],
1521            subsections: false,
1522        };
1523        let bytes = assembled(&input, &windows()).expect("an object");
1524        let file = object::File::parse(&bytes[..]).expect("a readable object");
1525        let section = file.section_by_name(".text").expect("the section");
1526        let (at, reloc) = section.relocations().next().expect("the relocation");
1527        assert_eq!(at, 2);
1528        assert_eq!(
1529            reloc.flags(),
1530            RelocationFlags::Coff { typ: pe::RelocationType(pe::IMAGE_REL_AMD64_REL32.0 + 4) }
1531        );
1532    }
1533}