Skip to main content

rucc_object/
file.rs

1//! Relocatable objects, in whichever of the formats the target wants.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.3, which says the three formats are written
4//! through the [`object`] crate's writer with our own layer above it for the parts it does not
5//! model. This is that layer, and what it holds is the part `object` cannot decide: which
6//! relocation an instruction wants, what a symbol's binding and type are, and the sections a
7//! linker expects to find whether or not anything was put in them.
8//!
9//! # One layout and two sets of answers
10//!
11//! Which sections a file has, what goes in each of them, which symbol says where each thing is and
12//! what each relocation is against are the same questions for ELF and for COFF, and they have the
13//! same answers, so they are asked once here. What differs is a short list: the number a relocation
14//! is, the field a visibility goes in, the note saying what the file was built to have checked, and
15//! the marker whose absence makes the stack executable. [`Flavour`] is that list, and the answers
16//! are in [`crate::elf`] and [`crate::coff`] beside each other where they can be read against one
17//! another.
18//!
19//! The alternative was two writers, and the reason against it is what a second copy of a layout
20//! decays into: a fix to one of them is a fix to one platform, and which platform got it is
21//! whichever the person who found the bug was building for.
22//!
23//! # What is not here
24//!
25//! Mach-O. The formats disagree about more than their headers: an Apple symbol carries an
26//! underscore in front of the C name and Mach-O has no way to say how long a function is, wanting
27//! `.subsections_via_symbols` instead. It is written when the target that needs it is.
28//!
29//! Thread-local storage. Reaching a thread-local variable is a different instruction sequence per
30//! model and the back end writes none of them, so a module carrying one is refused before it
31//! reaches here rather than written as an ordinary variable in the wrong section.
32
33use std::collections::BTreeMap;
34
35use object::write::{
36    Comdat, Mangling, Object as Writer, Relocation, StandardSection, Symbol, SymbolId,
37    SymbolSection,
38};
39use object::{
40    Architecture, BinaryFormat, ComdatKind, Endianness, RelocationFlags, SectionFlags, SectionKind,
41    SymbolFlags, SymbolKind, SymbolScope,
42};
43use rucc_base::hash::{Map, Set};
44use rucc_target::{ObjectFormat, TargetInfo};
45use rucc_tuple::Arch;
46
47use crate::section::{
48    Alias, Apart, Array, Binding, Compress, Data, EXCEPT_TABLE, Export, Holds, Info, Object,
49    Output, Place, Property, Reference, Reloc, Sections, Text, Visibility,
50};
51use crate::{coff, elf};
52
53/// Which of the three formats is being written, and therefore which set of answers the questions
54/// this module cannot decide get.
55///
56/// A short list rather than a trait, because the list is short and closed: everything a format has
57/// an opinion about is a call to one of the methods below, so a format is an arm in each of them
58/// and the compiler names every one that was forgotten.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub(crate) enum Flavour {
61    /// Linux, the BSDs and the freestanding targets.
62    Elf,
63    /// Windows, under either of its two runtimes.
64    Coff,
65    /// Apple's platforms, which are written only from a file of assembly and only for AArch64 so
66    /// far, so [`Flavour::of`] does not give it and [`crate::assembled`] asks for it by name.
67    MachO,
68}
69
70impl Flavour {
71    /// Which one a target wants, and nothing for the two formats that are not written.
72    pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
73        match target.object_format {
74            ObjectFormat::Elf => Some(Flavour::Elf),
75            ObjectFormat::Coff => Some(Flavour::Coff),
76            ObjectFormat::MachO | ObjectFormat::Wasm => None,
77        }
78    }
79
80    /// The format the writer underneath is asked for.
81    pub(crate) fn binary(self) -> BinaryFormat {
82        match self {
83            Flavour::Elf => BinaryFormat::Elf,
84            Flavour::Coff => BinaryFormat::Coff,
85            Flavour::MachO => BinaryFormat::MachO,
86        }
87    }
88
89    /// Which relocation this reference is on this machine, or `None` for one this format has none
90    /// of there.
91    ///
92    /// `after` is how many bytes of the instruction come after the four the linker writes over,
93    /// which ELF has already folded into the addend and COFF wants told apart. See [`crate::Reloc`].
94    pub(crate) fn reloc(
95        self,
96        machine: Architecture,
97        reference: Reference,
98        after: u8,
99    ) -> Option<RelocationFlags> {
100        let flags = |r_type| RelocationFlags::Elf { r_type };
101        match (self, machine) {
102            (Flavour::Elf, Architecture::I386) => elf::r_type_i386(reference).map(flags),
103            (Flavour::Elf, Architecture::Aarch64) => elf::r_type_aarch64(reference).map(flags),
104            (Flavour::Elf, _) => elf::r_type(reference).map(flags),
105            (Flavour::Coff, Architecture::Aarch64) => {
106                coff::arm64(reference).map(|typ| RelocationFlags::Coff { typ })
107            }
108            (Flavour::Coff, Architecture::I386) => {
109                coff::i386(reference).map(|typ| RelocationFlags::Coff { typ })
110            }
111            (Flavour::Coff, _) => coff::reloc(reference, after),
112            (Flavour::MachO, _) => crate::macho::reloc(reference, 0).ok(),
113        }
114    }
115
116    /// The machine the writer underneath is asked for, for a target whose objects this writes in
117    /// this format, and nothing for one it does not.
118    ///
119    /// i386 is both. COFF for it has relocations of its own and a symbol decoration the other
120    /// machines do not, which [`Flavour::spell`] puts on.
121    pub(crate) fn machine(self, arch: Arch) -> Option<Architecture> {
122        match (self, arch) {
123            (Flavour::Elf | Flavour::Coff, Arch::X86_64) => Some(Architecture::X86_64),
124            (Flavour::Elf | Flavour::Coff, Arch::Aarch64) => Some(Architecture::Aarch64),
125            (Flavour::Elf | Flavour::Coff, Arch::X86) => Some(Architecture::I386),
126            _ => None,
127        }
128    }
129
130    /// The name a symbol the program named has in the file, given the name C gave it.
131    ///
132    /// The same name everywhere but COFF for i386, where a C name has an underscore in front. See
133    /// [`coff::decorate`]. The writer underneath would put one on as well, but on every name of a
134    /// function or a variable alike, which is wrong for a `__fastcall` one and for a pointer the
135    /// import library fills in, so it is told to leave names alone and the decoration is done here.
136    /// A name the compiler minted for a place inside a function is not a C name and is not asked.
137    pub(crate) fn spell(self, machine: Architecture, name: &str) -> String {
138        match (self, machine) {
139            (Flavour::Coff, Architecture::I386) => coff::decorate(name),
140            _ => name.to_owned(),
141        }
142    }
143
144    /// Say how far a name reaches beyond what its scope already said.
145    ///
146    /// Nothing on COFF, where a symbol has nowhere to keep it. A file built with
147    /// `-fvisibility=hidden` for Windows is a file where that flag changed nothing, which is what
148    /// gcc does there as well.
149    pub(crate) fn see(
150        self,
151        obj: &mut Writer<'_>,
152        id: SymbolId,
153        binding: Binding,
154        visibility: Visibility,
155    ) {
156        match self {
157            Flavour::Elf => elf::see(obj, id, binding, visibility),
158            Flavour::Coff => {}
159            // Hidden is the one visibility Mach-O has a bit for, which keeps a name out of the
160            // image's exports and lets every object in the link see it. Protected has none.
161            Flavour::MachO => {
162                if binding != Binding::Local && visibility == Visibility::Hidden {
163                    obj.symbol_mut(id).scope = SymbolScope::Linkage;
164                }
165            }
166        }
167    }
168
169    /// The section a variable the loader writes into before anything reads it goes in, when the
170    /// program asked for the half of it the linker keeps apart, or nothing for a format that has no
171    /// such half and puts one in ordinary read only data with the rest.
172    fn rel_ro_local(self) -> Option<&'static str> {
173        match self {
174            Flavour::Elf => elf::REL_RO_LOCAL,
175            Flavour::Coff => coff::REL_RO_LOCAL,
176            Flavour::MachO => None,
177        }
178    }
179
180    /// The type and flags a section of function addresses the startup code calls has, where the
181    /// format has something to say about it.
182    ///
183    /// Nothing on COFF, where such a section is refused by [`beyond`] before it reaches here rather
184    /// than written under a name nothing on that platform gathers.
185    /// What a relocation in a debug section is here, given whether it names another debug section.
186    ///
187    /// A four byte reference from one debug section into another is an offset from the front of
188    /// that section. ELF gets one from an address relocation against the section symbol, since the
189    /// debug sections all start at zero. COFF has a relocation of its own for it, because an address
190    /// there is one in the image and the debug sections are not placed in the image.
191    pub(crate) fn debug(self, kind: Reference, into_debug: bool) -> Reference {
192        match kind {
193            Reference::Address { bytes: 4 } if self == Flavour::Coff && into_debug => {
194                Reference::Section
195            }
196            kind => kind,
197        }
198    }
199
200    fn gathered(self, array: Array) -> Option<SectionFlags> {
201        match self {
202            Flavour::Elf => Some(elf::gathered(array)),
203            Flavour::Coff | Flavour::MachO => None,
204        }
205    }
206
207    /// The header fields a file of assembly stated about one of its own sections, where the format
208    /// has fields to put them in.
209    ///
210    /// ELF has one for each of the letters, so what the source wrote is written down as it stands
211    /// and the section kind handed to the writer alongside is only a summary of it. COFF has no
212    /// field the letters map onto one for one, and the characteristics the writer works out from
213    /// that kind are the ones every other Windows assembler produces, so there is nothing to add and
214    /// saying so is [`None`] rather than a word built out of guesses.
215    pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
216        match self {
217            Flavour::Elf => {
218                Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
219            }
220            Flavour::Coff => (shape.coff != 0).then_some(SectionFlags::Coff {
221                characteristics: object::pe::SectionFlags(shape.coff),
222            }),
223            Flavour::MachO => Some(SectionFlags::MachO {
224                flags: object::macho::SectionFlags(shape.mach),
225                reserved2: 0,
226            }),
227        }
228    }
229
230    /// What kind of symbol a name out of a file of assembly is, given what `.type` said about it and
231    /// how far it reaches.
232    ///
233    /// The binding is a parameter because on COFF the two are not separable. ELF keeps the type and
234    /// the binding in different halves of a byte, so a name that nothing stated a type for is
235    /// `STT_NOTYPE` whether it is local or global, and that is what gas writes for a plain label.
236    /// COFF has no type field of that sort: what the writer underneath calls a label is storage
237    /// class `LABEL`, which is a name inside this file and nothing a linker will resolve against, so
238    /// a `.globl` with no `.type` under it would quietly stop being offered. The kind with no
239    /// function type on it and an external storage class is the data one, which is what gas for this
240    /// platform writes for the same input, so that is what an untyped global becomes here.
241    ///
242    /// Mach-O keeps no type at all and the writer underneath has no label there, so a function is
243    /// text and everything else is data. A thread-local is data as well, because the kind the
244    /// writer has for one makes a descriptor for it and the listing has already written that.
245    pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
246        if self == Flavour::MachO {
247            return match sort {
248                crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
249                crate::source::Sort::File => SymbolKind::File,
250                _ => SymbolKind::Data,
251            };
252        }
253        match sort {
254            // An indirect function is text as far as the writer underneath goes, and the type it
255            // writes for one is put right afterwards. See [`elf::indirect`].
256            crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
257            crate::source::Sort::Object => SymbolKind::Data,
258            crate::source::Sort::Thread => SymbolKind::Tls,
259            crate::source::Sort::File => SymbolKind::File,
260            crate::source::Sort::Untyped => match (self, binding) {
261                (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
262                _ => SymbolKind::Label,
263            },
264        }
265    }
266
267    /// The marker a linker looks for in every input, where there is one.
268    pub(crate) fn marker(self, obj: &mut Writer<'_>) {
269        match self {
270            Flavour::Elf => elf::marker(obj),
271            Flavour::Coff => coff::marker(obj),
272            Flavour::MachO => {}
273        }
274    }
275
276    /// What the file says it was built to have checked, where the format has a way to say it.
277    ///
278    /// ELF writes a note the linker keeps only the agreed part of. A PE image says the same thing in
279    /// the header of the finished image rather than in its inputs, so an object carries nothing and
280    /// the instructions the flag asked for are in the text either way.
281    fn property(self, obj: &mut Writer<'_>, property: Property) {
282        if !property.any() {
283            return;
284        }
285        match self {
286            Flavour::Elf => {
287                let note = obj.section_id(StandardSection::GnuProperty);
288                let align = if obj.architecture() == Architecture::I386 { 4 } else { 8 };
289                obj.append_section_data(note, &elf::record(property, align), u64::from(align));
290            }
291            Flavour::Coff | Flavour::MachO => {}
292        }
293    }
294
295    /// Where the unwind table goes: the section the records are in and what it is aligned to, and
296    /// the second section holding what those records point at, on the format that keeps the two
297    /// apart.
298    fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
299        match self {
300            Flavour::Elf => (elf::FRAMES, None),
301            Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
302            Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
303        }
304    }
305
306    /// Anything that has to be written into the finished bytes rather than said to the writer.
307    fn finish(self, bytes: &mut [u8], ordered: &[String]) {
308        match self {
309            Flavour::Elf => elf::link(bytes, ordered),
310            Flavour::Coff | Flavour::MachO => {
311                debug_assert!(ordered.is_empty(), "a record this format cannot write");
312            }
313        }
314    }
315}
316
317/// Why an object file could not be written.
318#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum Error {
320    /// A machine or a platform this does not write objects for.
321    Format {
322        /// The triple that was asked for.
323        triple: String,
324    },
325    /// The writer refused something it was given, which is a bug here rather than in a program.
326    Refused {
327        /// What it said, already formatted.
328        why: String,
329    },
330}
331
332impl std::fmt::Display for Error {
333    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334        match self {
335            Error::Format { triple } => {
336                write!(f, "there is no object writer for {triple} in this compiler yet")
337            }
338            Error::Refused { why } => {
339                write!(f, "the object writer refused what it was given: {why}")
340            }
341        }
342    }
343}
344
345impl std::error::Error for Error {}
346
347/// One text section and the variables beside it, as a relocatable object in the target's format.
348///
349/// `info` is the debug sections, already encoded, and is empty in a build that asked for none.
350/// What it holds is bytes and relocations for the same reason [`Text::unwind`] is bytes: the
351/// format's answer is the producer's to give and what is left here is where the sections go.
352///
353/// # Errors
354///
355/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for
356/// anything the writer underneath objected to, which would be a bug here. An alias whose target
357/// this file does not define is refused the same way, since the front end is what reports that as
358/// a program's mistake and one reaching here means it did not. So is anything the target's format
359/// has no way to write, which for COFF is a thread-local variable, a reference through a table the
360/// platform does not have, a record of where a patcher's room is and a section the startup code is
361/// expected to gather. See [`Error`].
362pub fn write(
363    text: &Text,
364    data: &Data,
365    aliases: &[Alias],
366    target: &TargetInfo,
367    output: Output,
368    info: &Info,
369) -> Result<Vec<u8>, Error> {
370    let Output { sections, property, ident, .. } = output;
371    let Some((flavour, machine)) = written(target) else {
372        return Err(Error::Format { triple: target.tuple.to_string() });
373    };
374    if flavour == Flavour::Coff {
375        beyond(text, data)?;
376    }
377    let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
378    // The names go in as they are, and the one format and machine that decorates them has that
379    // done by `spell` rather than by the writer underneath.
380    obj.set_mangling(Mangling::None);
381    let spell = |name: &str| flavour.spell(machine, name).into_bytes();
382    // How wide an address is, which is how wide the records of addresses below are written.
383    let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
384    // The one that holds every function when they are not being split up. Asked for even when it
385    // will stay empty, because it is the section the writer underneath starts a file with anyway
386    // and gcc writes an empty `.text` under `-ffunction-sections` too.
387    let whole = obj.section_id(StandardSection::Text);
388    // And `.data` and `.bss` next to it, empty or not, because gas makes all three before it reads
389    // a line and every ELF object gcc hands it comes out with them. The kernel's section checks
390    // compare the two compilers' objects by the sections they have.
391    if flavour == Flavour::Elf {
392        obj.section_id(StandardSection::Data);
393        obj.section_id(StandardSection::UninitializedData);
394    }
395    if !sections.functions {
396        obj.append_section_data(whole, &text.bytes, u64::from(text.align));
397    }
398
399    // Every function defined here, then every variable, then every name either of them wanted that
400    // is not. A name is looked up rather than added twice, because two symbols with one name is
401    // not a file a linker accepts.
402    let mut symbols = BTreeMap::new();
403    // Where each function ended up, in the order they were written, so that a relocation inside
404    // one goes into the section that one is in and one that points at the start of one can be
405    // written against that section. The same list as `text.funcs` and in the same order, so the
406    // two are walked together below.
407    let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
408    // Which text section each record of where a patcher's room is belongs to, in the order the
409    // records were added, which is the order their headers come out in. See `link`.
410    let mut ordered: Vec<String> = Vec::new();
411    for func in &text.funcs {
412        // A section of its own, holding this function's bytes and nothing else, so the linker can
413        // drop it when nothing reaches it. The name is what gcc writes, and the leading `.text.`
414        // is not decoration: `--gc-sections` and the linker scripts that place code both match on
415        // it, and a section called something else would be placed by the catch all rule.
416        //
417        // The room a patcher was promised in front of the label goes in it too. Those bytes are
418        // the function's, they are just not under its name: the symbol is where the label was and
419        // the room is what came before, so a section holding one without the other would be a
420        // section a linker could place with the room missing.
421        let ahead = func.patch.map_or(0, |patch| patch.before);
422        let (section, at) = if sections.functions {
423            let name = format!(".text.{}", func.name).into_bytes();
424            let id = obj.add_section(Vec::new(), name, SectionKind::Text);
425            let bytes = &text.bytes[func.start - ahead..func.start + func.len];
426            obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
427            (id, ahead as u64)
428        } else {
429            (whole, func.start as u64)
430        };
431        // Where the room is, in a section of its own that says nothing else. What reads it is a
432        // tracer patching every function in an image at once, and what it needs is every address
433        // in one place: a stripped kernel has no symbol table to walk instead, which is the whole
434        // reason the list is written rather than worked out later.
435        //
436        // The address is a relocation rather than a number, because a function is at a fixed
437        // offset in its own section and where that section lands is the linker's answer. It is
438        // written against the section rather than against the function's own name so that it still
439        // points at the room when the room is in front of the name.
440        //
441        // One section per function even when they all point at the same text, which is what gas
442        // produces and what lets a linker throw the record away with the function. `SHF_LINK_ORDER`
443        // is what ties the two together and it needs a section index the writer underneath does not
444        // set, so `link` fills it in afterwards. See `link`.
445        if let Some(patch) = func.patch {
446            let base = if sections.functions { func.start - ahead } else { 0 };
447            let name = elf::PATCHABLE.as_bytes().to_vec();
448            let id = obj.add_section(Vec::new(), name, SectionKind::Data);
449            obj.section_mut(id).flags = elf::ordered();
450            obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
451            let symbol = obj.section_symbol(section);
452            let flags =
453                flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(
454                    || Error::Refused { why: "no relocation holds an address here".to_owned() },
455                )?;
456            relocate(
457                &mut obj,
458                id,
459                Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
460            )?;
461            ordered.push(if sections.functions {
462                format!(".text.{}", func.name)
463            } else {
464                ".text".to_owned()
465            });
466        }
467        let id = obj.add_symbol(Symbol {
468            name: spell(&func.name),
469            value: at,
470            size: func.len as u64,
471            kind: SymbolKind::Text,
472            scope: scope_of(func.binding),
473            weak: func.binding == Binding::Weak,
474            section: SymbolSection::Section(section),
475            flags: SymbolFlags::None,
476        });
477        flavour.see(&mut obj, id, func.binding, func.visibility);
478        symbols.insert(func.name.clone(), id);
479        split.push((section, at));
480    }
481
482    // The places inside a function that have names of their own, which is where a label whose
483    // address an image holds is. After the functions, because the section one goes in is the
484    // section of the function it is inside and that is what the walk above worked out.
485    for label in &text.labels {
486        let after = text.funcs.partition_point(|func| func.start <= label.at);
487        let Some(index) = after.checked_sub(1) else {
488            let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
489            return Err(Error::Refused { why });
490        };
491        let func = &text.funcs[index];
492        let (section, at) = if sections.functions {
493            // From the start of the section rather than from the symbol, which is the same
494            // correction a relocation inside a function gets below.
495            let base = func.start - func.patch.map_or(0, |patch| patch.before);
496            (split[index].0, (label.at - base) as u64)
497        } else {
498            (whole, label.at as u64)
499        };
500        let id = obj.add_symbol(Symbol {
501            name: label.name.clone().into_bytes(),
502            value: at,
503            // A label has no length. What is at it is the rest of the function, and a size here
504            // would be a claim that the bytes after it are a thing of their own.
505            size: 0,
506            kind: SymbolKind::Label,
507            // Never offered to another file. The name is one the compiler minted and what it
508            // points at is the middle of a function, so the only thing that resolves against it
509            // is the image in this same file that asked for it.
510            scope: SymbolScope::Compilation,
511            weak: false,
512            section: SymbolSection::Section(section),
513            flags: SymbolFlags::None,
514        });
515        symbols.insert(label.name.clone(), id);
516    }
517
518    // The profiler's calls `-mrecord-mcount` lists, one eight byte address each in one section for
519    // the whole file, which is what gcc writes: `.quad 1b` after every call, each in the same
520    // `__mcount_loc`, allocated and never written by the program. The address is against the
521    // section the call is in for the reason the patch record's is, so it survives a function being
522    // at an offset the linker picks.
523    if !text.mcount.is_empty() {
524        let name = crate::section::MCOUNT_LOC.as_bytes().to_vec();
525        let id = obj.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
526        let flags =
527            flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(|| {
528                Error::Refused { why: "no relocation holds an address here".to_owned() }
529            })?;
530        for &call in &text.mcount {
531            let after = text.funcs.partition_point(|func| func.start <= call);
532            let Some(index) = after.checked_sub(1) else {
533                let why = format!("a profiler call at {call} is in front of every function");
534                return Err(Error::Refused { why });
535            };
536            let func = &text.funcs[index];
537            let (section, at) = if sections.functions {
538                let base = func.start - func.patch.map_or(0, |patch| patch.before);
539                (split[index].0, call - base)
540            } else {
541                (whole, call)
542            };
543            let offset =
544                obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
545            let symbol = obj.section_symbol(section);
546            relocate(&mut obj, id, Relocation { offset, symbol, addend: at as i64, flags })?;
547        }
548    }
549
550    // Where each variable's image landed in the section it went into, kept because a relocation in
551    // an image counts from the start of the image and one in a file counts from the start of the
552    // section. A variable that is not in a section has no entry, since nothing in a merged one can
553    // hold a relocation: the linker is being asked for zeroed space rather than for an image.
554    let mut placed = Vec::with_capacity(data.objects.len());
555    // The sections the writer has no name of its own for, remembered by name so that every variable
556    // that wants one lands in the same one. The rest come back from `section_id`, which already
557    // answers with the section it made the first time it was asked.
558    let mut named = Map::default();
559    for object in &data.objects {
560        let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
561        // COFF says which section a group is with the section's own symbol, which carries the
562        // selection, and takes the first symbol after it in the table as the one the group is
563        // keyed on. So a pointer's section gets its symbol here, before the pointer's own name.
564        if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
565            obj.section_symbol(section);
566        }
567        let id = obj.add_symbol(Symbol {
568            name: spell(&object.name),
569            // A common symbol says what it wants rather than where it is, and what it wants is
570            // recorded where an ordinary symbol records its address.
571            value: if object.place == Place::Merged { object.align } else { offset },
572            size: object.size,
573            // A thread-local variable is a different kind of symbol rather than a symbol in a
574            // different section, and it has to be both: the kind is what a linker checks a
575            // relocation against, so a `R_X86_64_PC32` aimed at one is refused rather than
576            // resolved to an address that would have been one thread's and is nobody's.
577            kind: match object.place {
578                Place::Thread { .. } => SymbolKind::Tls,
579                _ => SymbolKind::Data,
580            },
581            scope: scope_of(object.binding),
582            weak: object.binding == Binding::Weak,
583            section,
584            flags: SymbolFlags::None,
585        });
586        flavour.see(&mut obj, id, object.binding, object.visibility);
587        // A pointer every object that reads the variable writes the same copy of, so the section
588        // it is in is one the linker keeps any one of and drops the rest, keyed on the pointer's
589        // own name. That is `discard` in the listing and `IMAGE_COMDAT_SELECT_ANY` here.
590        if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
591            obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
592        }
593        symbols.insert(object.name.clone(), id);
594        placed.push((section.id(), offset));
595    }
596
597    // The jump tables, which the code reaches by name and which reach the code in turn. Placed
598    // before any relocation of the text is added, since the instruction that reads one names it.
599    let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
600
601    // The distances between two labels, written into the images just placed. Both labels were
602    // added above with the section they are in and where in it, so the distance is the one value
603    // less the other, and it is a number only when the section is the same one.
604    for apart in &data.apart {
605        let (Some(section), offset) = placed[apart.object] else { continue };
606        let value = distance(&obj, &symbols, apart)?;
607        let bytes = usize::from(apart.bytes);
608        let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
609        let at = at + apart.at;
610        let image = obj.section_mut(section).data_mut();
611        image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
612    }
613
614    // A second name for something already added, which is where the alias's own binding is the
615    // only thing it does not take from what it points at: the target of one may be a `static` and
616    // the alias of it may not be. Before the loop below rather than after it, because a reference
617    // to the new name is a reference to something this file defines and would otherwise be added
618    // as a name this file wants from somewhere else.
619    for alias in aliases {
620        let Some(&id) = symbols.get(&alias.target) else {
621            let why =
622                format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
623            return Err(Error::Refused { why });
624        };
625        let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
626        let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
627        let id = obj.add_symbol(Symbol {
628            name: spell(&alias.name),
629            value,
630            size,
631            kind,
632            scope: scope_of(alias.binding),
633            weak: alias.binding == Binding::Weak,
634            section,
635            flags: SymbolFlags::None,
636        });
637        flavour.see(&mut obj, id, alias.binding, alias.visibility);
638        if alias.ifunc {
639            if flavour != Flavour::Elf {
640                let why = format!("'{}' is an indirect function, which only ELF has", alias.name);
641                return Err(Error::Refused { why });
642            }
643            elf::indirect(&mut obj, id);
644        }
645        symbols.insert(alias.name.clone(), id);
646    }
647
648    // Not the unwind table's, which name functions this file defines and are written against the
649    // section rather than against the name. A record for anything else is refused below, so a name
650    // added here for one would be a name nothing goes on to use.
651    // The names a declaration wrote `weak` on, which the link is allowed to leave undefined and
652    // whose references then read a zero address. The listing writes a `.weak` for each of the same
653    // names, so the two paths put the same entries in whether or not anything refers to one.
654    let weak: Set<&str> = data.weak.iter().map(String::as_str).collect();
655    let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
656    // The names something here reaches through the thread pointer, which is the one thing about an
657    // undefined name this file does know. A reference to a thread-local variable is a different kind
658    // of reference from a reference to an ordinary one and the code that makes it is already
659    // different, so the file has been told, and ELF wants the symbol to say so as well.
660    let thread: Set<&str> = relocs()
661        .filter(|reloc| reloc.kind == Reference::Thread)
662        .map(|reloc| reloc.symbol.as_str())
663        .collect();
664    let wanted: Vec<&String> =
665        relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
666    for name in wanted {
667        if symbols.contains_key(name) || tables.contains_key(name) {
668            continue;
669        }
670        let id = obj.add_symbol(Symbol {
671            name: spell(name),
672            value: 0,
673            size: 0,
674            // What kind of thing an undefined name is is not known here and does not have to be:
675            // a linker resolves an undefined symbol by its name, and the type of one that is not
676            // defined anywhere in this file is nothing this file can say. A thread-local one is the
677            // exception, and the linker makes it one. A reference to a thread-local variable is
678            // satisfied by an offset into a block rather than by an address, so the linker has to
679            // know which of the two it is being asked for before it has found the definition, and it
680            // refuses a link where one file says `STT_TLS` and another does not rather than picking
681            // one. That is tamnd/rucc#1461: libmpfr writes `__gmpfr_flags` in one file and reads it
682            // in a hundred others, and `ld` stopped at the first reader with a mismatch.
683            kind: if thread.contains(name.as_str()) {
684                SymbolKind::Tls
685            } else {
686                SymbolKind::Unknown
687            },
688            scope: SymbolScope::Dynamic,
689            weak: weak.contains(name.as_str()),
690            section: SymbolSection::Undefined,
691            flags: SymbolFlags::None,
692        });
693        symbols.insert(name.clone(), id);
694    }
695
696    for reloc in &text.relocs {
697        // Which function's bytes this one is in, which is the question only the split path has to
698        // ask: when there is one text section every offset in it is already the offset in it.
699        // Every relocation is inside some function, since the padding between two of them is
700        // instructions that do nothing and holds nothing a linker fills in.
701        let (section, at) = if sections.functions {
702            let after = text.funcs.partition_point(|func| func.start <= reloc.at);
703            let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
704                let why = format!("a relocation at {} is in front of every function", reloc.at);
705                return Err(Error::Refused { why });
706            };
707            // From the start of the section rather than from the symbol, and the two are not the
708            // same byte in a function with room in front of its label.
709            let base = func.start - func.patch.map_or(0, |patch| patch.before);
710            (split[after - 1].0, (reloc.at - base) as u64)
711        } else {
712            (whole, reloc.at as u64)
713        };
714        // The address of a jump table, which is against the section the table is in and not a
715        // name of its own, the way gas writes a reference to a `.L` label: such a name is not
716        // kept in the symbol table, so what the linker is told is the section and how far in.
717        if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
718            let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
719                Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
720            })?;
721            let symbol = obj.section_symbol(table);
722            let addend = reloc.addend + offset as i64;
723            relocate(&mut obj, section, Relocation { offset: at, symbol, addend, flags })?;
724            continue;
725        }
726        add(&mut obj, section, at, reloc, &symbols, flavour)?;
727    }
728
729    // The unwind table, if there is one. Its own section rather than part of the text, because it
730    // is read rather than run: the loader maps it and the linker gathers every input's into one
731    // table and builds the index the unwinder searches.
732    //
733    // Not on Windows for i386, which has no such table. A handler there is found by walking a
734    // chain of records the running code pushes onto its own stack, so a function that installs
735    // none needs nothing written about it, and `.pdata` is a section the loader of a 32 bit image
736    // does not read. What the rest of the file says is the same whether or not the producer
737    // described its frames.
738    let seh_free = flavour == Flavour::Coff && machine == Architecture::I386;
739    if !text.unwind.bytes.is_empty() && !seh_free {
740        let ((name, align), second) = flavour.tables();
741        // Four on a machine whose addresses are four bytes, which is what gas aligns the table to
742        // there.
743        let align = if machine == Architecture::I386 { 4 } else { align };
744        let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
745        obj.append_section_data(frames, &text.unwind.bytes, align);
746        // What the rows point at, on the format that keeps the descriptions in a section of their
747        // own, and a name for each of them, because a row reaches one through a relocation and a
748        // relocation names a symbol. The names are never offered to another file: what they point
749        // at is one function's prologue, described for the runtime of this program and nothing else.
750        let mut described = Map::default();
751        if !text.unwind.info.is_empty() {
752            let Some((name, align)) = second else {
753                let why = "an unwind table here is one section and it was given two".to_owned();
754                return Err(Error::Refused { why });
755            };
756            let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
757            obj.append_section_data(codes, &text.unwind.info, align);
758            for label in &text.unwind.labels {
759                let id = obj.add_symbol(Symbol {
760                    name: label.name.clone().into_bytes(),
761                    value: label.at as u64,
762                    size: 0,
763                    kind: SymbolKind::Label,
764                    scope: SymbolScope::Compilation,
765                    weak: false,
766                    section: SymbolSection::Section(codes),
767                    flags: SymbolFlags::None,
768                });
769                described.insert(label.name.clone(), id);
770            }
771        }
772        // The call site tables of the functions with a landing pad, which a record reaches through
773        // the section's own symbol and the table's offset in it, the same way gcc's records do.
774        // The personality routine's pointer is an ordinary data symbol of this file and is looked
775        // up with the rest below.
776        if !text.unwind.except.is_empty() {
777            let except =
778                obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
779            obj.append_section_data(except, &text.unwind.except, 4);
780            described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
781        }
782        for reloc in &text.unwind.relocs {
783            let found = described.get(&reloc.symbol).or_else(|| {
784                // Only a variable this file defines. A function is reached through its section
785                // below for the reasons given there, and a name defined somewhere else is refused
786                // there as well.
787                let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
788                if ours { symbols.get(&reloc.symbol) } else { None }
789            });
790            let (symbol, addend) = match found {
791                // A description in the section above, reached by its own name and needing no
792                // correction, since the name is at the description rather than at the front of the
793                // section it is in.
794                Some(&id) => (id, reloc.addend),
795                // A function, and against the section it is in rather than against its own name,
796                // which is the same reason the record of a patcher's room is written that way and
797                // one more besides. The section is the only one of the two that is settled here: a
798                // global name is answered at load time by whichever object defines it first, so a
799                // distance measured to one is not a distance the linker can work out, and it says
800                // so and stops. The effect was that nothing this compiler wrote could go into a
801                // shared library at all, because every function has a record and every record
802                // pointed at a name.
803                //
804                // A function defined elsewhere has no record here, so the lookup failing means the
805                // record is for something that is not a function in this file, and that is a bug
806                // rather than a shape to handle: the writer says what it was given rather than
807                // guessing.
808                None => {
809                    let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
810                    let Some((section, at)) = found.map(|i| split[i]) else {
811                        let why = format!(
812                            "'{}' has an unwind record and is not a function here",
813                            reloc.symbol
814                        );
815                        return Err(Error::Refused { why });
816                    };
817                    // Where the function starts inside its section, since the section symbol is
818                    // where the section starts and the two are the same byte only for the first
819                    // function in one.
820                    (obj.section_symbol(section), reloc.addend + at as i64)
821                }
822            };
823            let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
824                Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
825            })?;
826            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
827            relocate(&mut obj, frames, record)?;
828        }
829    }
830    // The debug information, if the build asked for any. One section per chunk under the name
831    // DWARF gives it, and none of them allocated: the loader does not map a debug section and
832    // nothing at run time reads one, which is what tells this apart from the unwind table, whose
833    // whole point is that a program walking its own stack can reach it.
834    //
835    // Every section is added before any relocation is, because a relocation in one of them names
836    // another as often as it names a function, and a name is resolved against the sections the
837    // file already has.
838    let mut named = Map::default();
839    for chunk in &info.chunks {
840        // An i386 file keeps each addend in the bytes of its section, which a compressed section
841        // no longer holds, so its debug sections are left as they are for now.
842        let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
843            info.compress
844        } else {
845            Compress::None
846        };
847        let id = crate::zlib::debug_section(&mut obj, chunk, how);
848        named.insert(chunk.name.as_str(), id);
849    }
850    for chunk in &info.chunks {
851        let section = named[chunk.name.as_str()];
852        for reloc in &chunk.relocs {
853            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
854                // Another debug section, reached by its own name. The distance is from the front
855                // of that section, which is what the section symbol is, so the addend stands.
856                Some(&id) => (obj.section_symbol(id), reloc.addend),
857                // A function, and against the section it is in rather than against its own name,
858                // for the reason the unwind table's records are written that way: a global name is
859                // answered at load time by whichever object defines it first, and a distance to
860                // one is not a distance a linker can work out.
861                None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
862                    Some(which) => {
863                        let (section, at) = split[which];
864                        (obj.section_symbol(section), reloc.addend + at as i64)
865                    }
866                    // Or a variable this file defines, which a `DW_TAG_variable` asks for the
867                    // address of. Against its section for the reason a function is, where it has
868                    // one. A variable the linker is being asked for zeroed space for has no
869                    // section to count from and nothing but its own name to ask by, which is the
870                    // one case here where the name goes in the relocation.
871                    None => {
872                        let found = data.objects.iter().position(|had| had.name == reloc.symbol);
873                        let Some(which) = found else {
874                            let why = format!(
875                                "'{}' is named by the debug information and is not defined here",
876                                reloc.symbol
877                            );
878                            return Err(Error::Refused { why });
879                        };
880                        match placed[which] {
881                            (Some(section), at) => {
882                                (obj.section_symbol(section), reloc.addend + at as i64)
883                            }
884                            (None, _) => (symbols[&reloc.symbol], reloc.addend),
885                        }
886                    }
887                },
888            };
889            let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
890            let flags = flavour
891                .reloc(machine, kind, reloc.after)
892                .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
893            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
894            relocate(&mut obj, section, record)?;
895        }
896    }
897    for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
898        let Some(section) = section else { continue };
899        for reloc in &object.relocs {
900            add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
901        }
902    }
903
904    // The names the DLL this file is linked into offers to others, as options for the linker in
905    // the one section COFF reads options from. Nothing at all where there are none, which is every
906    // file on every other format. See `Export`.
907    if !data.exports.is_empty() {
908        let options: String = data.exports.iter().map(Export::option).collect();
909        let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
910        obj.append_section_data(id, options.as_bytes(), 1);
911    }
912
913    // What the file was built to have checked, when it was built to have anything checked. Left
914    // out otherwise rather than written as a zero, because a linker treats a missing note and a
915    // note with no bits in it the same way and gcc writes nothing.
916    flavour.property(&mut obj, property);
917
918    // The string gas makes of gcc's `.ident`, with the zero byte gas puts in front of the first.
919    if let Some(ident) = ident.filter(|_| flavour == Flavour::Elf) {
920        let id = obj.add_section(Vec::new(), b".comment".to_vec(), SectionKind::OtherString);
921        let bytes = [&[0][..], ident.as_bytes(), &[0]].concat();
922        obj.append_section_data(id, &bytes, 1);
923    }
924
925    // Written rather than left out, because a linker that does not find it in every input marks
926    // the stack executable, on the format that has one.
927    flavour.marker(&mut obj);
928
929    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
930    flavour.finish(&mut bytes, &ordered);
931    Ok(bytes)
932}
933
934/// How far one label is from another, from the symbols [`write()`] added for them.
935///
936/// # Errors
937///
938/// [`Error::Refused`] for a label that is not here, for two that are in different sections, and
939/// for a distance too far for the width it is written in.
940fn distance(
941    obj: &Writer<'_>,
942    symbols: &BTreeMap<String, SymbolId>,
943    apart: &Apart,
944) -> Result<i64, Error> {
945    let find = |name: &str| match symbols.get(name) {
946        Some(&id) => Ok(obj.symbol(id)),
947        None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
948    };
949    let (to, from) = (find(&apart.to)?, find(&apart.from)?);
950    if to.section != from.section {
951        let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
952        return Err(Error::Refused { why });
953    }
954    let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
955    let bits = u32::from(apart.bytes) * 8;
956    if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
957        let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
958        return Err(Error::Refused { why });
959    }
960    Ok(value)
961}
962
963/// Everything in this module the target's format has no way to write, refused by name.
964///
965/// Each of these is something ELF has and COFF does not, and each would otherwise be written as the
966/// nearest thing rather than refused, which is worse: a thread-local variable written as an ordinary
967/// one is a program where every thread shares what the source said each would have its own copy of,
968/// and a constructor list under a name the Windows runtime does not gather is a program whose
969/// constructors never run. A message naming the feature is what the caller turns into a diagnostic,
970/// and the front end refusing first is what stops one ever being seen.
971///
972/// # Errors
973///
974/// [`Error::Refused`], naming the one it found first.
975fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
976    let why = |why: String| Err(Error::Refused { why });
977    if text.funcs.iter().any(|func| func.patch.is_some()) {
978        return why("a record of where a patcher's room is has no section flags here".to_owned());
979    }
980    for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
981        if matches!(
982            reloc.kind,
983            Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
984        ) {
985            return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
986        }
987    }
988    for object in &data.objects {
989        if matches!(object.place, Place::Thread { zero: true }) {
990            return why(format!(
991                "'{}' is zeroed thread-local storage, which is not here",
992                object.name
993            ));
994        }
995        let Place::Named(name, _) = &object.place else { continue };
996        if Array::of(name).is_some() {
997            return why(format!("'{name}' is not a list the startup code here gathers"));
998        }
999    }
1000    Ok(())
1001}
1002
1003/// Every name a linker can find in the object [`write()`] would write from the same input.
1004///
1005/// What asks for this is the archive writer. A static link resolves through the symbol index, so an
1006/// index entry has to name a symbol the member really defines: an entry for a name that is not in
1007/// the member is an archive the linker searches, pulls the member out of, and then still reports
1008/// the name undefined. So the list comes from the writer rather than from the caller, because the
1009/// writer is the only thing that knows what it wrote.
1010///
1011/// The names are the ones in the file, which is the C name on every format and machine this writes
1012/// except COFF for i386, where it has an underscore in front. That is why this asks about the target
1013/// it otherwise would not have to. See `Flavour::spell`.
1014///
1015/// Order is the functions, then the variables, then the aliases, each in the order the module held
1016/// them, which is the order [`write()`] adds the symbols in. A `static` is left out: it is a name the
1017/// link has already finished with by the time an archive is searched, and an index entry for one
1018/// would offer the linker a definition it is not allowed to use.
1019///
1020/// # Errors
1021///
1022/// [`Error::Format`] for a machine or a platform this does not write, which is the same refusal
1023/// [`write()`] gives and is here for the same reason: a list of undecorated names for a format whose
1024/// symbols carry an underscore is worse than no list at all.
1025pub fn defines(
1026    text: &Text,
1027    data: &Data,
1028    aliases: &[Alias],
1029    target: &TargetInfo,
1030) -> Result<Vec<String>, Error> {
1031    let Some((flavour, machine)) = written(target) else {
1032        return Err(Error::Format { triple: target.tuple.to_string() });
1033    };
1034    let spell = |name: &String| flavour.spell(machine, name);
1035    let names = text
1036        .funcs
1037        .iter()
1038        .filter(|func| func.binding != Binding::Local)
1039        .map(|func| spell(&func.name))
1040        .chain(
1041            data.objects
1042                .iter()
1043                .filter(|object| object.binding != Binding::Local)
1044                .map(|object| spell(&object.name)),
1045        )
1046        .chain(
1047            aliases
1048                .iter()
1049                .filter(|alias| alias.binding != Binding::Local)
1050                .map(|alias| spell(&alias.name)),
1051        )
1052        .collect();
1053    Ok(names)
1054}
1055
1056/// One variable's image into the section it belongs in, and where in that section it landed.
1057///
1058/// A zero filled variable takes as many bytes of the file as it is long on the way in and none on
1059/// the way out, which is the whole point of the section it goes in. A merged one goes in no section
1060/// at all: the linker is being asked for that much zeroed space under that name, and where it ends
1061/// up is the linker's answer rather than this file's.
1062fn put(
1063    obj: &mut Writer<'_>,
1064    object: &Object,
1065    named: &mut Map<String, object::write::SectionId>,
1066    sections: Sections,
1067    flavour: Flavour,
1068) -> (SymbolSection, u64) {
1069    // A section of its own, named after the variable and after the section it would have gone in,
1070    // which is what `-fdata-sections` asks for. A merged variable has no section to split and a
1071    // named one was named by the program, so both are left where they are: the first is a request
1072    // to the linker rather than an image, and the second would otherwise have the flag silently
1073    // overrule what the source said.
1074    if sections.data {
1075        if let Some(name) = object.place.split(&object.name) {
1076            let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
1077            let offset = if carries_no_bytes(&object.place) {
1078                obj.append_section_bss(section, object.size, object.align)
1079            } else {
1080                obj.append_section_data(section, &object.bytes, object.align)
1081            };
1082            return (SymbolSection::Section(section), offset);
1083        }
1084    }
1085    let section = match &object.place {
1086        Place::Written => obj.section_id(StandardSection::Data),
1087        Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
1088        // Read only after the loader has written it, which the writer knows as the relocatable
1089        // read only data section and which is `.data.rel.ro` on ELF. The `.local` half is a layout
1090        // hint the writer has no name for, so it is added by hand and remembered: asking again
1091        // would make a second section with the same name, and a file with one of those per variable
1092        // is a file whose section headers outweigh what they describe.
1093        Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
1094            Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
1095            None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
1096        },
1097        Place::Zero => obj.section_id(StandardSection::UninitializedData),
1098        // The writer's kind for these is the one that flags the section for merging as strings a
1099        // byte wide, and the name is ours, since the alignment is part of it.
1100        Place::Strings { align } => {
1101            made(obj, named, &Place::strings(*align), SectionKind::ReadOnlyString)
1102        }
1103        Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
1104        Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
1105        Place::Merged => return (SymbolSection::Common, 0),
1106        // A named section is the program's word for where this goes, and a program that names one
1107        // wants what it named rather than what would have been chosen. Its flags are what the
1108        // variable holds, which is the answer gcc gives, except for the three names the startup
1109        // code calls what it finds in, which have a section type of their own and are gathered by
1110        // the linker whether or not they carry it. Two variables naming one section share it, in
1111        // the order they were written, and the first one is what made it.
1112        Place::Named(name, _) => {
1113            let section = made(obj, named, name, kind_of(&object.place));
1114            if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
1115                obj.section_mut(section).flags = flags;
1116            }
1117            section
1118        }
1119        // A section of its own whatever the flags say, since it is the unit the linker keeps one
1120        // copy of. The name after the `$` is dropped by the linker when it sorts, so the pointer
1121        // ends up in `.rdata` with the rest of the read only data.
1122        Place::Pointer => {
1123            let name = format!(".rdata${}", object.name);
1124            made(obj, named, &name, SectionKind::ReadOnlyData)
1125        }
1126    };
1127    let offset = if carries_no_bytes(&object.place) {
1128        obj.append_section_bss(section, object.size, object.align)
1129    } else {
1130        obj.append_section_data(section, &object.bytes, object.align)
1131    };
1132    (SymbolSection::Section(section), offset)
1133}
1134
1135/// Every jump table of the text, in `.rodata`, each cell a distance the linker works out, giving
1136/// back the section each one went in and where in it, by the name the code gives it.
1137///
1138/// The section is `.rodata` for all of them, or `.rodata.` and the function's name under
1139/// `-fdata-sections`, which is where gcc puts a table in each case. Not split under
1140/// `-ffunction-sections` alone, which is gcc's answer too.
1141///
1142/// A cell is the distance from the front of the table to a block, and the block is in the text
1143/// while the table is not, so it is `R_X86_64_PC32` against the function's section with the block's
1144/// offset and the cell's own place in the table as the addend. Against the section rather than the
1145/// function's name for the reason the unwind records are: a global name may be answered by another
1146/// object at load time, and a linker refuses a distance to one.
1147fn tables(
1148    obj: &mut Writer<'_>,
1149    text: &Text,
1150    split: &[(object::write::SectionId, u64)],
1151    named: &mut Map<String, object::write::SectionId>,
1152    sections: Sections,
1153    flavour: Flavour,
1154) -> Result<Map<String, (object::write::SectionId, u64)>, Error> {
1155    let mut placed = Map::default();
1156    if text.tables.is_empty() {
1157        return Ok(placed);
1158    }
1159    if flavour != Flavour::Elf {
1160        let why = "a jump table outside the code is written on ELF only".to_owned();
1161        return Err(Error::Refused { why });
1162    }
1163    let machine = obj.architecture();
1164    let flags = flavour.reloc(machine, Reference::Away, 0).ok_or_else(|| Error::Refused {
1165        why: "no relocation is a distance from where it is written".to_owned(),
1166    })?;
1167    // How wide a cell that holds an address is, which is the width of an address.
1168    let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
1169    for table in &text.tables {
1170        let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1171            why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1172        })?;
1173        let section = if sections.data {
1174            let name = format!(".rodata.{}", func.name);
1175            made(obj, named, &name, SectionKind::ReadOnlyData)
1176        } else {
1177            obj.section_id(StandardSection::ReadOnlyData)
1178        };
1179        // An address a cell under the kernel code model, which is counted from the front of the
1180        // code section alone rather than from the cell.
1181        let (width, flags) = if table.absolute {
1182            let reference = Reference::Address { bytes: pointer };
1183            let wide = flavour.reloc(machine, reference, 0).ok_or_else(|| Error::Refused {
1184                why: format!("no relocation is an address in {pointer} bytes"),
1185            })?;
1186            (usize::from(pointer), wide)
1187        } else {
1188            (4, flags)
1189        };
1190        let offset =
1191            obj.append_section_data(section, &vec![0; width * table.cells.len()], width as u64);
1192        placed.insert(table.name.clone(), (section, offset));
1193        let (code, at) = split[table.func];
1194        let symbol = obj.section_symbol(code);
1195        for (index, &cell) in table.cells.iter().enumerate() {
1196            let place = (width * index) as u64;
1197            let addend = at as i64 + cell as i64 + if table.absolute { 0 } else { place as i64 };
1198            let record = Relocation { offset: offset + place, symbol, addend, flags };
1199            relocate(obj, section, record)?;
1200        }
1201    }
1202    Ok(placed)
1203}
1204
1205/// Whether the section this goes in says how big the variable is and holds none of its bytes.
1206///
1207/// Two of them, and they are the same answer twice: `.bss` is the image that is all zeros, and
1208/// `.tbss` is a thread's own copy of one. A section like this costs its size in the section header
1209/// and nothing in the file, which is what keeps a program with a large zeroed array small.
1210fn carries_no_bytes(place: &Place) -> bool {
1211    matches!(place, Place::Zero | Place::Thread { zero: true } | Place::Named(_, Holds::Zero))
1212}
1213
1214/// The section of this name, made the first time it is asked for and found afterwards.
1215///
1216/// Two variables the program put the same section name on belong in one section, the way two in
1217/// `.data` do. Asking the writer for a new one each time would make a second header with the same
1218/// name, which a linker takes and which makes a file with ten constructors in it carry ten section
1219/// headers describing eight bytes each. `section_id` does this already for the sections it has
1220/// names of its own for, and this is the same answer for the ones it does not.
1221fn made(
1222    obj: &mut Writer<'_>,
1223    named: &mut Map<String, object::write::SectionId>,
1224    name: &str,
1225    kind: SectionKind,
1226) -> object::write::SectionId {
1227    if let Some(section) = named.get(name) {
1228        return *section;
1229    }
1230    let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1231    named.insert(name.to_owned(), section);
1232    section
1233}
1234
1235/// What a section split off for one variable is, which is what the section it was split off from
1236/// was.
1237///
1238/// Splitting changes the name and nothing else. A variable that was going to be in a page the
1239/// loader maps read only is still in one, and a zero filled variable still costs the file nothing,
1240/// so the flags a linker reads off the section header have to come out the same as they would
1241/// have. The two kinds with no section of their own never reach here, and `Data` for them is a
1242/// value that is never used rather than a claim about either.
1243///
1244/// A section the program named is never split, and is what this says for the same reason: what
1245/// the variable holds is what the section header has to say about it.
1246fn kind_of(place: &Place) -> SectionKind {
1247    match place {
1248        Place::ReadOnly | Place::Pointer | Place::Named(_, Holds::ReadOnly) => {
1249            SectionKind::ReadOnlyData
1250        }
1251        Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1252        Place::Strings { .. } => SectionKind::ReadOnlyString,
1253        Place::Zero | Place::Named(_, Holds::Zero) => SectionKind::UninitializedData,
1254        Place::Thread { zero: false } => SectionKind::Tls,
1255        Place::Thread { zero: true } => SectionKind::UninitializedTls,
1256        Place::Written | Place::Merged | Place::Named(_, Holds::Written) => SectionKind::Data,
1257    }
1258}
1259
1260/// One relocation, `at` bytes into the section it ended up in.
1261///
1262/// The offset is worked out by the caller rather than here, because the two callers count from
1263/// different places: a relocation in an image counts from the start of that image and a relocation
1264/// in a function counts from the start of that function, and neither of those is where the section
1265/// begins once something else is in front of it.
1266fn add(
1267    obj: &mut Writer<'_>,
1268    section: object::write::SectionId,
1269    at: u64,
1270    reloc: &Reloc,
1271    symbols: &BTreeMap<String, SymbolId>,
1272    flavour: Flavour,
1273) -> Result<(), Error> {
1274    let flags = flavour
1275        .reloc(obj.architecture(), reloc.kind, reloc.after)
1276        .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1277    relocate(
1278        obj,
1279        section,
1280        Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1281    )
1282}
1283
1284/// Add one relocation, with its addend written into the bytes it covers on a machine whose
1285/// relocations have nowhere else to keep one.
1286///
1287/// ELF for i386 uses `SHT_REL`, whose entries are an offset, a symbol and a type and nothing more:
1288/// what is added to the symbol is whatever the bytes held before the linker got there, so a call
1289/// carries its minus four in the four bytes of the call itself, the way gas writes it. The writer
1290/// underneath does that for some of the types and refuses the rest, `R_386_GOT32X` among them, so
1291/// it is done here for all of them, and the writer is handed a relocation whose addend is nothing.
1292/// The bytes are overwritten rather than added to, because what is in them before the linker has
1293/// been is nothing a program meant.
1294///
1295/// Every other machine this writes keeps the addend in the relocation, and its relocations go to
1296/// the writer as they are.
1297///
1298/// # Errors
1299///
1300/// [`Error::Refused`] for a relocation past the end of its section, an addend that does not fit in
1301/// the bytes it goes in, and anything the writer underneath objected to.
1302pub(crate) fn relocate(
1303    obj: &mut Writer<'_>,
1304    section: object::write::SectionId,
1305    mut relocation: Relocation,
1306) -> Result<(), Error> {
1307    if let (Architecture::I386, RelocationFlags::Elf { r_type }) =
1308        (obj.architecture(), relocation.flags)
1309    {
1310        let Some(width) = elf::width_i386(r_type) else {
1311            let why = format!("relocation type {} has no width this writer knows", r_type.0);
1312            return Err(Error::Refused { why });
1313        };
1314        let addend = relocation.addend;
1315        let bits = 8 * width as u32;
1316        // An address is four bytes on i386 and wraps there, so taking up to four gigabytes off a
1317        // name lands on the same address as adding what is left. The kernel's `__pa` of a static
1318        // is the name less `PAGE_OFFSET`, which is 0xC0000000, and that is how doublefault_32.c
1319        // fills `cr3`. A narrower field takes what is added to a name only if it fits, as gas has
1320        // it.
1321        let least = if width == 4 { -(1i64 << bits) } else { -(1i64 << (bits - 1)) };
1322        if addend < least || addend >= 1i64 << bits {
1323            let why =
1324                format!("{addend} added to a name, and there are {width} bytes to keep it in");
1325            return Err(Error::Refused { why });
1326        }
1327        let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1328        let data = obj.section_mut(section).data_mut();
1329        let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1330            let why = format!("a relocation at {at} is past the end of its section");
1331            return Err(Error::Refused { why });
1332        };
1333        place.copy_from_slice(&addend.to_le_bytes()[..width]);
1334        relocation.addend = 0;
1335    }
1336    obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1337}
1338
1339/// The format and the machine a target's object is written in by [`write()`], and nothing for a
1340/// target it does not write.
1341///
1342/// x86-64 on both formats and i386 on ELF. AArch64 reaches an object through a listing only, which
1343/// [`crate::assembled`] writes.
1344fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1345    let flavour = Flavour::of(target)?;
1346    let machine = flavour.machine(target.tuple.arch())?;
1347    (machine != Architecture::Aarch64).then_some((flavour, machine))
1348}
1349
1350/// How far a name reaches, which is the one thing about a symbol ELF calls its binding.
1351///
1352/// `SymbolScope` is two facts in one word, and the trap is that the middle one is not the neutral
1353/// answer it reads as. The writer turns `Compilation` into a local symbol, and it turns the choice
1354/// between `Linkage` and `Dynamic` into `st_other`: `Linkage` is `STV_HIDDEN` and `Dynamic` is
1355/// `STV_DEFAULT`. So there is no way to say global and decline to say anything about visibility,
1356/// and picking the one whose name sounds like the smaller claim is picking hidden. That is what
1357/// tamnd/rucc#733 was.
1358///
1359/// `Dynamic` is what every global asks for here, and the visibility is said afterwards by
1360/// [`see`] rather than through this, so that nothing about `st_other` depends on reading one of
1361/// these four names the way its author meant it.
1362pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1363    match binding {
1364        Binding::Local => SymbolScope::Compilation,
1365        Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1366    }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371    use super::*;
1372
1373    use object::read::elf::Sym as _;
1374    use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1375    use object::{elf, pe};
1376    use rucc_target::{Arch, Env, Os, Triple};
1377
1378    use crate::elf::PATCHABLE;
1379    use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1380
1381    /// A linux x86-64 target, which is the one most of these are written against.
1382    fn target() -> TargetInfo {
1383        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1384    }
1385
1386    /// One function of that name, at that offset, that many bytes long, and visible that far.
1387    ///
1388    /// Visibility is the field these cases mostly have no opinion about, so it is the one the
1389    /// helper fills in and the two that do have an opinion write for themselves.
1390    fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1391        Extent {
1392            name,
1393            start,
1394            len,
1395            align: crate::FUNC_ALIGN,
1396            binding,
1397            visibility: Visibility::Default,
1398            patch: None,
1399            landings: Vec::new(),
1400        }
1401    }
1402
1403    /// A call to something outside the file, which is the shape every case here starts from.
1404    fn calling(name: &str) -> Text {
1405        Text {
1406            bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1407            funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1408            relocs: vec![Reloc {
1409                at: 1,
1410                symbol: name.to_owned(),
1411                kind: Reference::Call,
1412                addend: -4,
1413                after: 0,
1414            }],
1415            ..Text::default()
1416        }
1417    }
1418
1419    #[test]
1420    fn the_bytes_come_back_out_of_the_section_they_went_into() {
1421        let text = calling("puts");
1422        let bytes =
1423            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1424                .expect("an object");
1425        let file = object::File::parse(&bytes[..]).expect("a readable object");
1426        let section = file.section_by_name(".text").expect("a text section");
1427        assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1428    }
1429
1430    #[test]
1431    fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1432        let mut text = calling("puts");
1433        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1434        text.bytes.resize(17, 0x90);
1435        let bytes =
1436            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1437                .expect("an object");
1438        let file = object::File::parse(&bytes[..]).expect("a readable object");
1439        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1440        assert_eq!(g.address(), 16);
1441        assert_eq!(g.size(), 1);
1442        assert_eq!(g.kind(), SymbolKind::Text);
1443        assert!(g.is_global(), "nothing said otherwise about this one");
1444    }
1445
1446    #[test]
1447    fn a_function_no_other_file_can_see_is_a_local_symbol() {
1448        let mut text = calling("puts");
1449        text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1450        text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1451        text.bytes.resize(33, 0x90);
1452        let bytes =
1453            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1454                .expect("an object");
1455        let file = object::File::parse(&bytes[..]).expect("a readable object");
1456        let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1457        // A symbol the linker keeps and does not let another file reach, which is the whole of
1458        // what `static` on a function means and what two files each defining their own need.
1459        assert!(hidden.is_local(), "a static function must not be offered to the linker");
1460        assert!(!hidden.is_weak());
1461        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1462        assert!(shared.is_weak(), "a weak function has to be able to lose");
1463        assert!(shared.is_global());
1464    }
1465
1466    /// A global is `STV_DEFAULT`, so a shared library built from these objects exports something.
1467    ///
1468    /// The bug in tamnd/rucc#733. Every global came out `STV_HIDDEN`, which a static link does not
1469    /// look at, so nothing here noticed and SQLite linked and ran and the whole test suite passed.
1470    /// What it costs is the dynamic symbol table: `gcc -shared` over one of these objects produced
1471    /// a library with an empty one, and `dlsym` could not find a function the file plainly defines.
1472    ///
1473    /// Written against `st_other` itself rather than against the reader's `scope`, because `scope`
1474    /// is the word that was misread in the first place and a test that asks it the same question
1475    /// would agree with whatever the writer did.
1476    /// The record of where a patcher's room is, and what it says about it.
1477    ///
1478    /// Four things have to be right at once for a linker to take it: the flags, the alignment, the
1479    /// relocation and the section it says it is ordered after. The last of those is the one the
1480    /// writer underneath cannot say, so a zero there would be a file `ld` refuses and a test that
1481    /// only looked at the bytes would not see it.
1482    #[test]
1483    fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1484        let mut text = calling("puts");
1485        text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1486        text.funcs[0].start = 3;
1487        text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1488        text.relocs[0].at = 4;
1489        let bytes =
1490            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1491                .expect("an object");
1492        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1493        let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1494        assert_eq!(section.size(), 8, "one address, and this file defines one function");
1495        assert_eq!(section.align(), 8);
1496        let header = section.elf_section_header();
1497        assert_eq!(
1498            header.sh_flags.get(Endianness::Little),
1499            elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1500        );
1501        // Which is the whole point of the fixup: the index has to be the text section's own, and
1502        // the writer underneath had written a zero there.
1503        let index = file.section_by_name(".text").expect("a text section").index().0;
1504        assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1505        assert_ne!(index, 0);
1506
1507        // And the address, which is the front of the room rather than the function's own symbol.
1508        let [(at, reloc)] = &section.relocations().collect::<Vec<_>>()[..] else {
1509            panic!("one address in the record")
1510        };
1511        assert_eq!(*at, 0);
1512        assert_eq!(reloc.addend(), 0);
1513        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1514    }
1515
1516    /// And a file that asked for none has no such section, which is nearly every file.
1517    #[test]
1518    fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1519        let text = calling("puts");
1520        let bytes =
1521            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1522                .expect("an object");
1523        let file = object::File::parse(&bytes[..]).expect("a readable object");
1524        assert!(file.section_by_name(PATCHABLE).is_none());
1525    }
1526
1527    /// The same when each function is a section of its own, which is what a kernel builds with.
1528    ///
1529    /// Each record then points at a different section, which is what makes the pairing worth
1530    /// asserting: getting it backwards would still produce a file every tool reads and every
1531    /// address in it would be about the wrong function.
1532    #[test]
1533    fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1534        let mut text = calling("puts");
1535        text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1536        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1537        text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1538        text.bytes.resize(17, 0x90);
1539        let output =
1540            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1541        let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1542            .expect("an object");
1543        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1544        let links: Vec<usize> = file
1545            .sections()
1546            .filter(|section| section.name() == Ok(PATCHABLE))
1547            .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1548            .collect();
1549        let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1550        assert_eq!(links, [index(".text.f"), index(".text.g")]);
1551    }
1552
1553    #[test]
1554    fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1555        let mut text = calling("puts");
1556        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1557        text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1558        text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1559        text.bytes.resize(49, 0x90);
1560        let bytes =
1561            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1562                .expect("an object");
1563        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1564        let visibility = |name: &str| {
1565            file.symbols()
1566                .find(|s| s.name() == Ok(name))
1567                .expect("the function")
1568                .elf_symbol()
1569                .st_visibility()
1570        };
1571        // Nothing said hidden about either of these, so neither is.
1572        assert_eq!(visibility("g"), elf::STV_DEFAULT);
1573        assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1574        // The `static` one is local, and a local symbol's visibility means nothing either way,
1575        // which is why the binding is what this asks about.
1576        assert_eq!(visibility("s"), elf::STV_DEFAULT);
1577    }
1578
1579    /// And the other direction: a name that did ask to be hidden is hidden, and a protected one is
1580    /// protected.
1581    ///
1582    /// The half of tamnd/rucc#733 that the fix above left open. Saying `STV_DEFAULT` for everything
1583    /// is right for everything nobody marked and wrong the moment something is marked, so the two
1584    /// tests together are what says the field carries an answer rather than a constant.
1585    ///
1586    /// Both are asked of a function and of a variable, because they are added by two different
1587    /// loops in `write` and a field one of them fills in is not a field the other one does.
1588    #[test]
1589    fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1590        let mut text = calling("puts");
1591        for (index, (name, seen)) in
1592            [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1593        {
1594            let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1595            func.visibility = seen;
1596            text.funcs.push(func);
1597        }
1598        text.bytes.resize(49, 0x90);
1599        let mut data = Data::default();
1600        for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1601            let mut object = variable(name, Place::Written);
1602            object.visibility = seen;
1603            data.objects.push(object);
1604        }
1605        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1606            .expect("an object");
1607        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1608        let visibility = |name: &str| {
1609            file.symbols()
1610                .find(|s| s.name() == Ok(name))
1611                .expect("the symbol")
1612                .elf_symbol()
1613                .st_visibility()
1614        };
1615        assert_eq!(visibility("h"), elf::STV_HIDDEN);
1616        assert_eq!(visibility("p"), elf::STV_PROTECTED);
1617        assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1618        assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1619        // The one thing a visibility must not disturb, since `st_info` and `st_other` are written
1620        // in one go and the second was set after the first.
1621        let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1622        assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1623        assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1624    }
1625
1626    #[test]
1627    fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1628        let bytes = write(
1629            &calling("puts"),
1630            &Data::default(),
1631            &[],
1632            &target(),
1633            Output::default(),
1634            &Info::default(),
1635        )
1636        .expect("an object");
1637        let file = object::File::parse(&bytes[..]).expect("a readable object");
1638        let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1639        assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1640    }
1641
1642    #[test]
1643    fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1644        for (reference, wanted) in [
1645            (Reference::Call, elf::R_X86_64_PLT32),
1646            (Reference::Data, elf::R_X86_64_PC32),
1647            (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1648            (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1649            (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1650            (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1651        ] {
1652            let mut text = calling("puts");
1653            text.relocs[0].kind = reference;
1654            let bytes =
1655                write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1656                    .expect("an object");
1657            let file = object::File::parse(&bytes[..]).expect("a readable object");
1658            let section = file.section_by_name(".text").expect("a text section");
1659            let (offset, reloc) = section.relocations().next().expect("one relocation");
1660            assert_eq!(offset, 1);
1661            assert_eq!(reloc.addend(), -4);
1662            assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1663        }
1664    }
1665
1666    /// The relocations a kernel's hand written assembly asks for beyond what a compiler writes:
1667    /// eight bytes of distance for its jump table, and an address in one byte or two.
1668    #[test]
1669    fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1670        for (reference, wanted) in [
1671            (Reference::AwayWide, elf::R_X86_64_PC64),
1672            (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1673            (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1674        ] {
1675            assert_eq!(crate::elf::r_type(reference), Some(wanted));
1676        }
1677        assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1678    }
1679
1680    #[test]
1681    fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1682        let mut text = calling("puts");
1683        text.relocs.push(Reloc {
1684            at: 1,
1685            symbol: "puts".to_owned(),
1686            kind: Reference::Call,
1687            addend: -4,
1688            after: 0,
1689        });
1690        let bytes =
1691            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1692                .expect("an object");
1693        let file = object::File::parse(&bytes[..]).expect("a readable object");
1694        assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1695    }
1696
1697    #[test]
1698    fn a_function_that_is_also_called_is_not_a_second_symbol() {
1699        let text = calling("f");
1700        let bytes =
1701            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1702                .expect("an object");
1703        let file = object::File::parse(&bytes[..]).expect("a readable object");
1704        let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1705        let f = found.next().expect("the function");
1706        assert!(!f.is_undefined(), "the file defines it");
1707        assert!(found.next().is_none(), "and defines it once");
1708    }
1709
1710    #[test]
1711    fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1712        let bytes = write(
1713            &calling("puts"),
1714            &Data::default(),
1715            &[],
1716            &target(),
1717            Output::default(),
1718            &Info::default(),
1719        )
1720        .expect("an object");
1721        let file = object::File::parse(&bytes[..]).expect("a readable object");
1722        let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1723        assert!(note.data().expect("no bytes").is_empty());
1724    }
1725
1726    /// What the file says it was built to have checked, byte for byte.
1727    ///
1728    /// Written against the bytes rather than against a reader, because the two lengths in the
1729    /// header count the padding after what they measure and a note whose lengths are one word out
1730    /// is one a linker drops without saying anything. What comes of that is a program the loader
1731    /// leaves the check turned off for, which is a build that looks like it worked.
1732    #[test]
1733    fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1734        let property = Property { features: Property::IBT | Property::SHSTK };
1735        let output = Output { property, ..Output::default() };
1736        let bytes =
1737            write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1738                .expect("an object");
1739        let file = object::File::parse(&bytes[..]).expect("a readable object");
1740        let note = file.section_by_name(".note.gnu.property").expect("the note");
1741        assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1742        let want: Vec<u8> = [
1743            4u32,
1744            16,
1745            5,
1746            u32::from_le_bytes(*b"GNU\0"),
1747            Property::X86_FEATURES,
1748            4,
1749            Property::IBT | Property::SHSTK,
1750            0,
1751        ]
1752        .iter()
1753        .flat_map(|word| word.to_le_bytes())
1754        .collect();
1755        assert_eq!(note.data().expect("the bytes"), &want[..]);
1756    }
1757
1758    /// And nothing at all when the file was built to have nothing checked.
1759    ///
1760    /// A note with an empty feature word and no note are the same thing to a linker, which drops
1761    /// the whole property when any input lacks it. gcc writes nothing, so a section header that
1762    /// describes nothing would be the one difference between the two compilers' objects.
1763    #[test]
1764    fn a_file_built_to_have_nothing_checked_says_nothing() {
1765        let bytes = write(
1766            &calling("puts"),
1767            &Data::default(),
1768            &[],
1769            &target(),
1770            Output::default(),
1771            &Info::default(),
1772        )
1773        .expect("an object");
1774        let file = object::File::parse(&bytes[..]).expect("a readable object");
1775        assert!(file.section_by_name(".note.gnu.property").is_none());
1776    }
1777
1778    /// Every unwind record names the function it is about, and each name goes where it is in the
1779    /// table rather than at the start of it.
1780    ///
1781    /// Written because working the offset out is the caller's job here, which is what the two text
1782    /// paths differ about, and a third caller that let it default to nothing would put every record
1783    /// in the table on the same function. Nothing else would notice: the section is the right
1784    /// length, the symbols are right, the link succeeds, and what comes of it is an unwinder that
1785    /// walks out of the wrong frame the first time something throws or a backtrace is taken.
1786    #[test]
1787    fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1788        let mut text = calling("puts");
1789        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1790        text.bytes.resize(17, 0x90);
1791        // A shared header and two records, whose contents nothing here reads: what is being asked
1792        // is where in them each name landed.
1793        text.unwind.bytes = vec![0; 64];
1794        for (at, name) in [(32usize, "f"), (48usize, "g")] {
1795            text.unwind.relocs.push(Reloc {
1796                at,
1797                symbol: name.to_owned(),
1798                kind: Reference::Address { bytes: 8 },
1799                addend: 0,
1800                after: 0,
1801            });
1802        }
1803        let bytes =
1804            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1805                .expect("an object");
1806        let file = object::File::parse(&bytes[..]).expect("a readable object");
1807        let mut found = points_at(&file);
1808        found.sort_unstable();
1809        assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1810    }
1811
1812    /// What each record in the unwind table points at: where it is, the section it reaches, and
1813    /// how far into that section the function it is about begins.
1814    fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1815        let frames = file.section_by_name(".eh_frame").expect("the table");
1816        frames
1817            .relocations()
1818            .map(|(offset, reloc)| {
1819                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1820                    panic!("a record points at something that is not a symbol");
1821                };
1822                let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1823                assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1824                let section = symbol.section_index().expect("a section symbol is in one");
1825                let name = file.section_by_index(section).expect("a readable section");
1826                (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1827            })
1828            .collect()
1829    }
1830
1831    /// A record points at the section its function is in rather than at the function's name.
1832    ///
1833    /// Written for tamnd/rucc#1004, which was that nothing this compiler wrote could go into a
1834    /// shared library. A global name is answered at load time by whichever object defines it
1835    /// first, so the distance from a record to one of them is not a distance a static linker can
1836    /// work out, and `ld` says so and stops with advice to recompile with the flag that was
1837    /// already on the command line. A section is settled by then, which is why gcc measures to a
1838    /// local label and why this measures to the section.
1839    ///
1840    /// Both ways of splitting the text, because the offset is the part that differs: one section
1841    /// holding everything makes it the function's place in the whole text, and a section per
1842    /// function makes it whatever room a patcher was promised in front of the label.
1843    #[test]
1844    fn a_record_reaches_its_function_through_the_section_it_is_in() {
1845        let mut text = two();
1846        text.unwind.bytes = vec![0; 64];
1847        for (at, name) in [(32usize, "f"), (48usize, "g")] {
1848            text.unwind.relocs.push(Reloc {
1849                at,
1850                symbol: name.to_owned(),
1851                kind: Reference::Data,
1852                addend: 0,
1853                after: 0,
1854            });
1855        }
1856        let bytes =
1857            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1858                .expect("an object");
1859        let file = object::File::parse(&bytes[..]).expect("a readable object");
1860        let mut whole = points_at(&file);
1861        whole.sort_unstable();
1862        assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1863
1864        let sections =
1865            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1866        let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1867            .expect("an object");
1868        let file = object::File::parse(&bytes[..]).expect("a readable object");
1869        let mut split = points_at(&file);
1870        split.sort_unstable();
1871        assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1872    }
1873
1874    /// A record about a name this file does not define is refused rather than written.
1875    ///
1876    /// There is no such file today: the table is built beside the text out of the functions that
1877    /// were just compiled. It is refused rather than left to the linker because the alternative is
1878    /// the shape that was just fixed, a record measured to a name, and the writer saying what it
1879    /// was given is how that stays fixed.
1880    #[test]
1881    fn a_record_about_something_this_file_does_not_define_is_refused() {
1882        let mut text = calling("puts");
1883        text.unwind.bytes = vec![0; 64];
1884        text.unwind.relocs.push(Reloc {
1885            at: 32,
1886            symbol: "puts".to_owned(),
1887            kind: Reference::Data,
1888            addend: 0,
1889            after: 0,
1890        });
1891        let why =
1892            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1893                .expect_err("a record about a name from somewhere else");
1894        assert!(why.to_string().contains("puts"), "{why}");
1895    }
1896
1897    /// The name of the section that symbol is defined in.
1898    fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1899        let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1900        let index = symbol.section_index().expect("a section to be defined in");
1901        let section = file.section_by_index(index).expect("a readable section");
1902        section.name().expect("a named section").to_owned()
1903    }
1904
1905    /// Two functions, the second of them sixteen bytes in and calling something outside the file.
1906    fn two() -> Text {
1907        let mut text = calling("puts");
1908        // Padded to where the second one is aligned to, with the instruction that does nothing,
1909        // because the space in front of a function is reached by falling off the end of one.
1910        text.bytes.resize(16, 0x90);
1911        text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1912        text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1913        text.relocs.push(Reloc {
1914            at: 17,
1915            symbol: "puts".to_owned(),
1916            kind: Reference::Call,
1917            addend: -4,
1918            after: 0,
1919        });
1920        text
1921    }
1922
1923    /// What `-ffunction-sections` comes down to in an object file, which is the flag that makes
1924    /// `--gc-sections` able to drop anything: a linker can leave out a section nothing reaches and
1925    /// cannot leave out half of one.
1926    ///
1927    /// The empty `.text` stays, because it is the section the writer underneath opens a file with
1928    /// and gcc 16 leaves an empty one behind under the flag too.
1929    #[test]
1930    fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1931        let sections =
1932            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1933        let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1934            .expect("an object");
1935        let file = object::File::parse(&bytes[..]).expect("a readable object");
1936        assert_eq!(lives_in(&file, "f"), ".text.f");
1937        assert_eq!(lives_in(&file, "g"), ".text.g");
1938        assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1939        // Each one at nothing into its own section, and as long as it was: a function alone in a
1940        // section starts where the section does, whatever it started at when they shared one.
1941        for name in ["f", "g"] {
1942            let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1943            assert_eq!(symbol.address(), 0, "{name}");
1944            assert_eq!(symbol.size(), 6, "{name}");
1945        }
1946        let section = file.section_by_name(".text.g").expect("the second function");
1947        assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1948        // The padding between the two is gone with them, since it was there to align the second
1949        // one inside a section they shared and each section is aligned by the linker now.
1950        assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1951    }
1952
1953    /// A relocation counts from the start of whichever section its function ended up in, which is
1954    /// the arithmetic the split path has to do and the unsplit one never does.
1955    ///
1956    /// Getting it wrong is a call patched over the wrong bytes, which assembles, links, and jumps
1957    /// into the middle of an instruction at run time.
1958    #[test]
1959    fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1960        let sections =
1961            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1962        let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1963            .expect("an object");
1964        let file = object::File::parse(&bytes[..]).expect("a readable object");
1965        for name in [".text.f", ".text.g"] {
1966            let section = file.section_by_name(name).expect("a function");
1967            let (offset, _) = section.relocations().next().expect("the call in it");
1968            // One byte in either way, because the call is the first instruction of both and the
1969            // opcode is one byte in front of the address the linker fills in.
1970            assert_eq!(offset, 1, "{name}");
1971            assert_eq!(section.relocations().count(), 1, "{name}");
1972        }
1973    }
1974
1975    /// The second of `two` with a table of two cells, to its first byte and to its return.
1976    fn switching() -> Text {
1977        let mut text = two();
1978        let name = ".Lg_j0".to_owned();
1979        text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
1980        text
1981    }
1982
1983    /// Where each relocation of that section is, what it is against and what it adds.
1984    fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1985        let section = file.section_by_name(section).expect("the table's section");
1986        section
1987            .relocations()
1988            .map(|(offset, reloc)| {
1989                assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1990                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1991                    panic!("a cell against something that is not a symbol");
1992                };
1993                let symbol = file.symbol_by_index(index).expect("a symbol");
1994                assert_eq!(symbol.kind(), SymbolKind::Section);
1995                let at = symbol.section_index().expect("a section symbol is in one");
1996                let name = file.section_by_index(at).expect("a section").name().expect("a name");
1997                (offset, name.to_owned(), reloc.addend())
1998            })
1999            .collect()
2000    }
2001
2002    #[test]
2003    fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
2004        // And the code reaches it by the name the table was given, which here is the second of the
2005        // two references in `two`.
2006        let mut text = switching();
2007        text.relocs[1].symbol = ".Lg_j0".to_owned();
2008        text.relocs[1].kind = Reference::Data;
2009        let bytes =
2010            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2011                .expect("an object");
2012        let file = object::File::parse(&bytes[..]).expect("a readable object");
2013        let rodata = file.section_by_name(".rodata").expect("the table's section");
2014        assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2015        assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2016        assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2017        let (at, reloc) = file
2018            .section_by_name(".text")
2019            .expect("the code")
2020            .relocations()
2021            .find(|(at, _)| *at == 17)
2022            .expect("the reference to the table");
2023        assert_eq!((at, reloc.addend()), (17, -4));
2024        let object::RelocationTarget::Symbol(index) = reloc.target() else {
2025            panic!("a reference against something that is not a symbol");
2026        };
2027        let symbol = file.symbol_by_index(index).expect("a symbol");
2028        assert_eq!(symbol.section_index(), Some(rodata.index()));
2029        assert_eq!(symbol.kind(), SymbolKind::Section);
2030        // `g` starts sixteen bytes into `.text`, and each cell is its block's place in the text
2031        // and its own place in the table, so that the linker's answer is block less table.
2032        assert_eq!(
2033            cells(&file, ".rodata"),
2034            [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2035        );
2036    }
2037
2038    #[test]
2039    fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2040        let sections =
2041            Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2042        let bytes =
2043            write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2044                .expect("an object");
2045        let file = object::File::parse(&bytes[..]).expect("a readable object");
2046        // Against the function's own section now, where it starts at nothing.
2047        assert_eq!(
2048            cells(&file, ".rodata.g"),
2049            [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2050        );
2051    }
2052
2053    #[test]
2054    fn a_jump_table_outside_the_code_is_refused_on_windows() {
2055        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2056        let written = write(
2057            &switching(),
2058            &Data::default(),
2059            &[],
2060            &target,
2061            Output::default(),
2062            &Info::default(),
2063        );
2064        assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2065    }
2066
2067    /// Debug information on Windows: an offset into another debug section is a section relative
2068    /// relocation, and an address in the code is still an address.
2069    #[test]
2070    fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2071        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2072        let reloc = |at, symbol: &str, bytes| Reloc {
2073            at,
2074            symbol: symbol.to_owned(),
2075            kind: Reference::Address { bytes },
2076            addend: 0,
2077            after: 0,
2078        };
2079        let info = Info {
2080            chunks: vec![
2081                Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2082                Chunk {
2083                    name: ".debug_info".to_owned(),
2084                    bytes: vec![0; 12],
2085                    relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2086                },
2087            ],
2088            ..Info::default()
2089        };
2090        let bytes =
2091            write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2092                .expect("object");
2093        let file = object::File::parse(&bytes[..]).expect("a readable object");
2094        let section = file.section_by_name(".debug_info").expect("the debug section");
2095        let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2096        assert_eq!(
2097            kinds,
2098            [
2099                (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2100                (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2101            ]
2102        );
2103    }
2104
2105    /// One variable of four bytes, in whichever section its own answer puts it.
2106    fn variable(name: &str, place: Place) -> Object {
2107        Object {
2108            name: name.to_owned(),
2109            bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2110            size: 4,
2111            align: 4,
2112            place,
2113            binding: Binding::Global,
2114            visibility: Visibility::Default,
2115            relocs: Vec::new(),
2116        }
2117    }
2118
2119    /// Two labels in `f` and an image holding the distance between them each way round.
2120    fn measured() -> (Text, Data) {
2121        let mut text = calling("puts");
2122        text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2123        text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2124        let mut table = variable("table", Place::ReadOnly);
2125        table.bytes = vec![0; 8];
2126        table.size = 8;
2127        let apart = |at, to: &str, from: &str| Apart {
2128            object: 0,
2129            at,
2130            to: to.to_owned(),
2131            from: from.to_owned(),
2132            addend: 0,
2133            bytes: 4,
2134        };
2135        let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2136        (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2137    }
2138
2139    #[test]
2140    fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2141        let (text, data) = measured();
2142        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2143            .expect("an object");
2144        let file = object::File::parse(&bytes[..]).expect("a readable object");
2145        let section = file.section_by_name(".rodata").expect("a read only section");
2146        assert_eq!(section.relocations().count(), 0);
2147        let image = section.data().expect("the image");
2148        assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2149    }
2150
2151    #[test]
2152    fn a_distance_between_labels_in_two_sections_is_refused() {
2153        // `.L1` moves to a second function, which `-ffunction-sections` puts in a section of its
2154        // own, and then no number is the distance.
2155        let (mut text, data) = measured();
2156        text.bytes.resize(22, 0x90);
2157        text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2158        text.labels[1].at = 17;
2159        let output =
2160            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2161        let refused = write(&text, &data, &[], &target(), output, &Info::default());
2162        assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2163    }
2164
2165    /// A file of that one variable and nothing else.
2166    fn holding(object: Object) -> Vec<u8> {
2167        let data = Data {
2168            apart: Vec::new(),
2169            exports: Vec::new(),
2170            weak: Vec::new(),
2171            objects: vec![object],
2172        };
2173        write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2174            .expect("an object")
2175    }
2176
2177    #[test]
2178    fn what_a_variable_is_decides_which_section_it_goes_in() {
2179        for (place, wanted) in [
2180            (Place::Written, ".data"),
2181            (Place::ReadOnly, ".rodata"),
2182            (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2183            (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2184            (Place::Zero, ".bss"),
2185            (Place::Thread { zero: false }, ".tdata"),
2186            (Place::Thread { zero: true }, ".tbss"),
2187            (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2188        ] {
2189            let bytes = holding(variable("x", place.clone()));
2190            let file = object::File::parse(&bytes[..]).expect("a readable object");
2191            let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2192            assert_eq!(section.size(), 4, "{place:?}");
2193            // The zero filled one is as long as it says and carries none of it, which is the
2194            // whole reason the section exists.
2195            let carried = section.data().expect("the bytes").len();
2196            assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2197        }
2198    }
2199
2200    /// The section is half of it and the symbol is the other half.
2201    ///
2202    /// A linker checks a relocation against the kind of the symbol it names, so a variable that is
2203    /// in `.tdata` and is an ordinary data symbol is one an ordinary reference resolves to an
2204    /// address that belongs to no thread. `STT_TLS` is what makes that reference an error instead.
2205    #[test]
2206    fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2207        for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2208            let bytes = holding(variable("counter", place.clone()));
2209            let file = object::File::parse(&bytes[..]).expect("a readable object");
2210            let symbol = file
2211                .symbols()
2212                .find(|symbol| symbol.name() == Ok("counter"))
2213                .unwrap_or_else(|| panic!("{place:?}"));
2214            assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2215        }
2216    }
2217
2218    /// The section type a startup list carries, which is what makes the CRT call what is in it.
2219    ///
2220    /// A section of the ordinary type with the right name is gathered by the linker in the same run
2221    /// and called by nobody, so the type is the whole of what this is about. The numbered name is
2222    /// the same kind of section as the plain one: the number is there so that the linker sorts it.
2223    #[test]
2224    fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2225        for (name, wanted) in [
2226            (".init_array", elf::SHT_INIT_ARRAY),
2227            (".init_array.00101", elf::SHT_INIT_ARRAY),
2228            (".fini_array", elf::SHT_FINI_ARRAY),
2229            (".preinit_array", elf::SHT_PREINIT_ARRAY),
2230            (".init_arrays", elf::SHT_PROGBITS),
2231        ] {
2232            let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2233            let file = object::File::parse(&bytes[..]).expect("a readable object");
2234            let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2235            let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2236                panic!("{name} is not an elf section");
2237            };
2238            assert_eq!(sh_type, wanted, "{name}");
2239            assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2240        }
2241    }
2242
2243    /// A section the program named carries the flags of what is in it, which are the flags gcc
2244    /// writes: read only for a constant with no address in it, no bytes in the file for zeros in
2245    /// a section whose name means zeros, and writable bytes for the rest.
2246    #[test]
2247    fn a_named_section_carries_the_flags_of_what_is_in_it() {
2248        for (name, holds, kind, flags) in [
2249            (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2250            (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2251            (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2252        ] {
2253            let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2254            let file = object::File::parse(&bytes[..]).expect("a readable object");
2255            let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2256            let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2257                panic!("{name} is not an elf section");
2258            };
2259            assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2260            assert_eq!(section.size(), 4, "{name}");
2261        }
2262    }
2263
2264    /// Two variables the program put one section name on, which belong in one section.
2265    ///
2266    /// A file with ten constructors in it would otherwise carry ten section headers describing eight
2267    /// bytes each, and the order the entries run in would be the order the linker happened to put
2268    /// the headers in rather than the order they were written.
2269    #[test]
2270    fn two_variables_in_one_named_section_share_it() {
2271        let objects = vec![
2272            variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2273            variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2274        ];
2275        let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2276        let bytes =
2277            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2278                .expect("an object");
2279        let file = object::File::parse(&bytes[..]).expect("a readable object");
2280        let named: Vec<_> =
2281            file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2282        assert_eq!(named.len(), 1);
2283        assert_eq!(named[0].size(), 8);
2284    }
2285
2286    /// What `-fdata-sections` comes down to in an object file: the section a variable would have
2287    /// shared, with its own name after it. The names are gcc 16's, checked against it on a Linux
2288    /// host, and the part in front of the dot is what a linker script and `--gc-sections` match on.
2289    #[test]
2290    fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2291        let sections =
2292            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2293        for (place, wanted) in [
2294            (Place::Written, ".data.x"),
2295            (Place::ReadOnly, ".rodata.x"),
2296            (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2297            (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2298            (Place::Zero, ".bss.x"),
2299            (Place::Thread { zero: false }, ".tdata.x"),
2300            (Place::Thread { zero: true }, ".tbss.x"),
2301        ] {
2302            let data = Data {
2303                apart: Vec::new(),
2304                exports: Vec::new(),
2305                weak: Vec::new(),
2306                objects: vec![variable("x", place.clone())],
2307            };
2308            let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2309                .expect("object");
2310            let file = object::File::parse(&bytes[..]).expect("a readable object");
2311            assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2312            let section = file.section_by_name(wanted).expect("the section it named");
2313            assert_eq!(section.size(), 4, "{place:?}");
2314            // Which page it lands in is what the section it came out of decided, and splitting
2315            // must not quietly change it: the zero filled one still carries none of its bytes.
2316            let carried = section.data().expect("the bytes").len();
2317            assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2318        }
2319    }
2320
2321    /// The two kinds of variable the flag leaves alone. A tentative definition is a request to the
2322    /// linker for that much zeroed space rather than an image, so there is no section to split off,
2323    /// and one the program named has the answer the source gave, which a flag must not overrule.
2324    #[test]
2325    fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2326        let sections =
2327            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2328        let named = Place::Named(".init_array".to_owned(), Holds::Written);
2329        let objects = vec![variable("m", Place::Merged), variable("n", named)];
2330        let bytes = write(
2331            &Text::default(),
2332            &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2333            &[],
2334            &target(),
2335            sections,
2336            &Info::default(),
2337        )
2338        .expect("object");
2339        let file = object::File::parse(&bytes[..]).expect("a readable object");
2340        let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2341        assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2342        assert_eq!(lives_in(&file, "n"), ".init_array");
2343        assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2344    }
2345
2346    /// A relocation in a variable's image counts from the start of the section it ended up in, the
2347    /// same question the split text has to answer and a shorter answer: a variable alone in a
2348    /// section starts where the section does.
2349    #[test]
2350    fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2351        let sections =
2352            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2353        let pointer = Object {
2354            bytes: vec![0; 8],
2355            size: 8,
2356            align: 8,
2357            relocs: vec![Reloc {
2358                at: 0,
2359                symbol: "y".to_owned(),
2360                kind: Reference::Address { bytes: 8 },
2361                addend: 0,
2362                after: 0,
2363            }],
2364            ..variable("p", Place::Written)
2365        };
2366        let objects = vec![variable("first", Place::Written), pointer];
2367        let bytes = write(
2368            &Text::default(),
2369            &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2370            &[],
2371            &target(),
2372            sections,
2373            &Info::default(),
2374        )
2375        .expect("object");
2376        let file = object::File::parse(&bytes[..]).expect("a readable object");
2377        let section = file.section_by_name(".data.p").expect("the pointer's own section");
2378        let (offset, reloc) = section.relocations().next().expect("one relocation");
2379        // Nothing rather than the eight it would be if the variable in front of it were still
2380        // counted, which is what a section of its own means.
2381        assert_eq!(offset, 0);
2382        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2383    }
2384
2385    /// Two variables that want `.data.rel.ro.local` end up in one section, not two of one name.
2386    ///
2387    /// The writer has no name of its own for that section, so it is added by hand, and asking for
2388    /// it again makes a second section rather than handing back the first. SQLite has enough const
2389    /// tables of function pointers in it to turn that into eighty odd sections in one object, each
2390    /// with its own relocation section beside it, which is a pile of section headers describing
2391    /// eight bytes apiece.
2392    #[test]
2393    fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2394        let place = Place::RelocReadOnly { local: true };
2395        let data = Data {
2396            apart: Vec::new(),
2397            exports: Vec::new(),
2398            weak: Vec::new(),
2399            objects: vec![variable("first", place.clone()), variable("second", place)],
2400        };
2401        let bytes =
2402            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2403                .expect("an object");
2404        let file = object::File::parse(&bytes[..]).expect("a readable object");
2405        let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2406        assert_eq!(named, 1, "one section holding both, not one each");
2407    }
2408
2409    #[test]
2410    fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2411        let mut data = Data {
2412            apart: Vec::new(),
2413            exports: Vec::new(),
2414            weak: Vec::new(),
2415            objects: vec![variable("first", Place::Written)],
2416        };
2417        data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2418        let bytes =
2419            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2420                .expect("an object");
2421        let file = object::File::parse(&bytes[..]).expect("a readable object");
2422        let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2423        assert_eq!(second.kind(), SymbolKind::Data);
2424        assert_eq!(second.size(), 4);
2425        // Sixteen rather than four, because the second one asked for sixteen and the first one
2426        // had already used four. Getting this wrong is a variable at an address it said it would
2427        // never be at, which nothing downstream would notice until an aligned load faulted.
2428        assert_eq!(second.address(), 16);
2429    }
2430
2431    #[test]
2432    fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2433        for (binding, global, weak) in [
2434            (Binding::Global, true, false),
2435            (Binding::Local, false, false),
2436            (Binding::Weak, true, true),
2437        ] {
2438            let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2439            let file = object::File::parse(&bytes[..]).expect("a readable object");
2440            let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2441            assert_eq!(x.is_global(), global, "{binding:?}");
2442            assert_eq!(x.is_weak(), weak, "{binding:?}");
2443        }
2444    }
2445
2446    #[test]
2447    fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2448        let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2449        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2450        let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2451        assert!(x.is_common(), "the linker merges every definition of this name into one");
2452        assert_eq!(x.size(), 4);
2453        // What a common symbol records where an ordinary one records its address is what it wants
2454        // to be aligned to, because it has no address yet. The reader deliberately answers nothing
2455        // when asked for the address of one, so this is the field itself.
2456        assert_eq!(x.address(), 0);
2457        assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2458    }
2459
2460    #[test]
2461    fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2462        let object = Object {
2463            bytes: vec![0; 8],
2464            size: 8,
2465            align: 8,
2466            relocs: vec![Reloc {
2467                at: 0,
2468                symbol: "y".to_owned(),
2469                kind: Reference::Address { bytes: 8 },
2470                addend: 16,
2471                after: 0,
2472            }],
2473            ..variable("p", Place::Written)
2474        };
2475        let bytes = holding(object);
2476        let file = object::File::parse(&bytes[..]).expect("a readable object");
2477        let section = file.section_by_name(".data").expect("a data section");
2478        let (offset, reloc) = section.relocations().next().expect("one relocation");
2479        assert_eq!(offset, 0);
2480        assert_eq!(reloc.addend(), 16);
2481        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2482        let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2483        assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2484    }
2485
2486    /// A name a declaration wrote `weak` on is undefined and may stay that way.
2487    ///
2488    /// The difference between this and the case above is one bit and the whole of what a link does
2489    /// about it: an ordinary undefined symbol is a name the linker has to find, and a weak one is a
2490    /// name it may fail to find, in which case every reference reads a zero address. That is what
2491    /// lets a library offer a hook a profiler may fill in, which is tamnd/rucc#1414.
2492    #[test]
2493    fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2494        let mut text = Text::default();
2495        text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2496        text.bytes.resize(8, 0x90);
2497        text.relocs.push(Reloc {
2498            at: 1,
2499            symbol: "hook".to_owned(),
2500            kind: Reference::Call,
2501            addend: -4,
2502            after: 0,
2503        });
2504        let data = Data {
2505            apart: Vec::new(),
2506            exports: Vec::new(),
2507            weak: vec!["hook".to_owned(), "never_called".to_owned()],
2508            objects: vec![],
2509        };
2510        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2511            .expect("an object");
2512        let file = object::File::parse(&bytes[..]).expect("a readable object");
2513
2514        let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2515        assert!(hook.is_undefined(), "nothing here defines it");
2516        assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2517
2518        // And one nothing refers to is still written down, because the listing writes a directive
2519        // for it and the two paths have to put the same entries in. A linker has nothing to do
2520        // about an undefined weak symbol no relocation names.
2521        let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2522        assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2523    }
2524
2525    /// A name this file reads through the thread pointer is undefined and is still known to be
2526    /// thread-local.
2527    ///
2528    /// The other undefined names here are written with no type at all, because a name this file does
2529    /// not define is a name this file has nothing to say about. A thread-local one is different in
2530    /// the one way that counts: a reference to it is satisfied by an offset into a block rather than
2531    /// by an address, so the linker has to know which of the two is wanted before it has found the
2532    /// definition, and rather than guess it refuses a link where one file says `STT_TLS` about a name
2533    /// and another does not. Writing the type is not extra information, it is the same information
2534    /// the relocation already carried, said where the linker looks for it.
2535    ///
2536    /// That is tamnd/rucc#1461. libmpfr defines `__gmpfr_flags` in `exceptions.c` and reads it in a
2537    /// hundred other files, and the link stopped at the first reader with `TLS definition in
2538    /// exceptions.o section .tdata mismatches non-TLS reference in add.o`.
2539    #[test]
2540    fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2541        let mut text = Text::default();
2542        text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2543        text.bytes.resize(16, 0x90);
2544        text.relocs.push(Reloc {
2545            at: 3,
2546            symbol: "flags".to_owned(),
2547            kind: Reference::Thread,
2548            addend: -4,
2549            after: 0,
2550        });
2551        // One of them reached the ordinary way, so that what the type says is the relocation's doing
2552        // and not something every undefined name here would have got.
2553        text.relocs.push(Reloc {
2554            at: 10,
2555            symbol: "shared".to_owned(),
2556            kind: Reference::Got,
2557            addend: -4,
2558            after: 0,
2559        });
2560        let data =
2561            Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2562        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2563            .expect("an object");
2564        let file = object::File::parse(&bytes[..]).expect("a readable object");
2565
2566        let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2567        assert!(flags.is_undefined(), "nothing here defines it");
2568        assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2569
2570        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2571        assert!(shared.is_undefined(), "nothing here defines this one either");
2572        assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2573    }
2574
2575    /// Not a rewording of the case above: what is checked is the arithmetic between the two.
2576    #[test]
2577    fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2578        let mut data = Data {
2579            apart: Vec::new(),
2580            exports: Vec::new(),
2581            weak: Vec::new(),
2582            objects: vec![variable("first", Place::Written)],
2583        };
2584        data.objects.push(Object {
2585            bytes: vec![0; 16],
2586            size: 16,
2587            align: 8,
2588            relocs: vec![Reloc {
2589                at: 8,
2590                symbol: "y".to_owned(),
2591                kind: Reference::Address { bytes: 8 },
2592                addend: 0,
2593                after: 0,
2594            }],
2595            ..variable("second", Place::Written)
2596        });
2597        let bytes =
2598            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2599                .expect("an object");
2600        let file = object::File::parse(&bytes[..]).expect("a readable object");
2601        let section = file.section_by_name(".data").expect("a data section");
2602        let (offset, _) = section.relocations().next().expect("one relocation");
2603        // Eight into the second image, which starts eight in because the first one is four long
2604        // and the second is eight aligned.
2605        assert_eq!(offset, 16);
2606    }
2607
2608    #[test]
2609    fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2610        let data = Data {
2611            apart: Vec::new(),
2612            exports: Vec::new(),
2613            weak: Vec::new(),
2614            objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2615        };
2616        let aliases = [Alias {
2617            name: "b".to_owned(),
2618            target: "a".to_owned(),
2619            binding: Binding::Global,
2620            visibility: Visibility::Default,
2621            ifunc: false,
2622        }];
2623        let bytes = write(
2624            &Text::default(),
2625            &data,
2626            &aliases,
2627            &target(),
2628            Output::default(),
2629            &Info::default(),
2630        )
2631        .expect("an object");
2632        let file = object::File::parse(&bytes[..]).expect("a readable object");
2633        let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2634        let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2635        assert_eq!(b.address(), a.address(), "the same place");
2636        assert_eq!(b.size(), a.size());
2637        assert_eq!(b.section_index(), a.section_index());
2638        // The binding is the one thing the second name does not take from the first, which is
2639        // what `extern int b __attribute__((alias("a")))` on a `static a` asks for.
2640        assert!(a.is_local(), "the target was written `static`");
2641        assert!(b.is_global(), "and the name given to it was not");
2642        // Four bytes of image and not eight, since an alias is a name and not a copy.
2643        assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2644    }
2645
2646    #[test]
2647    fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2648        let text = calling("puts");
2649        let aliases = [Alias {
2650            name: "g".to_owned(),
2651            target: "f".to_owned(),
2652            binding: Binding::Weak,
2653            visibility: Visibility::Default,
2654            ifunc: false,
2655        }];
2656        let bytes = write(
2657            &text,
2658            &Data::default(),
2659            &aliases,
2660            &target(),
2661            Output::default(),
2662            &Info::default(),
2663        )
2664        .expect("an object");
2665        let file = object::File::parse(&bytes[..]).expect("a readable object");
2666        let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2667        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2668        assert_eq!(g.address(), f.address());
2669        assert_eq!(g.size(), f.size());
2670        assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2671        assert!(g.is_weak(), "so that a program may define the name itself instead");
2672    }
2673
2674    /// An ifunc is the alias whose type is its own: `STT_GNU_IFUNC`, with the binding the alias
2675    /// was given, at the resolver's address. A `static` one is a local symbol of the same type,
2676    /// which is what gas writes for gcc's listing of a `static` function with `target_clones`.
2677    #[test]
2678    fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2679        let text = calling("puts");
2680        for (binding, bind) in [
2681            (Binding::Global, elf::STB_GLOBAL),
2682            (Binding::Weak, elf::STB_WEAK),
2683            (Binding::Local, elf::STB_LOCAL),
2684        ] {
2685            let aliases = [Alias {
2686                name: "g".to_owned(),
2687                target: "f".to_owned(),
2688                binding,
2689                visibility: Visibility::Default,
2690                ifunc: true,
2691            }];
2692            let bytes = write(
2693                &text,
2694                &Data::default(),
2695                &aliases,
2696                &target(),
2697                Output::default(),
2698                &Info::default(),
2699            )
2700            .expect("an object");
2701            let file = object::File::parse(&bytes[..]).expect("a readable object");
2702            let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2703            let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2704            assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2705            let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2706                panic!("an ELF symbol");
2707            };
2708            assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2709            let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2710            let os_abi = elf.elf_header().e_ident.os_abi;
2711            assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2712        }
2713    }
2714
2715    /// The other formats have no symbol type for one, and an ordinary name would be a call to the
2716    /// resolver, so the writer says so.
2717    #[test]
2718    fn an_ifunc_is_refused_on_a_format_without_the_type() {
2719        let aliases = [Alias {
2720            name: "g".to_owned(),
2721            target: "f".to_owned(),
2722            binding: Binding::Global,
2723            visibility: Visibility::Default,
2724            ifunc: true,
2725        }];
2726        let error = write(
2727            &calling("puts"),
2728            &Data::default(),
2729            &aliases,
2730            &windows(),
2731            Output::default(),
2732            &Info::default(),
2733        )
2734        .expect_err("no ifunc on COFF");
2735        assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2736    }
2737
2738    /// The front end is what reports this as a program's mistake, so one arriving here is a bug
2739    /// in this compiler and is said so rather than written as an undefined symbol.
2740    #[test]
2741    fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2742        let aliases = [Alias {
2743            name: "b".to_owned(),
2744            target: "a".to_owned(),
2745            binding: Binding::Global,
2746            visibility: Visibility::Default,
2747            ifunc: false,
2748        }];
2749        let error = write(
2750            &Text::default(),
2751            &Data::default(),
2752            &aliases,
2753            &target(),
2754            Output::default(),
2755            &Info::default(),
2756        )
2757        .expect_err("nothing to point at");
2758        assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2759    }
2760
2761    #[test]
2762    fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2763        let text = calling("puts");
2764        for triple in [
2765            Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2766            Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2767        ] {
2768            let error = write(
2769                &text,
2770                &Data::default(),
2771                &[],
2772                &TargetInfo::new(triple),
2773                Output::default(),
2774                &Info::default(),
2775            )
2776            .expect_err("no writer");
2777            assert!(matches!(error, Error::Format { .. }), "{error:?}");
2778        }
2779    }
2780
2781    /// What the archive's symbol index is built from is what the linker can find in the member.
2782    ///
2783    /// Written against the object rather than against the list, because the two agreeing is the
2784    /// whole point: a list that says more than the file does is an archive that promises a
2785    /// definition it does not have, and a list that says less is a member nothing pulls out.
2786    #[test]
2787    fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2788        let mut text = calling("puts");
2789        text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2790        text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2791        text.bytes.resize(33, 0x90);
2792        let data = Data {
2793            apart: Vec::new(),
2794            exports: Vec::new(),
2795            weak: Vec::new(),
2796            objects: vec![variable("seen", Place::Written), {
2797                let mut quiet = variable("quiet", Place::Zero);
2798                quiet.binding = Binding::Local;
2799                quiet
2800            }],
2801        };
2802        let aliases = [Alias {
2803            name: "second".to_owned(),
2804            target: "f".to_owned(),
2805            binding: Binding::Global,
2806            visibility: Visibility::Default,
2807            ifunc: false,
2808        }];
2809
2810        let names = defines(&text, &data, &aliases, &target()).expect("a list");
2811        assert_eq!(names, ["f", "shared", "seen", "second"]);
2812
2813        let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2814            .expect("an object");
2815        let file = object::File::parse(&bytes[..]).expect("a readable object");
2816        let found: Vec<String> = file
2817            .symbols()
2818            .filter(|symbol| symbol.is_global() && symbol.is_definition())
2819            .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2820            .collect();
2821        let mut sorted = names.clone();
2822        sorted.sort();
2823        let mut theirs = found;
2824        theirs.sort();
2825        assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2826    }
2827
2828    /// A windows x86-64 target, which is the other format this writes.
2829    fn windows() -> TargetInfo {
2830        TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2831    }
2832
2833    /// What the four bytes a relocation covers hold, which is where COFF keeps its addend.
2834    fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2835        let file = object::File::parse(bytes).expect("a readable object");
2836        let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2837        i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2838    }
2839
2840    #[test]
2841    fn a_windows_target_is_written_rather_than_refused() {
2842        let text = calling("puts");
2843        let bytes =
2844            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2845                .expect("an object");
2846        let file = object::File::parse(&bytes[..]).expect("a readable object");
2847        assert_eq!(file.format(), BinaryFormat::Coff);
2848        let section = file.section_by_name(".text").expect("a text section");
2849        assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2850        let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2851        assert!(names.contains(&"f"), "{names:?}");
2852        assert!(names.contains(&"puts"), "{names:?}");
2853    }
2854
2855    /// The whole reason a relocation carries where the instruction ended as well as the addend.
2856    ///
2857    /// A call ends at the four bytes the linker writes over, and a store of a constant through an
2858    /// address counted from the instruction pointer has the constant after them, and ELF tells the
2859    /// two apart by the addend alone. COFF cannot: it says how far the end is in the relocation type
2860    /// and works the addend out from that, so the same four bytes come out of two different types
2861    /// and both have to end up meaning the same distance.
2862    #[test]
2863    fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2864        for (after, typ) in [
2865            (0, pe::IMAGE_REL_AMD64_REL32),
2866            (1, pe::IMAGE_REL_AMD64_REL32_1),
2867            (4, pe::IMAGE_REL_AMD64_REL32_4),
2868            (5, pe::IMAGE_REL_AMD64_REL32_5),
2869        ] {
2870            let mut text = calling("puts");
2871            // The same distance every time, said the way ELF says it: from where the four bytes
2872            // start, with everything else folded in.
2873            text.relocs[0].addend = -4 - i64::from(after);
2874            text.relocs[0].after = after;
2875            text.bytes.resize(6 + after as usize, 0x90);
2876            text.funcs[0].len = text.bytes.len();
2877            let bytes = write(
2878                &text,
2879                &Data::default(),
2880                &[],
2881                &windows(),
2882                Output::default(),
2883                &Info::default(),
2884            )
2885            .expect("an object");
2886            let file = object::File::parse(&bytes[..]).expect("a readable object");
2887            let section = file.section_by_name(".text").expect("a text section");
2888            let (_, reloc) = section.relocations().next().expect("the relocation");
2889            assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2890            // And the bytes come out holding nothing, because the distance the instruction wants
2891            // and the distance the type already says are the same one.
2892            assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2893        }
2894    }
2895
2896    /// The addend a COFF object keeps is in the bytes rather than in the relocation, so the number
2897    /// the caller handed over has to survive the trip through the type.
2898    #[test]
2899    fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2900        let mut text = calling("puts");
2901        text.relocs[0].addend = 12;
2902        let bytes =
2903            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2904                .expect("an object");
2905        assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2906    }
2907
2908    #[test]
2909    fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2910        let object = Object {
2911            bytes: vec![0; 8],
2912            size: 8,
2913            align: 8,
2914            relocs: vec![Reloc {
2915                at: 0,
2916                symbol: "y".to_owned(),
2917                kind: Reference::Address { bytes: 8 },
2918                addend: 0,
2919                after: 0,
2920            }],
2921            ..variable("p", Place::Written)
2922        };
2923        let data = Data {
2924            apart: Vec::new(),
2925            exports: Vec::new(),
2926            weak: Vec::new(),
2927            objects: vec![object],
2928        };
2929        let bytes =
2930            write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2931                .expect("an object");
2932        let file = object::File::parse(&bytes[..]).expect("a readable object");
2933        let section = file.section_by_name(".data").expect("a data section");
2934        let (_, reloc) = section.relocations().next().expect("the relocation");
2935        let typ = pe::IMAGE_REL_AMD64_ADDR64;
2936        assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2937    }
2938
2939    /// A pointer to a variable the file only declares is in a section of its own that the linker
2940    /// keeps one copy of, keyed on the pointer's name, and read only, which is what gcc and clang
2941    /// both write for `.refptr.` and the name.
2942    #[test]
2943    fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
2944        let pointer = Object {
2945            bytes: vec![0; 8],
2946            size: 8,
2947            align: 8,
2948            relocs: vec![Reloc {
2949                at: 0,
2950                symbol: "environ".to_owned(),
2951                kind: Reference::Address { bytes: 8 },
2952                addend: 0,
2953                after: 0,
2954            }],
2955            ..variable(".refptr.environ", Place::Pointer)
2956        };
2957        let data = Data { objects: vec![pointer], ..Data::default() };
2958        let bytes =
2959            write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2960                .expect("an object");
2961        let file = object::File::parse(&bytes[..]).expect("a readable object");
2962        let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
2963        let SectionFlags::Coff { characteristics } = section.flags() else {
2964            panic!("a COFF section has COFF flags");
2965        };
2966        let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
2967        // The alignment, which is its own field in the same word.
2968        let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
2969        assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
2970        assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
2971        let comdat = file.comdats().next().expect("a group the linker picks one copy of");
2972        assert_eq!(comdat.kind(), ComdatKind::Any);
2973        assert_eq!(comdat.name(), Ok(".refptr.environ"));
2974        let (_, reloc) = section.relocations().next().expect("the address it holds");
2975        assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
2976    }
2977
2978    /// What `dllexport` and a hidden definition ask for is an option to the linker, one per name,
2979    /// in the order clang writes them and in the section COFF keeps options in, which the linker
2980    /// drops afterwards.
2981    #[test]
2982    fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
2983        let exports = vec![
2984            Export { name: "offered".to_owned(), kind: Offer::Function },
2985            Export { name: "count".to_owned(), kind: Offer::Variable },
2986            Export { name: "kept".to_owned(), kind: Offer::Hidden },
2987        ];
2988        let data = Data { exports, ..Data::default() };
2989        let bytes =
2990            write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2991                .expect("an object");
2992        let file = object::File::parse(&bytes[..]).expect("a readable object");
2993        let section = file.section_by_name(".drectve").expect("the options section");
2994        assert_eq!(
2995            section.data().expect("the options"),
2996            b" -export:offered -export:count,data -exclude-symbols:kept"
2997        );
2998        let SectionFlags::Coff { characteristics } = section.flags() else {
2999            panic!("a COFF section has COFF flags");
3000        };
3001        let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
3002        assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
3003
3004        let none = write(
3005            &Text::default(),
3006            &Data::default(),
3007            &[],
3008            &windows(),
3009            Output::default(),
3010            &Info::default(),
3011        )
3012        .expect("an object");
3013        let file = object::File::parse(&none[..]).expect("a readable object");
3014        assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3015    }
3016
3017    /// `.data.rel.ro` is an ELF answer to a problem this format solves elsewhere, so both halves of
3018    /// it land in ordinary read only data, which is where the platform's own linker puts them.
3019    #[test]
3020    fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3021        for local in [false, true] {
3022            let data = Data {
3023                apart: Vec::new(),
3024                exports: Vec::new(),
3025                weak: Vec::new(),
3026                objects: vec![variable("p", Place::RelocReadOnly { local })],
3027            };
3028            let bytes = write(
3029                &Text::default(),
3030                &data,
3031                &[],
3032                &windows(),
3033                Output::default(),
3034                &Info::default(),
3035            )
3036            .expect("an object");
3037            let file = object::File::parse(&bytes[..]).expect("a readable object");
3038            assert!(file.section_by_name(".rdata").is_some(), "{local}");
3039            assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3040        }
3041    }
3042
3043    /// No marker and no note, because a PE image says both of those things in the header of the
3044    /// finished image rather than in each of its inputs.
3045    #[test]
3046    fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3047        let text = calling("puts");
3048        let output = Output { property: Property { features: 3 }, ..Output::default() };
3049        let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3050            .expect("an object");
3051        let file = object::File::parse(&bytes[..]).expect("a readable object");
3052        assert!(file.section_by_name(".note.GNU-stack").is_none());
3053        assert!(file.section_by_name(".note.gnu.property").is_none());
3054    }
3055
3056    /// Each of these is something this format has no way to write, and writing the nearest thing
3057    /// would be worse than refusing: a zeroed thread-local variable written as ordinary zeroed
3058    /// space is one copy where the program asked for one per thread, and a constructor list under
3059    /// a name nothing gathers is a program whose constructors never run.
3060    #[test]
3061    fn what_this_format_cannot_say_is_refused_by_name() {
3062        let ordinary = Text::default();
3063        let empty = Data::default();
3064
3065        let mut thread = Data::default();
3066        thread.objects.push(variable("t", Place::Thread { zero: true }));
3067
3068        let mut gathered = Data::default();
3069        gathered
3070            .objects
3071            .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3072
3073        let mut table = calling("puts");
3074        table.relocs[0].kind = Reference::Got;
3075
3076        let mut room = calling("puts");
3077        room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3078
3079        let cases: [(&str, &Text, &Data); 4] = [
3080            ("thread-local", &ordinary, &thread),
3081            ("startup", &ordinary, &gathered),
3082            ("table", &table, &empty),
3083            ("patcher", &room, &empty),
3084        ];
3085        for (what, text, data) in cases {
3086            let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3087                .expect_err("something this format cannot write");
3088            assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3089        }
3090    }
3091
3092    /// A thread-local variable with an image goes in `.tls$`, which is the section every thread
3093    /// gets a copy of.
3094    #[test]
3095    fn a_thread_local_variable_goes_in_the_tls_section() {
3096        let mut thread = Data::default();
3097        thread.objects.push(variable("t", Place::Thread { zero: false }));
3098        let bytes =
3099            write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3100                .expect("an object");
3101        let file = object::File::parse(&bytes[..]).expect("a readable object");
3102        assert!(file.section_by_name(".tls$").is_some());
3103    }
3104
3105    /// A visibility is not refused, because there is nothing to refuse: it is a fact about a dynamic
3106    /// symbol table and a COFF symbol has nowhere to keep one, which is what gcc does on the
3107    /// platform as well.
3108    #[test]
3109    fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3110        let mut text = calling("puts");
3111        text.funcs[0].visibility = Visibility::Hidden;
3112        let bytes =
3113            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3114                .expect("an object");
3115        let file = object::File::parse(&bytes[..]).expect("a readable object");
3116        let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3117        assert!(symbol.is_global(), "a name others may use either way");
3118    }
3119
3120    #[test]
3121    fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3122        let text = calling("puts");
3123        let data = Data {
3124            apart: Vec::new(),
3125            exports: Vec::new(),
3126            weak: Vec::new(),
3127            objects: vec![variable("shared", Place::Written)],
3128        };
3129        let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3130        assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3131    }
3132
3133    /// The same refusal the writer gives, for the reason the function says: an undecorated name is
3134    /// the wrong answer for a format whose symbols carry an underscore, and a wrong index entry is
3135    /// worse than no archive.
3136    #[test]
3137    fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3138        let text = calling("puts");
3139        for triple in [
3140            Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3141            Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3142        ] {
3143            let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3144                .expect_err("no writer");
3145            assert!(matches!(error, Error::Format { .. }), "{error:?}");
3146        }
3147    }
3148
3149    /// A linux i386 target, which [`write()`] writes as a 32 bit ELF file with REL relocations.
3150    fn i386() -> TargetInfo {
3151        TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3152    }
3153
3154    /// A compilation for i386 comes out as a 32 bit file whose addends are in the bytes, and the
3155    /// records of addresses in it are four bytes each.
3156    ///
3157    /// The call is the shape every case here starts from, the variable holds the address of
3158    /// something else, and the function has room in front of it for a patcher, which is a record
3159    /// of one address whose section header has to be read back from where a 32 bit file keeps it.
3160    #[test]
3161    fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3162        let mut text = calling("puts");
3163        text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3164        text.funcs[0].start = 3;
3165        text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3166        text.relocs[0].at = 4;
3167        let data = Data {
3168            objects: vec![Object {
3169                name: "p".to_owned(),
3170                bytes: vec![0; 4],
3171                size: 4,
3172                align: 4,
3173                place: Place::Written,
3174                binding: Binding::Global,
3175                visibility: Visibility::Default,
3176                relocs: vec![Reloc {
3177                    at: 0,
3178                    symbol: "x".to_owned(),
3179                    kind: Reference::Address { bytes: 4 },
3180                    addend: 12,
3181                    after: 0,
3182                }],
3183            }],
3184            ..Data::default()
3185        };
3186        let property = Property { features: Property::IBT | Property::SHSTK };
3187        let output = Output { property, ..Output::default() };
3188        let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3189        let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3190        assert_eq!(file.architecture(), Architecture::I386);
3191        assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3192        assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3193
3194        // The call, with its minus four in the four bytes of the call.
3195        let code = file.section_by_name(".text").expect("a text section");
3196        let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3197            panic!("one relocation in the text")
3198        };
3199        assert_eq!(*at, 4);
3200        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3201        assert!(reloc.has_implicit_addend());
3202        assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3203
3204        // The address in the variable, with what is added to it where the address goes.
3205        let variable = file.section_by_name(".data").expect("a data section");
3206        let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3207            panic!("one relocation in the data")
3208        };
3209        assert_eq!(*at, 0);
3210        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3211        assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3212
3213        // The patcher's record, one four byte address tied to the text it is about.
3214        let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3215        assert_eq!(record.size(), 4);
3216        assert_eq!(record.align(), 4);
3217        let index = code.index().0;
3218        assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3219        let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3220            panic!("one address in the record")
3221        };
3222        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3223
3224        // The note, padded to four rather than to eight, which is what gcc -m32 writes.
3225        let note = file.section_by_name(".note.gnu.property").expect("the note");
3226        assert_eq!(note.align(), 4);
3227        let want: Vec<u8> = [
3228            4u32,
3229            12,
3230            5,
3231            u32::from_le_bytes(*b"GNU\0"),
3232            Property::X86_FEATURES,
3233            4,
3234            Property::IBT | Property::SHSTK,
3235        ]
3236        .iter()
3237        .flat_map(|word| word.to_le_bytes())
3238        .collect();
3239        assert_eq!(note.data().expect("the bytes"), &want[..]);
3240    }
3241
3242    /// A name less 0xC0000000 in an i386 address wraps to the name plus 0x40000000, which is what
3243    /// the kernel's `__pa` of a static comes to. A name less that much in two bytes does not fit.
3244    #[test]
3245    fn an_i386_address_less_three_gigabytes_wraps_in_its_four_bytes() {
3246        let object = |bytes: u8| Object {
3247            name: "cr3".to_owned(),
3248            bytes: vec![0; usize::from(bytes)],
3249            size: u64::from(bytes),
3250            align: u64::from(bytes),
3251            place: Place::Written,
3252            binding: Binding::Global,
3253            visibility: Visibility::Default,
3254            relocs: vec![Reloc {
3255                at: 0,
3256                symbol: "swapper_pg_dir".to_owned(),
3257                kind: Reference::Address { bytes },
3258                addend: -0xC000_0000,
3259                after: 0,
3260            }],
3261        };
3262        let data = Data { objects: vec![object(4)], ..Data::default() };
3263        let bytes =
3264            write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default())
3265                .expect("an object");
3266        let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3267        let variable = file.section_by_name(".data").expect("a data section");
3268        assert_eq!(variable.data().expect("the bytes"), &0x4000_0000u32.to_le_bytes());
3269
3270        let data = Data { objects: vec![object(2)], ..Data::default() };
3271        let refused =
3272            write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default());
3273        assert!(refused.is_err(), "two bytes cannot hold a name less three gigabytes");
3274    }
3275
3276    /// The debug sections of an i386 file are not compressed even when `-gz` asks, since the addend
3277    /// of each relocation in them goes in the bytes and a compressed section does not hold those.
3278    #[test]
3279    fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3280        let info = Info {
3281            chunks: vec![Chunk {
3282                name: ".debug_info".to_owned(),
3283                bytes: vec![0; 64],
3284                relocs: vec![Reloc {
3285                    at: 8,
3286                    symbol: "f".to_owned(),
3287                    kind: Reference::Address { bytes: 4 },
3288                    addend: 7,
3289                    after: 0,
3290                }],
3291            }],
3292            compress: Compress::Zlib,
3293        };
3294        let bytes =
3295            write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3296                .expect("an object");
3297        let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3298        let section = file.section_by_name(".debug_info").expect("the debug section");
3299        let packed =
3300            SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3301        assert_ne!(section.flags(), packed);
3302        let data = section.data().expect("the bytes");
3303        assert_eq!(data.len(), 64);
3304        assert_eq!(&data[8..12], &7u32.to_le_bytes());
3305    }
3306
3307    /// A mingw i386 target, which [`write()`] writes as COFF with the i386 relocations.
3308    fn i386_windows() -> TargetInfo {
3309        TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3310    }
3311
3312    /// A compilation for i386 on Windows is a COFF file for that machine, with an underscore in
3313    /// front of every C name, and with the addend of each relocation in the bytes it covers.
3314    ///
3315    /// The call is `REL32` with nothing in its field, because the linker counts from the end of
3316    /// the four bytes and the minus four the call carried is that same distance. The pointer is
3317    /// `DIR32` with its addend in the variable. A `__fastcall` name already carries its own `@`
3318    /// and gets nothing more, and a pointer the import library fills in has the underscore after
3319    /// its `__imp_`.
3320    #[test]
3321    fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3322        let mut text = calling("puts");
3323        text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3324        text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3325        text.relocs.push(Reloc {
3326            at: 7,
3327            symbol: "__imp_GetTickCount".to_owned(),
3328            kind: Reference::Call,
3329            addend: -4,
3330            after: 0,
3331        });
3332        let data = Data {
3333            objects: vec![Object {
3334                name: "p".to_owned(),
3335                bytes: vec![0; 12],
3336                size: 12,
3337                align: 4,
3338                place: Place::Written,
3339                binding: Binding::Global,
3340                visibility: Visibility::Default,
3341                relocs: vec![
3342                    Reloc {
3343                        at: 0,
3344                        symbol: "x".to_owned(),
3345                        kind: Reference::Address { bytes: 4 },
3346                        addend: 12,
3347                        after: 0,
3348                    },
3349                    Reloc {
3350                        at: 4,
3351                        symbol: "f".to_owned(),
3352                        kind: Reference::Image,
3353                        addend: 0,
3354                        after: 0,
3355                    },
3356                    Reloc {
3357                        at: 8,
3358                        symbol: "x".to_owned(),
3359                        kind: Reference::Away,
3360                        addend: 0,
3361                        after: 0,
3362                    },
3363                ],
3364            }],
3365            ..Data::default()
3366        };
3367        let aliases = [Alias {
3368            name: "g".to_owned(),
3369            target: "f".to_owned(),
3370            binding: Binding::Global,
3371            visibility: Visibility::Default,
3372            ifunc: false,
3373        }];
3374        let target = i386_windows();
3375        let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3376            .expect("an object");
3377        let file = object::File::parse(&bytes[..]).expect("a readable object");
3378        assert_eq!(file.format(), BinaryFormat::Coff);
3379        assert_eq!(file.architecture(), Architecture::I386);
3380        assert!(!file.is_64());
3381
3382        let named = |name: &str| file.symbol_by_name(name).is_some();
3383        for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3384            assert!(named(name), "{name}");
3385        }
3386        for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3387            assert!(!named(name), "{name}");
3388        }
3389
3390        let code = file.section_by_name(".text").expect("a text section");
3391        let relocs: Vec<_> = code.relocations().collect();
3392        assert_eq!(relocs.len(), 2);
3393        for (at, reloc) in &relocs {
3394            assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3395            let at = *at as usize;
3396            assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3397        }
3398
3399        let variable = file.section_by_name(".data").expect("a data section");
3400        let types: Vec<_> = variable
3401            .relocations()
3402            .map(|(at, reloc)| match reloc.flags() {
3403                RelocationFlags::Coff { typ } => (at, typ),
3404                flags => panic!("{flags:?}"),
3405            })
3406            .collect();
3407        assert_eq!(
3408            types,
3409            [
3410                (0, pe::IMAGE_REL_I386_DIR32),
3411                (4, pe::IMAGE_REL_I386_DIR32NB),
3412                (8, pe::IMAGE_REL_I386_REL32)
3413            ]
3414        );
3415        // The addend of the address, and the four a distance written into an image needs back
3416        // because the linker counts it from the end of the four bytes.
3417        let image = variable.data().expect("the bytes");
3418        assert_eq!(&image[0..4], &12u32.to_le_bytes());
3419        assert_eq!(&image[8..12], &4u32.to_le_bytes());
3420
3421        // The archive index is the names the file has.
3422        let listed = defines(&text, &data, &aliases, &target).expect("a list");
3423        assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3424    }
3425
3426    /// Windows on i386 has no unwind table, so the rows a producer wrote for one are left out rather
3427    /// than put in a `.pdata` the loader of a 32 bit image never reads.
3428    #[test]
3429    fn an_i386_windows_object_has_no_unwind_table() {
3430        let mut text = calling("puts");
3431        text.unwind.bytes = vec![0; 12];
3432        let bytes = write(
3433            &text,
3434            &Data::default(),
3435            &[],
3436            &i386_windows(),
3437            Output::default(),
3438            &Info::default(),
3439        )
3440        .expect("an object");
3441        let file = object::File::parse(&bytes[..]).expect("a readable object");
3442        assert!(file.section_by_name(".pdata").is_none());
3443        assert!(file.section_by_name(".xdata").is_none());
3444        assert!(file.section_by_name(".eh_frame").is_none());
3445    }
3446
3447    /// Every i386 Windows object says it is safe for SafeSEH, which is bit 0 of an absolute local
3448    /// `@feat.00`, so that `lld-link /safeseh` takes it. An x86-64 one has no such list to be on.
3449    #[test]
3450    fn an_i386_windows_object_says_it_is_safe_for_safeseh() {
3451        let write_for = |target: &TargetInfo| {
3452            write(
3453                &calling("puts"),
3454                &Data::default(),
3455                &[],
3456                target,
3457                Output::default(),
3458                &Info::default(),
3459            )
3460            .expect("an object")
3461        };
3462        let bytes = write_for(&i386_windows());
3463        let file = object::File::parse(&bytes[..]).expect("a readable object");
3464        let feat = file.symbol_by_name("@feat.00").expect("the feature symbol");
3465        // The reader gives an absolute COFF symbol no address, so the value is read as written.
3466        let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
3467        let raw = coff.symbol_by_name("@feat.00").expect("the feature symbol");
3468        assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
3469        assert_eq!(feat.section(), object::SymbolSection::Absolute);
3470        assert!(feat.is_local());
3471        let bytes = write_for(&windows());
3472        let file = object::File::parse(&bytes[..]).expect("a readable object");
3473        assert!(file.symbol_by_name("@feat.00").is_none());
3474    }
3475
3476    /// No relocation of this machine holds eight bytes or reaches through a table, so a file
3477    /// asking for one is refused rather than written with some other number in the type.
3478    #[test]
3479    fn i386_windows_has_no_eight_byte_or_table_relocations() {
3480        for kind in [
3481            Reference::Address { bytes: 8 },
3482            Reference::AwayWide,
3483            Reference::Got,
3484            Reference::GotOffset,
3485            Reference::Slot,
3486            Reference::Thread,
3487        ] {
3488            assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3489        }
3490        assert_eq!(
3491            Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3492            Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3493        );
3494        assert_eq!(
3495            Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3496            Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3497            "an address an instruction holds"
3498        );
3499    }
3500}