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