Skip to main content

rucc_object/
file.rs

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