Skip to main content

rucc_object/
file.rs

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