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