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, HashMap, HashSet};
34
35use object::write::{
36    Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
37};
38use object::{
39    Architecture, BinaryFormat, Endianness, RelocationFlags, SectionFlags, SectionKind,
40    SymbolFlags, SymbolKind, SymbolScope,
41};
42use rucc_target::{ObjectFormat, TargetInfo};
43use rucc_tuple::Arch;
44
45use crate::section::{
46    Alias, Apart, Array, Binding, Data, EXCEPT_TABLE, Info, Object, Output, Place, Property,
47    Reference, Reloc, Sections, Text, Visibility,
48};
49use crate::{coff, elf};
50
51/// Which of the three formats is being written, and therefore which set of answers the questions
52/// this module cannot decide get.
53///
54/// A short list rather than a trait, because the list is short and closed: everything a format has
55/// an opinion about is a call to one of the methods below, so a format is an arm in each of them
56/// and the compiler names every one that was forgotten.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub(crate) enum Flavour {
59    /// Linux, the BSDs and the freestanding targets.
60    Elf,
61    /// Windows, under either of its two runtimes.
62    Coff,
63    /// Apple's platforms, which are written only from a file of assembly and only for AArch64 so
64    /// far, so [`Flavour::of`] does not give it and [`crate::assembled`] asks for it by name.
65    MachO,
66}
67
68impl Flavour {
69    /// Which one a target wants, and nothing for the two formats that are not written.
70    pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
71        match target.object_format {
72            ObjectFormat::Elf => Some(Flavour::Elf),
73            ObjectFormat::Coff => Some(Flavour::Coff),
74            ObjectFormat::MachO | ObjectFormat::Wasm => None,
75        }
76    }
77
78    /// The format the writer underneath is asked for.
79    pub(crate) fn binary(self) -> BinaryFormat {
80        match self {
81            Flavour::Elf => BinaryFormat::Elf,
82            Flavour::Coff => BinaryFormat::Coff,
83            Flavour::MachO => BinaryFormat::MachO,
84        }
85    }
86
87    /// Which relocation this reference is, or `None` for one this format has none of.
88    ///
89    /// `after` is how many bytes of the instruction come after the four the linker writes over,
90    /// which ELF has already folded into the addend and COFF wants told apart. See [`crate::Reloc`].
91    pub(crate) fn reloc(self, reference: Reference, after: u8) -> Option<RelocationFlags> {
92        match self {
93            Flavour::Elf => elf::r_type(reference).map(|r_type| RelocationFlags::Elf { r_type }),
94            Flavour::Coff => coff::reloc(reference, after),
95            Flavour::MachO => crate::macho::reloc(reference, 0).ok(),
96        }
97    }
98
99    /// Say how far a name reaches beyond what its scope already said.
100    ///
101    /// Nothing on COFF, where a symbol has nowhere to keep it. A file built with
102    /// `-fvisibility=hidden` for Windows is a file where that flag changed nothing, which is what
103    /// gcc does there as well.
104    pub(crate) fn see(
105        self,
106        obj: &mut Writer<'_>,
107        id: SymbolId,
108        binding: Binding,
109        visibility: Visibility,
110    ) {
111        match self {
112            Flavour::Elf => elf::see(obj, id, binding, visibility),
113            Flavour::Coff => {}
114            // Hidden is the one visibility Mach-O has a bit for, which keeps a name out of the
115            // image's exports and lets every object in the link see it. Protected has none.
116            Flavour::MachO => {
117                if binding != Binding::Local && visibility == Visibility::Hidden {
118                    obj.symbol_mut(id).scope = SymbolScope::Linkage;
119                }
120            }
121        }
122    }
123
124    /// The section a variable the loader writes into before anything reads it goes in, when the
125    /// program asked for the half of it the linker keeps apart, or nothing for a format that has no
126    /// such half and puts one in ordinary read only data with the rest.
127    fn rel_ro_local(self) -> Option<&'static str> {
128        match self {
129            Flavour::Elf => elf::REL_RO_LOCAL,
130            Flavour::Coff => coff::REL_RO_LOCAL,
131            Flavour::MachO => None,
132        }
133    }
134
135    /// The type and flags a section of function addresses the startup code calls has, where the
136    /// format has something to say about it.
137    ///
138    /// Nothing on COFF, where such a section is refused by [`beyond`] before it reaches here rather
139    /// than written under a name nothing on that platform gathers.
140    fn gathered(self, array: Array) -> Option<SectionFlags> {
141        match self {
142            Flavour::Elf => Some(elf::gathered(array)),
143            Flavour::Coff | Flavour::MachO => None,
144        }
145    }
146
147    /// The header fields a file of assembly stated about one of its own sections, where the format
148    /// has fields to put them in.
149    ///
150    /// ELF has one for each of the letters, so what the source wrote is written down as it stands
151    /// and the section kind handed to the writer alongside is only a summary of it. COFF has no
152    /// field the letters map onto one for one, and the characteristics the writer works out from
153    /// that kind are the ones every other Windows assembler produces, so there is nothing to add and
154    /// saying so is [`None`] rather than a word built out of guesses.
155    pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
156        match self {
157            Flavour::Elf => {
158                Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
159            }
160            Flavour::Coff => None,
161            Flavour::MachO => Some(SectionFlags::MachO {
162                flags: object::macho::SectionFlags(shape.mach),
163                reserved2: 0,
164            }),
165        }
166    }
167
168    /// What kind of symbol a name out of a file of assembly is, given what `.type` said about it and
169    /// how far it reaches.
170    ///
171    /// The binding is a parameter because on COFF the two are not separable. ELF keeps the type and
172    /// the binding in different halves of a byte, so a name that nothing stated a type for is
173    /// `STT_NOTYPE` whether it is local or global, and that is what gas writes for a plain label.
174    /// COFF has no type field of that sort: what the writer underneath calls a label is storage
175    /// class `LABEL`, which is a name inside this file and nothing a linker will resolve against, so
176    /// a `.globl` with no `.type` under it would quietly stop being offered. The kind with no
177    /// function type on it and an external storage class is the data one, which is what gas for this
178    /// platform writes for the same input, so that is what an untyped global becomes here.
179    ///
180    /// Mach-O keeps no type at all and the writer underneath has no label there, so a function is
181    /// text and everything else is data. A thread-local is data as well, because the kind the
182    /// writer has for one makes a descriptor for it and the listing has already written that.
183    pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
184        if self == Flavour::MachO {
185            return match sort {
186                crate::source::Sort::Func => SymbolKind::Text,
187                crate::source::Sort::File => SymbolKind::File,
188                _ => SymbolKind::Data,
189            };
190        }
191        match sort {
192            crate::source::Sort::Func => SymbolKind::Text,
193            crate::source::Sort::Object => SymbolKind::Data,
194            crate::source::Sort::Thread => SymbolKind::Tls,
195            crate::source::Sort::File => SymbolKind::File,
196            crate::source::Sort::Untyped => match (self, binding) {
197                (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
198                _ => SymbolKind::Label,
199            },
200        }
201    }
202
203    /// The marker a linker looks for in every input, where there is one.
204    pub(crate) fn marker(self, obj: &mut Writer<'_>) {
205        match self {
206            Flavour::Elf => elf::marker(obj),
207            Flavour::Coff => coff::marker(obj),
208            Flavour::MachO => {}
209        }
210    }
211
212    /// What the file says it was built to have checked, where the format has a way to say it.
213    ///
214    /// ELF writes a note the linker keeps only the agreed part of. A PE image says the same thing in
215    /// the header of the finished image rather than in its inputs, so an object carries nothing and
216    /// the instructions the flag asked for are in the text either way.
217    fn property(self, obj: &mut Writer<'_>, property: Property) {
218        if !property.any() {
219            return;
220        }
221        match self {
222            Flavour::Elf => {
223                let note = obj.section_id(StandardSection::GnuProperty);
224                obj.append_section_data(note, &elf::record(property), 8);
225            }
226            Flavour::Coff | Flavour::MachO => {}
227        }
228    }
229
230    /// Where the unwind table goes: the section the records are in and what it is aligned to, and
231    /// the second section holding what those records point at, on the format that keeps the two
232    /// apart.
233    fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
234        match self {
235            Flavour::Elf => (elf::FRAMES, None),
236            Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
237            Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
238        }
239    }
240
241    /// Anything that has to be written into the finished bytes rather than said to the writer.
242    fn finish(self, bytes: &mut [u8], ordered: &[String]) {
243        match self {
244            Flavour::Elf => elf::link(bytes, ordered),
245            Flavour::Coff | Flavour::MachO => {
246                debug_assert!(ordered.is_empty(), "a record this format cannot write");
247            }
248        }
249    }
250}
251
252/// Why an object file could not be written.
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub enum Error {
255    /// A machine or a platform this does not write objects for.
256    Format {
257        /// The triple that was asked for.
258        triple: String,
259    },
260    /// The writer refused something it was given, which is a bug here rather than in a program.
261    Refused {
262        /// What it said, already formatted.
263        why: String,
264    },
265}
266
267impl std::fmt::Display for Error {
268    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269        match self {
270            Error::Format { triple } => {
271                write!(f, "there is no object writer for {triple} in this compiler yet")
272            }
273            Error::Refused { why } => {
274                write!(f, "the object writer refused what it was given: {why}")
275            }
276        }
277    }
278}
279
280impl std::error::Error for Error {}
281
282/// One text section and the variables beside it, as a relocatable object in the target's format.
283///
284/// `info` is the debug sections, already encoded, and is empty in a build that asked for none.
285/// What it holds is bytes and relocations for the same reason [`Text::unwind`] is bytes: the
286/// format's answer is the producer's to give and what is left here is where the sections go.
287///
288/// # Errors
289///
290/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for
291/// anything the writer underneath objected to, which would be a bug here. An alias whose target
292/// this file does not define is refused the same way, since the front end is what reports that as
293/// a program's mistake and one reaching here means it did not. So is anything the target's format
294/// has no way to write, which for COFF is a thread-local variable, a reference through a table the
295/// platform does not have, a record of where a patcher's room is and a section the startup code is
296/// expected to gather. See [`Error`].
297pub fn write(
298    text: &Text,
299    data: &Data,
300    aliases: &[Alias],
301    target: &TargetInfo,
302    output: Output,
303    info: &Info,
304) -> Result<Vec<u8>, Error> {
305    let Output { sections, property } = output;
306    let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
307    let Some(flavour) = flavour else {
308        return Err(Error::Format { triple: target.tuple.to_string() });
309    };
310    if flavour == Flavour::Coff {
311        beyond(text, data, info)?;
312    }
313    let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
314    // The one that holds every function when they are not being split up. Asked for even when it
315    // will stay empty, because it is the section the writer underneath starts a file with anyway
316    // and gcc writes an empty `.text` under `-ffunction-sections` too.
317    let whole = obj.section_id(StandardSection::Text);
318    if !sections.functions {
319        obj.append_section_data(whole, &text.bytes, u64::from(text.align));
320    }
321
322    // Every function defined here, then every variable, then every name either of them wanted that
323    // is not. A name is looked up rather than added twice, because two symbols with one name is
324    // not a file a linker accepts.
325    let mut symbols = BTreeMap::new();
326    // Where each function ended up, in the order they were written, so that a relocation inside
327    // one goes into the section that one is in and one that points at the start of one can be
328    // written against that section. The same list as `text.funcs` and in the same order, so the
329    // two are walked together below.
330    let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
331    // Which text section each record of where a patcher's room is belongs to, in the order the
332    // records were added, which is the order their headers come out in. See `link`.
333    let mut ordered: Vec<String> = Vec::new();
334    for func in &text.funcs {
335        // A section of its own, holding this function's bytes and nothing else, so the linker can
336        // drop it when nothing reaches it. The name is what gcc writes, and the leading `.text.`
337        // is not decoration: `--gc-sections` and the linker scripts that place code both match on
338        // it, and a section called something else would be placed by the catch all rule.
339        //
340        // The room a patcher was promised in front of the label goes in it too. Those bytes are
341        // the function's, they are just not under its name: the symbol is where the label was and
342        // the room is what came before, so a section holding one without the other would be a
343        // section a linker could place with the room missing.
344        let ahead = func.patch.map_or(0, |patch| patch.before);
345        let (section, at) = if sections.functions {
346            let name = format!(".text.{}", func.name).into_bytes();
347            let id = obj.add_section(Vec::new(), name, SectionKind::Text);
348            let bytes = &text.bytes[func.start - ahead..func.start + func.len];
349            obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
350            (id, ahead as u64)
351        } else {
352            (whole, func.start as u64)
353        };
354        // Where the room is, in a section of its own that says nothing else. What reads it is a
355        // tracer patching every function in an image at once, and what it needs is every address
356        // in one place: a stripped kernel has no symbol table to walk instead, which is the whole
357        // reason the list is written rather than worked out later.
358        //
359        // The address is a relocation rather than a number, because a function is at a fixed
360        // offset in its own section and where that section lands is the linker's answer. It is
361        // written against the section rather than against the function's own name so that it still
362        // points at the room when the room is in front of the name.
363        //
364        // One section per function even when they all point at the same text, which is what gas
365        // produces and what lets a linker throw the record away with the function. `SHF_LINK_ORDER`
366        // is what ties the two together and it needs a section index the writer underneath does not
367        // set, so `link` fills it in afterwards. See `link`.
368        if let Some(patch) = func.patch {
369            let base = if sections.functions { func.start - ahead } else { 0 };
370            let name = elf::PATCHABLE.as_bytes().to_vec();
371            let id = obj.add_section(Vec::new(), name, SectionKind::Data);
372            obj.section_mut(id).flags = elf::ordered();
373            obj.append_section_data(id, &[0; 8], 8);
374            let symbol = obj.section_symbol(section);
375            let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
376                Error::Refused { why: "no relocation holds an address here".to_owned() }
377            })?;
378            obj.add_relocation(
379                id,
380                Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
381            )
382            .map_err(|why| Error::Refused { why: why.to_string() })?;
383            ordered.push(if sections.functions {
384                format!(".text.{}", func.name)
385            } else {
386                ".text".to_owned()
387            });
388        }
389        let id = obj.add_symbol(Symbol {
390            name: func.name.clone().into_bytes(),
391            value: at,
392            size: func.len as u64,
393            kind: SymbolKind::Text,
394            scope: scope_of(func.binding),
395            weak: func.binding == Binding::Weak,
396            section: SymbolSection::Section(section),
397            flags: SymbolFlags::None,
398        });
399        flavour.see(&mut obj, id, func.binding, func.visibility);
400        symbols.insert(func.name.clone(), id);
401        split.push((section, at));
402    }
403
404    // The places inside a function that have names of their own, which is where a label whose
405    // address an image holds is. After the functions, because the section one goes in is the
406    // section of the function it is inside and that is what the walk above worked out.
407    for label in &text.labels {
408        let after = text.funcs.partition_point(|func| func.start <= label.at);
409        let Some(index) = after.checked_sub(1) else {
410            let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
411            return Err(Error::Refused { why });
412        };
413        let func = &text.funcs[index];
414        let (section, at) = if sections.functions {
415            // From the start of the section rather than from the symbol, which is the same
416            // correction a relocation inside a function gets below.
417            let base = func.start - func.patch.map_or(0, |patch| patch.before);
418            (split[index].0, (label.at - base) as u64)
419        } else {
420            (whole, label.at as u64)
421        };
422        let id = obj.add_symbol(Symbol {
423            name: label.name.clone().into_bytes(),
424            value: at,
425            // A label has no length. What is at it is the rest of the function, and a size here
426            // would be a claim that the bytes after it are a thing of their own.
427            size: 0,
428            kind: SymbolKind::Label,
429            // Never offered to another file. The name is one the compiler minted and what it
430            // points at is the middle of a function, so the only thing that resolves against it
431            // is the image in this same file that asked for it.
432            scope: SymbolScope::Compilation,
433            weak: false,
434            section: SymbolSection::Section(section),
435            flags: SymbolFlags::None,
436        });
437        symbols.insert(label.name.clone(), id);
438    }
439
440    // Where each variable's image landed in the section it went into, kept because a relocation in
441    // an image counts from the start of the image and one in a file counts from the start of the
442    // section. A variable that is not in a section has no entry, since nothing in a merged one can
443    // hold a relocation: the linker is being asked for zeroed space rather than for an image.
444    let mut placed = Vec::with_capacity(data.objects.len());
445    // The sections the writer has no name of its own for, remembered by name so that every variable
446    // that wants one lands in the same one. The rest come back from `section_id`, which already
447    // answers with the section it made the first time it was asked.
448    let mut named = HashMap::new();
449    for object in &data.objects {
450        let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
451        let id = obj.add_symbol(Symbol {
452            name: object.name.clone().into_bytes(),
453            // A common symbol says what it wants rather than where it is, and what it wants is
454            // recorded where an ordinary symbol records its address.
455            value: if object.place == Place::Merged { object.align } else { offset },
456            size: object.size,
457            // A thread-local variable is a different kind of symbol rather than a symbol in a
458            // different section, and it has to be both: the kind is what a linker checks a
459            // relocation against, so a `R_X86_64_PC32` aimed at one is refused rather than
460            // resolved to an address that would have been one thread's and is nobody's.
461            kind: match object.place {
462                Place::Thread { .. } => SymbolKind::Tls,
463                _ => SymbolKind::Data,
464            },
465            scope: scope_of(object.binding),
466            weak: object.binding == Binding::Weak,
467            section,
468            flags: SymbolFlags::None,
469        });
470        flavour.see(&mut obj, id, object.binding, object.visibility);
471        symbols.insert(object.name.clone(), id);
472        placed.push((section.id(), offset));
473    }
474
475    // The jump tables, which the code reaches by name and which reach the code in turn. Placed
476    // before any relocation of the text is added, since the instruction that reads one names it.
477    let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
478
479    // The distances between two labels, written into the images just placed. Both labels were
480    // added above with the section they are in and where in it, so the distance is the one value
481    // less the other, and it is a number only when the section is the same one.
482    for apart in &data.apart {
483        let (Some(section), offset) = placed[apart.object] else { continue };
484        let value = distance(&obj, &symbols, apart)?;
485        let bytes = usize::from(apart.bytes);
486        let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
487        let at = at + apart.at;
488        let image = obj.section_mut(section).data_mut();
489        image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
490    }
491
492    // A second name for something already added, which is where the alias's own binding is the
493    // only thing it does not take from what it points at: the target of one may be a `static` and
494    // the alias of it may not be. Before the loop below rather than after it, because a reference
495    // to the new name is a reference to something this file defines and would otherwise be added
496    // as a name this file wants from somewhere else.
497    for alias in aliases {
498        let Some(&id) = symbols.get(&alias.target) else {
499            let why =
500                format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
501            return Err(Error::Refused { why });
502        };
503        let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
504        let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
505        let id = obj.add_symbol(Symbol {
506            name: alias.name.clone().into_bytes(),
507            value,
508            size,
509            kind,
510            scope: scope_of(alias.binding),
511            weak: alias.binding == Binding::Weak,
512            section,
513            flags: SymbolFlags::None,
514        });
515        flavour.see(&mut obj, id, alias.binding, alias.visibility);
516        symbols.insert(alias.name.clone(), id);
517    }
518
519    // Not the unwind table's, which name functions this file defines and are written against the
520    // section rather than against the name. A record for anything else is refused below, so a name
521    // added here for one would be a name nothing goes on to use.
522    // The names a declaration wrote `weak` on, which the link is allowed to leave undefined and
523    // whose references then read a zero address. The listing writes a `.weak` for each of the same
524    // names, so the two paths put the same entries in whether or not anything refers to one.
525    let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
526    let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
527    // The names something here reaches through the thread pointer, which is the one thing about an
528    // undefined name this file does know. A reference to a thread-local variable is a different kind
529    // of reference from a reference to an ordinary one and the code that makes it is already
530    // different, so the file has been told, and ELF wants the symbol to say so as well.
531    let thread: HashSet<&str> = relocs()
532        .filter(|reloc| reloc.kind == Reference::Thread)
533        .map(|reloc| reloc.symbol.as_str())
534        .collect();
535    let wanted: Vec<&String> =
536        relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
537    for name in wanted {
538        if symbols.contains_key(name) || tables.contains_key(name) {
539            continue;
540        }
541        let id = obj.add_symbol(Symbol {
542            name: name.clone().into_bytes(),
543            value: 0,
544            size: 0,
545            // What kind of thing an undefined name is is not known here and does not have to be:
546            // a linker resolves an undefined symbol by its name, and the type of one that is not
547            // defined anywhere in this file is nothing this file can say. A thread-local one is the
548            // exception, and the linker makes it one. A reference to a thread-local variable is
549            // satisfied by an offset into a block rather than by an address, so the linker has to
550            // know which of the two it is being asked for before it has found the definition, and it
551            // refuses a link where one file says `STT_TLS` and another does not rather than picking
552            // one. That is tamnd/rucc#1461: libmpfr writes `__gmpfr_flags` in one file and reads it
553            // in a hundred others, and `ld` stopped at the first reader with a mismatch.
554            kind: if thread.contains(name.as_str()) {
555                SymbolKind::Tls
556            } else {
557                SymbolKind::Unknown
558            },
559            scope: SymbolScope::Dynamic,
560            weak: weak.contains(name.as_str()),
561            section: SymbolSection::Undefined,
562            flags: SymbolFlags::None,
563        });
564        symbols.insert(name.clone(), id);
565    }
566
567    for reloc in &text.relocs {
568        // Which function's bytes this one is in, which is the question only the split path has to
569        // ask: when there is one text section every offset in it is already the offset in it.
570        // Every relocation is inside some function, since the padding between two of them is
571        // instructions that do nothing and holds nothing a linker fills in.
572        let (section, at) = if sections.functions {
573            let after = text.funcs.partition_point(|func| func.start <= reloc.at);
574            let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
575                let why = format!("a relocation at {} is in front of every function", reloc.at);
576                return Err(Error::Refused { why });
577            };
578            // From the start of the section rather than from the symbol, and the two are not the
579            // same byte in a function with room in front of its label.
580            let base = func.start - func.patch.map_or(0, |patch| patch.before);
581            (split[after - 1].0, (reloc.at - base) as u64)
582        } else {
583            (whole, reloc.at as u64)
584        };
585        // The address of a jump table, which is against the section the table is in and not a
586        // name of its own, the way gas writes a reference to a `.L` label: such a name is not
587        // kept in the symbol table, so what the linker is told is the section and how far in.
588        if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
589            let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
590                why: format!("no relocation is {:?}", reloc.kind),
591            })?;
592            let symbol = obj.section_symbol(table);
593            let addend = reloc.addend + offset as i64;
594            obj.add_relocation(section, Relocation { offset: at, symbol, addend, flags })
595                .map_err(|why| Error::Refused { why: why.to_string() })?;
596            continue;
597        }
598        add(&mut obj, section, at, reloc, &symbols, flavour)?;
599    }
600
601    // The unwind table, if there is one. Its own section rather than part of the text, because it
602    // is read rather than run: the loader maps it and the linker gathers every input's into one
603    // table and builds the index the unwinder searches.
604    if !text.unwind.bytes.is_empty() {
605        let ((name, align), second) = flavour.tables();
606        let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
607        obj.append_section_data(frames, &text.unwind.bytes, align);
608        // What the rows point at, on the format that keeps the descriptions in a section of their
609        // own, and a name for each of them, because a row reaches one through a relocation and a
610        // relocation names a symbol. The names are never offered to another file: what they point
611        // at is one function's prologue, described for the runtime of this program and nothing else.
612        let mut described = HashMap::new();
613        if !text.unwind.info.is_empty() {
614            let Some((name, align)) = second else {
615                let why = "an unwind table here is one section and it was given two".to_owned();
616                return Err(Error::Refused { why });
617            };
618            let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
619            obj.append_section_data(codes, &text.unwind.info, align);
620            for label in &text.unwind.labels {
621                let id = obj.add_symbol(Symbol {
622                    name: label.name.clone().into_bytes(),
623                    value: label.at as u64,
624                    size: 0,
625                    kind: SymbolKind::Label,
626                    scope: SymbolScope::Compilation,
627                    weak: false,
628                    section: SymbolSection::Section(codes),
629                    flags: SymbolFlags::None,
630                });
631                described.insert(label.name.clone(), id);
632            }
633        }
634        // The call site tables of the functions with a landing pad, which a record reaches through
635        // the section's own symbol and the table's offset in it, the same way gcc's records do.
636        // The personality routine's pointer is an ordinary data symbol of this file and is looked
637        // up with the rest below.
638        if !text.unwind.except.is_empty() {
639            let except =
640                obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
641            obj.append_section_data(except, &text.unwind.except, 4);
642            described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
643        }
644        for reloc in &text.unwind.relocs {
645            let found = described.get(&reloc.symbol).or_else(|| {
646                // Only a variable this file defines. A function is reached through its section
647                // below for the reasons given there, and a name defined somewhere else is refused
648                // there as well.
649                let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
650                if ours { symbols.get(&reloc.symbol) } else { None }
651            });
652            let (symbol, addend) = match found {
653                // A description in the section above, reached by its own name and needing no
654                // correction, since the name is at the description rather than at the front of the
655                // section it is in.
656                Some(&id) => (id, reloc.addend),
657                // A function, and against the section it is in rather than against its own name,
658                // which is the same reason the record of a patcher's room is written that way and
659                // one more besides. The section is the only one of the two that is settled here: a
660                // global name is answered at load time by whichever object defines it first, so a
661                // distance measured to one is not a distance the linker can work out, and it says
662                // so and stops. The effect was that nothing this compiler wrote could go into a
663                // shared library at all, because every function has a record and every record
664                // pointed at a name.
665                //
666                // A function defined elsewhere has no record here, so the lookup failing means the
667                // record is for something that is not a function in this file, and that is a bug
668                // rather than a shape to handle: the writer says what it was given rather than
669                // guessing.
670                None => {
671                    let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
672                    let Some((section, at)) = found.map(|i| split[i]) else {
673                        let why = format!(
674                            "'{}' has an unwind record and is not a function here",
675                            reloc.symbol
676                        );
677                        return Err(Error::Refused { why });
678                    };
679                    // Where the function starts inside its section, since the section symbol is
680                    // where the section starts and the two are the same byte only for the first
681                    // function in one.
682                    (obj.section_symbol(section), reloc.addend + at as i64)
683                }
684            };
685            let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
686                why: format!("no relocation is {:?}", reloc.kind),
687            })?;
688            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
689            obj.add_relocation(frames, record)
690                .map_err(|why| Error::Refused { why: why.to_string() })?;
691        }
692    }
693    // The debug information, if the build asked for any. One section per chunk under the name
694    // DWARF gives it, and none of them allocated: the loader does not map a debug section and
695    // nothing at run time reads one, which is what tells this apart from the unwind table, whose
696    // whole point is that a program walking its own stack can reach it.
697    //
698    // Every section is added before any relocation is, because a relocation in one of them names
699    // another as often as it names a function, and a name is resolved against the sections the
700    // file already has.
701    let mut named = HashMap::new();
702    for chunk in &info.chunks {
703        let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
704        obj.append_section_data(id, &chunk.bytes, 1);
705        named.insert(chunk.name.as_str(), id);
706    }
707    for chunk in &info.chunks {
708        let section = named[chunk.name.as_str()];
709        for reloc in &chunk.relocs {
710            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
711                // Another debug section, reached by its own name. The distance is from the front
712                // of that section, which is what the section symbol is, so the addend stands.
713                Some(&id) => (obj.section_symbol(id), reloc.addend),
714                // A function, and against the section it is in rather than against its own name,
715                // for the reason the unwind table's records are written that way: a global name is
716                // answered at load time by whichever object defines it first, and a distance to
717                // one is not a distance a linker can work out.
718                None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
719                    Some(which) => {
720                        let (section, at) = split[which];
721                        (obj.section_symbol(section), reloc.addend + at as i64)
722                    }
723                    // Or a variable this file defines, which a `DW_TAG_variable` asks for the
724                    // address of. Against its section for the reason a function is, where it has
725                    // one. A variable the linker is being asked for zeroed space for has no
726                    // section to count from and nothing but its own name to ask by, which is the
727                    // one case here where the name goes in the relocation.
728                    None => {
729                        let found = data.objects.iter().position(|had| had.name == reloc.symbol);
730                        let Some(which) = found else {
731                            let why = format!(
732                                "'{}' is named by the debug information and is not defined here",
733                                reloc.symbol
734                            );
735                            return Err(Error::Refused { why });
736                        };
737                        match placed[which] {
738                            (Some(section), at) => {
739                                (obj.section_symbol(section), reloc.addend + at as i64)
740                            }
741                            (None, _) => (symbols[&reloc.symbol], reloc.addend),
742                        }
743                    }
744                },
745            };
746            let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
747                why: format!("no relocation is {:?}", reloc.kind),
748            })?;
749            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
750            obj.add_relocation(section, record)
751                .map_err(|why| Error::Refused { why: why.to_string() })?;
752        }
753    }
754    for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
755        let Some(section) = section else { continue };
756        for reloc in &object.relocs {
757            add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
758        }
759    }
760
761    // What the file was built to have checked, when it was built to have anything checked. Left
762    // out otherwise rather than written as a zero, because a linker treats a missing note and a
763    // note with no bits in it the same way and gcc writes nothing.
764    flavour.property(&mut obj, property);
765
766    // Written rather than left out, because a linker that does not find it in every input marks
767    // the stack executable, on the format that has one.
768    flavour.marker(&mut obj);
769
770    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
771    flavour.finish(&mut bytes, &ordered);
772    Ok(bytes)
773}
774
775/// How far one label is from another, from the symbols [`write()`] added for them.
776///
777/// # Errors
778///
779/// [`Error::Refused`] for a label that is not here, for two that are in different sections, and
780/// for a distance too far for the width it is written in.
781fn distance(
782    obj: &Writer<'_>,
783    symbols: &BTreeMap<String, SymbolId>,
784    apart: &Apart,
785) -> Result<i64, Error> {
786    let find = |name: &str| match symbols.get(name) {
787        Some(&id) => Ok(obj.symbol(id)),
788        None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
789    };
790    let (to, from) = (find(&apart.to)?, find(&apart.from)?);
791    if to.section != from.section {
792        let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
793        return Err(Error::Refused { why });
794    }
795    let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
796    let bits = u32::from(apart.bytes) * 8;
797    if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
798        let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
799        return Err(Error::Refused { why });
800    }
801    Ok(value)
802}
803
804/// Everything in this module the target's format has no way to write, refused by name.
805///
806/// Each of these is something ELF has and COFF does not, and each would otherwise be written as the
807/// nearest thing rather than refused, which is worse: a thread-local variable written as an ordinary
808/// one is a program where every thread shares what the source said each would have its own copy of,
809/// and a constructor list under a name the Windows runtime does not gather is a program whose
810/// constructors never run. A message naming the feature is what the caller turns into a diagnostic,
811/// and the front end refusing first is what stops one ever being seen.
812///
813/// # Errors
814///
815/// [`Error::Refused`], naming the one it found first.
816fn beyond(text: &Text, data: &Data, info: &Info) -> Result<(), Error> {
817    let why = |why: String| Err(Error::Refused { why });
818    if !info.chunks.is_empty() {
819        return why("debug information here goes in sections this writer does not name".to_owned());
820    }
821    if text.funcs.iter().any(|func| func.patch.is_some()) {
822        return why("a record of where a patcher's room is has no section flags here".to_owned());
823    }
824    for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
825        if matches!(
826            reloc.kind,
827            Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
828        ) {
829            return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
830        }
831    }
832    for object in &data.objects {
833        if matches!(object.place, Place::Thread { zero: true }) {
834            return why(format!(
835                "'{}' is zeroed thread-local storage, which is not here",
836                object.name
837            ));
838        }
839        let Place::Named(name) = &object.place else { continue };
840        if Array::of(name).is_some() {
841            return why(format!("'{name}' is not a list the startup code here gathers"));
842        }
843    }
844    Ok(())
845}
846
847/// Every name a linker can find in the object [`write()`] would write from the same input.
848///
849/// What asks for this is the archive writer. A static link resolves through the symbol index, so an
850/// index entry has to name a symbol the member really defines: an entry for a name that is not in
851/// the member is an archive the linker searches, pulls the member out of, and then still reports
852/// the name undefined. So the list comes from the writer rather than from the caller, because the
853/// writer is the only thing that knows what it wrote.
854///
855/// The names are as the C program spelled them, with nothing in front of them, which is what both
856/// the formats this writes have on this machine. Mach-O puts an underscore there and so does COFF on
857/// a 32-bit machine, and when either of those is written this is the function that has to say so,
858/// which is why it asks about the target it otherwise would not have to.
859///
860/// Order is the functions, then the variables, then the aliases, each in the order the module held
861/// them, which is the order [`write()`] adds the symbols in. A `static` is left out: it is a name the
862/// link has already finished with by the time an archive is searched, and an index entry for one
863/// would offer the linker a definition it is not allowed to use.
864///
865/// # Errors
866///
867/// [`Error::Format`] for a machine or a platform this does not write, which is the same refusal
868/// [`write()`] gives and is here for the same reason: a list of undecorated names for a format whose
869/// symbols carry an underscore is worse than no list at all.
870pub fn defines(
871    text: &Text,
872    data: &Data,
873    aliases: &[Alias],
874    target: &TargetInfo,
875) -> Result<Vec<String>, Error> {
876    if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
877        return Err(Error::Format { triple: target.tuple.to_string() });
878    }
879    let names = text
880        .funcs
881        .iter()
882        .filter(|func| func.binding != Binding::Local)
883        .map(|func| func.name.clone())
884        .chain(
885            data.objects
886                .iter()
887                .filter(|object| object.binding != Binding::Local)
888                .map(|object| object.name.clone()),
889        )
890        .chain(
891            aliases
892                .iter()
893                .filter(|alias| alias.binding != Binding::Local)
894                .map(|alias| alias.name.clone()),
895        )
896        .collect();
897    Ok(names)
898}
899
900/// One variable's image into the section it belongs in, and where in that section it landed.
901///
902/// A zero filled variable takes as many bytes of the file as it is long on the way in and none on
903/// the way out, which is the whole point of the section it goes in. A merged one goes in no section
904/// at all: the linker is being asked for that much zeroed space under that name, and where it ends
905/// up is the linker's answer rather than this file's.
906fn put(
907    obj: &mut Writer<'_>,
908    object: &Object,
909    named: &mut HashMap<String, object::write::SectionId>,
910    sections: Sections,
911    flavour: Flavour,
912) -> (SymbolSection, u64) {
913    // A section of its own, named after the variable and after the section it would have gone in,
914    // which is what `-fdata-sections` asks for. A merged variable has no section to split and a
915    // named one was named by the program, so both are left where they are: the first is a request
916    // to the linker rather than an image, and the second would otherwise have the flag silently
917    // overrule what the source said.
918    if sections.data {
919        if let Some(name) = object.place.split(&object.name) {
920            let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
921            let offset = if carries_no_bytes(&object.place) {
922                obj.append_section_bss(section, object.size, object.align)
923            } else {
924                obj.append_section_data(section, &object.bytes, object.align)
925            };
926            return (SymbolSection::Section(section), offset);
927        }
928    }
929    let section = match &object.place {
930        Place::Written => obj.section_id(StandardSection::Data),
931        Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
932        // Read only after the loader has written it, which the writer knows as the relocatable
933        // read only data section and which is `.data.rel.ro` on ELF. The `.local` half is a layout
934        // hint the writer has no name for, so it is added by hand and remembered: asking again
935        // would make a second section with the same name, and a file with one of those per variable
936        // is a file whose section headers outweigh what they describe.
937        Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
938            Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
939            None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
940        },
941        Place::Zero => obj.section_id(StandardSection::UninitializedData),
942        Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
943        Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
944        Place::Merged => return (SymbolSection::Common, 0),
945        // A named section is the program's word for where this goes, and a program that names one
946        // wants what it named rather than what would have been chosen. It is written as ordinary
947        // data because nothing in the IR says otherwise, except for the three names the startup
948        // code calls what it finds in, which have a section type of their own and are gathered by
949        // the linker whether or not they carry it.
950        Place::Named(name) => {
951            let section = made(obj, named, name, SectionKind::Data);
952            if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
953                obj.section_mut(section).flags = flags;
954            }
955            section
956        }
957    };
958    let offset = if carries_no_bytes(&object.place) {
959        obj.append_section_bss(section, object.size, object.align)
960    } else {
961        obj.append_section_data(section, &object.bytes, object.align)
962    };
963    (SymbolSection::Section(section), offset)
964}
965
966/// Every jump table of the text, in `.rodata`, each cell a distance the linker works out, giving
967/// back the section each one went in and where in it, by the name the code gives it.
968///
969/// The section is `.rodata` for all of them, or `.rodata.` and the function's name under
970/// `-fdata-sections`, which is where gcc puts a table in each case. Not split under
971/// `-ffunction-sections` alone, which is gcc's answer too.
972///
973/// A cell is the distance from the front of the table to a block, and the block is in the text
974/// while the table is not, so it is `R_X86_64_PC32` against the function's section with the block's
975/// offset and the cell's own place in the table as the addend. Against the section rather than the
976/// function's name for the reason the unwind records are: a global name may be answered by another
977/// object at load time, and a linker refuses a distance to one.
978fn tables(
979    obj: &mut Writer<'_>,
980    text: &Text,
981    split: &[(object::write::SectionId, u64)],
982    named: &mut HashMap<String, object::write::SectionId>,
983    sections: Sections,
984    flavour: Flavour,
985) -> Result<HashMap<String, (object::write::SectionId, u64)>, Error> {
986    let mut placed = HashMap::new();
987    if text.tables.is_empty() {
988        return Ok(placed);
989    }
990    if flavour != Flavour::Elf {
991        let why = "a jump table outside the code is written on ELF only".to_owned();
992        return Err(Error::Refused { why });
993    }
994    let flags = flavour.reloc(Reference::Away, 0).ok_or_else(|| Error::Refused {
995        why: "no relocation is a distance from where it is written".to_owned(),
996    })?;
997    for table in &text.tables {
998        let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
999            why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1000        })?;
1001        let section = if sections.data {
1002            let name = format!(".rodata.{}", func.name);
1003            made(obj, named, &name, SectionKind::ReadOnlyData)
1004        } else {
1005            obj.section_id(StandardSection::ReadOnlyData)
1006        };
1007        let offset = obj.append_section_data(section, &vec![0; 4 * table.cells.len()], 4);
1008        placed.insert(table.name.clone(), (section, offset));
1009        let (code, at) = split[table.func];
1010        let symbol = obj.section_symbol(code);
1011        for (index, &cell) in table.cells.iter().enumerate() {
1012            let place = 4 * index as u64;
1013            let addend = at as i64 + cell as i64 + place as i64;
1014            let record = Relocation { offset: offset + place, symbol, addend, flags };
1015            obj.add_relocation(section, record)
1016                .map_err(|why| Error::Refused { why: why.to_string() })?;
1017        }
1018    }
1019    Ok(placed)
1020}
1021
1022/// Whether the section this goes in says how big the variable is and holds none of its bytes.
1023///
1024/// Two of them, and they are the same answer twice: `.bss` is the image that is all zeros, and
1025/// `.tbss` is a thread's own copy of one. A section like this costs its size in the section header
1026/// and nothing in the file, which is what keeps a program with a large zeroed array small.
1027fn carries_no_bytes(place: &Place) -> bool {
1028    matches!(place, Place::Zero | Place::Thread { zero: true })
1029}
1030
1031/// The section of this name, made the first time it is asked for and found afterwards.
1032///
1033/// Two variables the program put the same section name on belong in one section, the way two in
1034/// `.data` do. Asking the writer for a new one each time would make a second header with the same
1035/// name, which a linker takes and which makes a file with ten constructors in it carry ten section
1036/// headers describing eight bytes each. `section_id` does this already for the sections it has
1037/// names of its own for, and this is the same answer for the ones it does not.
1038fn made(
1039    obj: &mut Writer<'_>,
1040    named: &mut HashMap<String, object::write::SectionId>,
1041    name: &str,
1042    kind: SectionKind,
1043) -> object::write::SectionId {
1044    if let Some(section) = named.get(name) {
1045        return *section;
1046    }
1047    let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1048    named.insert(name.to_owned(), section);
1049    section
1050}
1051
1052/// What a section split off for one variable is, which is what the section it was split off from
1053/// was.
1054///
1055/// Splitting changes the name and nothing else. A variable that was going to be in a page the
1056/// loader maps read only is still in one, and a zero filled variable still costs the file nothing,
1057/// so the flags a linker reads off the section header have to come out the same as they would
1058/// have. The two kinds with no section of their own never reach here, and `Data` for them is a
1059/// value that is never used rather than a claim about either.
1060fn kind_of(place: &Place) -> SectionKind {
1061    match place {
1062        Place::ReadOnly => SectionKind::ReadOnlyData,
1063        Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1064        Place::Zero => SectionKind::UninitializedData,
1065        Place::Thread { zero: false } => SectionKind::Tls,
1066        Place::Thread { zero: true } => SectionKind::UninitializedTls,
1067        Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
1068    }
1069}
1070
1071/// One relocation, `at` bytes into the section it ended up in.
1072///
1073/// The offset is worked out by the caller rather than here, because the two callers count from
1074/// different places: a relocation in an image counts from the start of that image and a relocation
1075/// in a function counts from the start of that function, and neither of those is where the section
1076/// begins once something else is in front of it.
1077fn add(
1078    obj: &mut Writer<'_>,
1079    section: object::write::SectionId,
1080    at: u64,
1081    reloc: &Reloc,
1082    symbols: &BTreeMap<String, SymbolId>,
1083    flavour: Flavour,
1084) -> Result<(), Error> {
1085    let flags = flavour
1086        .reloc(reloc.kind, reloc.after)
1087        .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1088    obj.add_relocation(
1089        section,
1090        Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1091    )
1092    .map_err(|why| Error::Refused { why: why.to_string() })
1093}
1094
1095/// How far a name reaches, which is the one thing about a symbol ELF calls its binding.
1096///
1097/// `SymbolScope` is two facts in one word, and the trap is that the middle one is not the neutral
1098/// answer it reads as. The writer turns `Compilation` into a local symbol, and it turns the choice
1099/// between `Linkage` and `Dynamic` into `st_other`: `Linkage` is `STV_HIDDEN` and `Dynamic` is
1100/// `STV_DEFAULT`. So there is no way to say global and decline to say anything about visibility,
1101/// and picking the one whose name sounds like the smaller claim is picking hidden. That is what
1102/// tamnd/rucc#733 was.
1103///
1104/// `Dynamic` is what every global asks for here, and the visibility is said afterwards by
1105/// [`see`] rather than through this, so that nothing about `st_other` depends on reading one of
1106/// these four names the way its author meant it.
1107pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1108    match binding {
1109        Binding::Local => SymbolScope::Compilation,
1110        Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1111    }
1112}
1113
1114#[cfg(test)]
1115mod tests {
1116    use super::*;
1117
1118    use object::read::elf::Sym as _;
1119    use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
1120    use object::{elf, pe};
1121    use rucc_target::{Arch, Env, Os, Triple};
1122
1123    use crate::elf::PATCHABLE;
1124    use crate::section::{Extent, Marker, Patch, Reloc};
1125
1126    /// A linux x86-64 target, which is the only one this writes.
1127    fn target() -> TargetInfo {
1128        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1129    }
1130
1131    /// One function of that name, at that offset, that many bytes long, and visible that far.
1132    ///
1133    /// Visibility is the field these cases mostly have no opinion about, so it is the one the
1134    /// helper fills in and the two that do have an opinion write for themselves.
1135    fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1136        Extent {
1137            name,
1138            start,
1139            len,
1140            align: crate::FUNC_ALIGN,
1141            binding,
1142            visibility: Visibility::Default,
1143            patch: None,
1144            landings: Vec::new(),
1145        }
1146    }
1147
1148    /// A call to something outside the file, which is the shape every case here starts from.
1149    fn calling(name: &str) -> Text {
1150        Text {
1151            bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1152            funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1153            relocs: vec![Reloc {
1154                at: 1,
1155                symbol: name.to_owned(),
1156                kind: Reference::Call,
1157                addend: -4,
1158                after: 0,
1159            }],
1160            ..Text::default()
1161        }
1162    }
1163
1164    #[test]
1165    fn the_bytes_come_back_out_of_the_section_they_went_into() {
1166        let text = calling("puts");
1167        let bytes =
1168            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1169                .expect("an object");
1170        let file = object::File::parse(&bytes[..]).expect("a readable object");
1171        let section = file.section_by_name(".text").expect("a text section");
1172        assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1173    }
1174
1175    #[test]
1176    fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1177        let mut text = calling("puts");
1178        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1179        text.bytes.resize(17, 0x90);
1180        let bytes =
1181            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1182                .expect("an object");
1183        let file = object::File::parse(&bytes[..]).expect("a readable object");
1184        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1185        assert_eq!(g.address(), 16);
1186        assert_eq!(g.size(), 1);
1187        assert_eq!(g.kind(), SymbolKind::Text);
1188        assert!(g.is_global(), "nothing said otherwise about this one");
1189    }
1190
1191    #[test]
1192    fn a_function_no_other_file_can_see_is_a_local_symbol() {
1193        let mut text = calling("puts");
1194        text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1195        text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1196        text.bytes.resize(33, 0x90);
1197        let bytes =
1198            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1199                .expect("an object");
1200        let file = object::File::parse(&bytes[..]).expect("a readable object");
1201        let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1202        // A symbol the linker keeps and does not let another file reach, which is the whole of
1203        // what `static` on a function means and what two files each defining their own need.
1204        assert!(hidden.is_local(), "a static function must not be offered to the linker");
1205        assert!(!hidden.is_weak());
1206        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1207        assert!(shared.is_weak(), "a weak function has to be able to lose");
1208        assert!(shared.is_global());
1209    }
1210
1211    /// A global is `STV_DEFAULT`, so a shared library built from these objects exports something.
1212    ///
1213    /// The bug in tamnd/rucc#733. Every global came out `STV_HIDDEN`, which a static link does not
1214    /// look at, so nothing here noticed and SQLite linked and ran and the whole test suite passed.
1215    /// What it costs is the dynamic symbol table: `gcc -shared` over one of these objects produced
1216    /// a library with an empty one, and `dlsym` could not find a function the file plainly defines.
1217    ///
1218    /// Written against `st_other` itself rather than against the reader's `scope`, because `scope`
1219    /// is the word that was misread in the first place and a test that asks it the same question
1220    /// would agree with whatever the writer did.
1221    /// The record of where a patcher's room is, and what it says about it.
1222    ///
1223    /// Four things have to be right at once for a linker to take it: the flags, the alignment, the
1224    /// relocation and the section it says it is ordered after. The last of those is the one the
1225    /// writer underneath cannot say, so a zero there would be a file `ld` refuses and a test that
1226    /// only looked at the bytes would not see it.
1227    #[test]
1228    fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1229        let mut text = calling("puts");
1230        text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1231        text.funcs[0].start = 3;
1232        text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1233        text.relocs[0].at = 4;
1234        let bytes =
1235            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1236                .expect("an object");
1237        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1238        let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1239        assert_eq!(section.size(), 8, "one address, and this file defines one function");
1240        assert_eq!(section.align(), 8);
1241        let header = section.elf_section_header();
1242        assert_eq!(
1243            header.sh_flags.get(Endianness::Little),
1244            elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1245        );
1246        // Which is the whole point of the fixup: the index has to be the text section's own, and
1247        // the writer underneath had written a zero there.
1248        let index = file.section_by_name(".text").expect("a text section").index().0;
1249        assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1250        assert_ne!(index, 0);
1251
1252        // And the address, which is the front of the room rather than the function's own symbol.
1253        let [(at, reloc)] = &section.relocations().collect::<Vec<_>>()[..] else {
1254            panic!("one address in the record")
1255        };
1256        assert_eq!(*at, 0);
1257        assert_eq!(reloc.addend(), 0);
1258        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1259    }
1260
1261    /// And a file that asked for none has no such section, which is nearly every file.
1262    #[test]
1263    fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1264        let text = calling("puts");
1265        let bytes =
1266            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1267                .expect("an object");
1268        let file = object::File::parse(&bytes[..]).expect("a readable object");
1269        assert!(file.section_by_name(PATCHABLE).is_none());
1270    }
1271
1272    /// The same when each function is a section of its own, which is what a kernel builds with.
1273    ///
1274    /// Each record then points at a different section, which is what makes the pairing worth
1275    /// asserting: getting it backwards would still produce a file every tool reads and every
1276    /// address in it would be about the wrong function.
1277    #[test]
1278    fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1279        let mut text = calling("puts");
1280        text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1281        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1282        text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1283        text.bytes.resize(17, 0x90);
1284        let output =
1285            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1286        let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1287            .expect("an object");
1288        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1289        let links: Vec<usize> = file
1290            .sections()
1291            .filter(|section| section.name() == Ok(PATCHABLE))
1292            .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1293            .collect();
1294        let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1295        assert_eq!(links, [index(".text.f"), index(".text.g")]);
1296    }
1297
1298    #[test]
1299    fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1300        let mut text = calling("puts");
1301        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1302        text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1303        text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1304        text.bytes.resize(49, 0x90);
1305        let bytes =
1306            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1307                .expect("an object");
1308        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1309        let visibility = |name: &str| {
1310            file.symbols()
1311                .find(|s| s.name() == Ok(name))
1312                .expect("the function")
1313                .elf_symbol()
1314                .st_visibility()
1315        };
1316        // Nothing said hidden about either of these, so neither is.
1317        assert_eq!(visibility("g"), elf::STV_DEFAULT);
1318        assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1319        // The `static` one is local, and a local symbol's visibility means nothing either way,
1320        // which is why the binding is what this asks about.
1321        assert_eq!(visibility("s"), elf::STV_DEFAULT);
1322    }
1323
1324    /// And the other direction: a name that did ask to be hidden is hidden, and a protected one is
1325    /// protected.
1326    ///
1327    /// The half of tamnd/rucc#733 that the fix above left open. Saying `STV_DEFAULT` for everything
1328    /// is right for everything nobody marked and wrong the moment something is marked, so the two
1329    /// tests together are what says the field carries an answer rather than a constant.
1330    ///
1331    /// Both are asked of a function and of a variable, because they are added by two different
1332    /// loops in `write` and a field one of them fills in is not a field the other one does.
1333    #[test]
1334    fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1335        let mut text = calling("puts");
1336        for (index, (name, seen)) in
1337            [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1338        {
1339            let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1340            func.visibility = seen;
1341            text.funcs.push(func);
1342        }
1343        text.bytes.resize(49, 0x90);
1344        let mut data = Data::default();
1345        for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1346            let mut object = variable(name, Place::Written);
1347            object.visibility = seen;
1348            data.objects.push(object);
1349        }
1350        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1351            .expect("an object");
1352        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1353        let visibility = |name: &str| {
1354            file.symbols()
1355                .find(|s| s.name() == Ok(name))
1356                .expect("the symbol")
1357                .elf_symbol()
1358                .st_visibility()
1359        };
1360        assert_eq!(visibility("h"), elf::STV_HIDDEN);
1361        assert_eq!(visibility("p"), elf::STV_PROTECTED);
1362        assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1363        assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1364        // The one thing a visibility must not disturb, since `st_info` and `st_other` are written
1365        // in one go and the second was set after the first.
1366        let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1367        assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1368        assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1369    }
1370
1371    #[test]
1372    fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1373        let bytes = write(
1374            &calling("puts"),
1375            &Data::default(),
1376            &[],
1377            &target(),
1378            Output::default(),
1379            &Info::default(),
1380        )
1381        .expect("an object");
1382        let file = object::File::parse(&bytes[..]).expect("a readable object");
1383        let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1384        assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1385    }
1386
1387    #[test]
1388    fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1389        for (reference, wanted) in [
1390            (Reference::Call, elf::R_X86_64_PLT32),
1391            (Reference::Data, elf::R_X86_64_PC32),
1392            (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1393            (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1394            (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1395            (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1396        ] {
1397            let mut text = calling("puts");
1398            text.relocs[0].kind = reference;
1399            let bytes =
1400                write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1401                    .expect("an object");
1402            let file = object::File::parse(&bytes[..]).expect("a readable object");
1403            let section = file.section_by_name(".text").expect("a text section");
1404            let (offset, reloc) = section.relocations().next().expect("one relocation");
1405            assert_eq!(offset, 1);
1406            assert_eq!(reloc.addend(), -4);
1407            assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1408        }
1409    }
1410
1411    #[test]
1412    fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1413        let mut text = calling("puts");
1414        text.relocs.push(Reloc {
1415            at: 1,
1416            symbol: "puts".to_owned(),
1417            kind: Reference::Call,
1418            addend: -4,
1419            after: 0,
1420        });
1421        let bytes =
1422            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1423                .expect("an object");
1424        let file = object::File::parse(&bytes[..]).expect("a readable object");
1425        assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1426    }
1427
1428    #[test]
1429    fn a_function_that_is_also_called_is_not_a_second_symbol() {
1430        let text = calling("f");
1431        let bytes =
1432            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1433                .expect("an object");
1434        let file = object::File::parse(&bytes[..]).expect("a readable object");
1435        let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1436        let f = found.next().expect("the function");
1437        assert!(!f.is_undefined(), "the file defines it");
1438        assert!(found.next().is_none(), "and defines it once");
1439    }
1440
1441    #[test]
1442    fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1443        let bytes = write(
1444            &calling("puts"),
1445            &Data::default(),
1446            &[],
1447            &target(),
1448            Output::default(),
1449            &Info::default(),
1450        )
1451        .expect("an object");
1452        let file = object::File::parse(&bytes[..]).expect("a readable object");
1453        let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1454        assert!(note.data().expect("no bytes").is_empty());
1455    }
1456
1457    /// What the file says it was built to have checked, byte for byte.
1458    ///
1459    /// Written against the bytes rather than against a reader, because the two lengths in the
1460    /// header count the padding after what they measure and a note whose lengths are one word out
1461    /// is one a linker drops without saying anything. What comes of that is a program the loader
1462    /// leaves the check turned off for, which is a build that looks like it worked.
1463    #[test]
1464    fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1465        let property = Property { features: Property::IBT | Property::SHSTK };
1466        let output = Output { property, ..Output::default() };
1467        let bytes =
1468            write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1469                .expect("an object");
1470        let file = object::File::parse(&bytes[..]).expect("a readable object");
1471        let note = file.section_by_name(".note.gnu.property").expect("the note");
1472        assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1473        let want: Vec<u8> = [
1474            4u32,
1475            16,
1476            5,
1477            u32::from_le_bytes(*b"GNU\0"),
1478            Property::X86_FEATURES,
1479            4,
1480            Property::IBT | Property::SHSTK,
1481            0,
1482        ]
1483        .iter()
1484        .flat_map(|word| word.to_le_bytes())
1485        .collect();
1486        assert_eq!(note.data().expect("the bytes"), &want[..]);
1487    }
1488
1489    /// And nothing at all when the file was built to have nothing checked.
1490    ///
1491    /// A note with an empty feature word and no note are the same thing to a linker, which drops
1492    /// the whole property when any input lacks it. gcc writes nothing, so a section header that
1493    /// describes nothing would be the one difference between the two compilers' objects.
1494    #[test]
1495    fn a_file_built_to_have_nothing_checked_says_nothing() {
1496        let bytes = write(
1497            &calling("puts"),
1498            &Data::default(),
1499            &[],
1500            &target(),
1501            Output::default(),
1502            &Info::default(),
1503        )
1504        .expect("an object");
1505        let file = object::File::parse(&bytes[..]).expect("a readable object");
1506        assert!(file.section_by_name(".note.gnu.property").is_none());
1507    }
1508
1509    /// Every unwind record names the function it is about, and each name goes where it is in the
1510    /// table rather than at the start of it.
1511    ///
1512    /// Written because working the offset out is the caller's job here, which is what the two text
1513    /// paths differ about, and a third caller that let it default to nothing would put every record
1514    /// in the table on the same function. Nothing else would notice: the section is the right
1515    /// length, the symbols are right, the link succeeds, and what comes of it is an unwinder that
1516    /// walks out of the wrong frame the first time something throws or a backtrace is taken.
1517    #[test]
1518    fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1519        let mut text = calling("puts");
1520        text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1521        text.bytes.resize(17, 0x90);
1522        // A shared header and two records, whose contents nothing here reads: what is being asked
1523        // is where in them each name landed.
1524        text.unwind.bytes = vec![0; 64];
1525        for (at, name) in [(32usize, "f"), (48usize, "g")] {
1526            text.unwind.relocs.push(Reloc {
1527                at,
1528                symbol: name.to_owned(),
1529                kind: Reference::Address { bytes: 8 },
1530                addend: 0,
1531                after: 0,
1532            });
1533        }
1534        let bytes =
1535            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1536                .expect("an object");
1537        let file = object::File::parse(&bytes[..]).expect("a readable object");
1538        let mut found = points_at(&file);
1539        found.sort_unstable();
1540        assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1541    }
1542
1543    /// What each record in the unwind table points at: where it is, the section it reaches, and
1544    /// how far into that section the function it is about begins.
1545    fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1546        let frames = file.section_by_name(".eh_frame").expect("the table");
1547        frames
1548            .relocations()
1549            .map(|(offset, reloc)| {
1550                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1551                    panic!("a record points at something that is not a symbol");
1552                };
1553                let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1554                assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1555                let section = symbol.section_index().expect("a section symbol is in one");
1556                let name = file.section_by_index(section).expect("a readable section");
1557                (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1558            })
1559            .collect()
1560    }
1561
1562    /// A record points at the section its function is in rather than at the function's name.
1563    ///
1564    /// Written for tamnd/rucc#1004, which was that nothing this compiler wrote could go into a
1565    /// shared library. A global name is answered at load time by whichever object defines it
1566    /// first, so the distance from a record to one of them is not a distance a static linker can
1567    /// work out, and `ld` says so and stops with advice to recompile with the flag that was
1568    /// already on the command line. A section is settled by then, which is why gcc measures to a
1569    /// local label and why this measures to the section.
1570    ///
1571    /// Both ways of splitting the text, because the offset is the part that differs: one section
1572    /// holding everything makes it the function's place in the whole text, and a section per
1573    /// function makes it whatever room a patcher was promised in front of the label.
1574    #[test]
1575    fn a_record_reaches_its_function_through_the_section_it_is_in() {
1576        let mut text = two();
1577        text.unwind.bytes = vec![0; 64];
1578        for (at, name) in [(32usize, "f"), (48usize, "g")] {
1579            text.unwind.relocs.push(Reloc {
1580                at,
1581                symbol: name.to_owned(),
1582                kind: Reference::Data,
1583                addend: 0,
1584                after: 0,
1585            });
1586        }
1587        let bytes =
1588            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1589                .expect("an object");
1590        let file = object::File::parse(&bytes[..]).expect("a readable object");
1591        let mut whole = points_at(&file);
1592        whole.sort_unstable();
1593        assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1594
1595        let sections =
1596            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1597        let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1598            .expect("an object");
1599        let file = object::File::parse(&bytes[..]).expect("a readable object");
1600        let mut split = points_at(&file);
1601        split.sort_unstable();
1602        assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1603    }
1604
1605    /// A record about a name this file does not define is refused rather than written.
1606    ///
1607    /// There is no such file today: the table is built beside the text out of the functions that
1608    /// were just compiled. It is refused rather than left to the linker because the alternative is
1609    /// the shape that was just fixed, a record measured to a name, and the writer saying what it
1610    /// was given is how that stays fixed.
1611    #[test]
1612    fn a_record_about_something_this_file_does_not_define_is_refused() {
1613        let mut text = calling("puts");
1614        text.unwind.bytes = vec![0; 64];
1615        text.unwind.relocs.push(Reloc {
1616            at: 32,
1617            symbol: "puts".to_owned(),
1618            kind: Reference::Data,
1619            addend: 0,
1620            after: 0,
1621        });
1622        let why =
1623            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1624                .expect_err("a record about a name from somewhere else");
1625        assert!(why.to_string().contains("puts"), "{why}");
1626    }
1627
1628    /// The name of the section that symbol is defined in.
1629    fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1630        let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1631        let index = symbol.section_index().expect("a section to be defined in");
1632        let section = file.section_by_index(index).expect("a readable section");
1633        section.name().expect("a named section").to_owned()
1634    }
1635
1636    /// Two functions, the second of them sixteen bytes in and calling something outside the file.
1637    fn two() -> Text {
1638        let mut text = calling("puts");
1639        // Padded to where the second one is aligned to, with the instruction that does nothing,
1640        // because the space in front of a function is reached by falling off the end of one.
1641        text.bytes.resize(16, 0x90);
1642        text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1643        text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1644        text.relocs.push(Reloc {
1645            at: 17,
1646            symbol: "puts".to_owned(),
1647            kind: Reference::Call,
1648            addend: -4,
1649            after: 0,
1650        });
1651        text
1652    }
1653
1654    /// What `-ffunction-sections` comes down to in an object file, which is the flag that makes
1655    /// `--gc-sections` able to drop anything: a linker can leave out a section nothing reaches and
1656    /// cannot leave out half of one.
1657    ///
1658    /// The empty `.text` stays, because it is the section the writer underneath opens a file with
1659    /// and gcc 16 leaves an empty one behind under the flag too.
1660    #[test]
1661    fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1662        let sections =
1663            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1664        let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1665            .expect("an object");
1666        let file = object::File::parse(&bytes[..]).expect("a readable object");
1667        assert_eq!(lives_in(&file, "f"), ".text.f");
1668        assert_eq!(lives_in(&file, "g"), ".text.g");
1669        assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1670        // Each one at nothing into its own section, and as long as it was: a function alone in a
1671        // section starts where the section does, whatever it started at when they shared one.
1672        for name in ["f", "g"] {
1673            let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1674            assert_eq!(symbol.address(), 0, "{name}");
1675            assert_eq!(symbol.size(), 6, "{name}");
1676        }
1677        let section = file.section_by_name(".text.g").expect("the second function");
1678        assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1679        // The padding between the two is gone with them, since it was there to align the second
1680        // one inside a section they shared and each section is aligned by the linker now.
1681        assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1682    }
1683
1684    /// A relocation counts from the start of whichever section its function ended up in, which is
1685    /// the arithmetic the split path has to do and the unsplit one never does.
1686    ///
1687    /// Getting it wrong is a call patched over the wrong bytes, which assembles, links, and jumps
1688    /// into the middle of an instruction at run time.
1689    #[test]
1690    fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1691        let sections =
1692            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1693        let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1694            .expect("an object");
1695        let file = object::File::parse(&bytes[..]).expect("a readable object");
1696        for name in [".text.f", ".text.g"] {
1697            let section = file.section_by_name(name).expect("a function");
1698            let (offset, _) = section.relocations().next().expect("the call in it");
1699            // One byte in either way, because the call is the first instruction of both and the
1700            // opcode is one byte in front of the address the linker fills in.
1701            assert_eq!(offset, 1, "{name}");
1702            assert_eq!(section.relocations().count(), 1, "{name}");
1703        }
1704    }
1705
1706    /// The second of `two` with a table of two cells, to its first byte and to its return.
1707    fn switching() -> Text {
1708        let mut text = two();
1709        let name = ".Lg_j0".to_owned();
1710        text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5] });
1711        text
1712    }
1713
1714    /// Where each relocation of that section is, what it is against and what it adds.
1715    fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1716        let section = file.section_by_name(section).expect("the table's section");
1717        section
1718            .relocations()
1719            .map(|(offset, reloc)| {
1720                assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1721                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1722                    panic!("a cell against something that is not a symbol");
1723                };
1724                let symbol = file.symbol_by_index(index).expect("a symbol");
1725                assert_eq!(symbol.kind(), SymbolKind::Section);
1726                let at = symbol.section_index().expect("a section symbol is in one");
1727                let name = file.section_by_index(at).expect("a section").name().expect("a name");
1728                (offset, name.to_owned(), reloc.addend())
1729            })
1730            .collect()
1731    }
1732
1733    #[test]
1734    fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1735        // And the code reaches it by the name the table was given, which here is the second of the
1736        // two references in `two`.
1737        let mut text = switching();
1738        text.relocs[1].symbol = ".Lg_j0".to_owned();
1739        text.relocs[1].kind = Reference::Data;
1740        let bytes =
1741            write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1742                .expect("an object");
1743        let file = object::File::parse(&bytes[..]).expect("a readable object");
1744        let rodata = file.section_by_name(".rodata").expect("the table's section");
1745        assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
1746        assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
1747        assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
1748        let (at, reloc) = file
1749            .section_by_name(".text")
1750            .expect("the code")
1751            .relocations()
1752            .find(|(at, _)| *at == 17)
1753            .expect("the reference to the table");
1754        assert_eq!((at, reloc.addend()), (17, -4));
1755        let object::RelocationTarget::Symbol(index) = reloc.target() else {
1756            panic!("a reference against something that is not a symbol");
1757        };
1758        let symbol = file.symbol_by_index(index).expect("a symbol");
1759        assert_eq!(symbol.section_index(), Some(rodata.index()));
1760        assert_eq!(symbol.kind(), SymbolKind::Section);
1761        // `g` starts sixteen bytes into `.text`, and each cell is its block's place in the text
1762        // and its own place in the table, so that the linker's answer is block less table.
1763        assert_eq!(
1764            cells(&file, ".rodata"),
1765            [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
1766        );
1767    }
1768
1769    #[test]
1770    fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
1771        let sections =
1772            Output { sections: Sections { functions: true, data: true }, ..Output::default() };
1773        let bytes =
1774            write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
1775                .expect("an object");
1776        let file = object::File::parse(&bytes[..]).expect("a readable object");
1777        // Against the function's own section now, where it starts at nothing.
1778        assert_eq!(
1779            cells(&file, ".rodata.g"),
1780            [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
1781        );
1782    }
1783
1784    #[test]
1785    fn a_jump_table_outside_the_code_is_refused_on_windows() {
1786        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1787        let written = write(
1788            &switching(),
1789            &Data::default(),
1790            &[],
1791            &target,
1792            Output::default(),
1793            &Info::default(),
1794        );
1795        assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
1796    }
1797
1798    /// One variable of four bytes, in whichever section its own answer puts it.
1799    fn variable(name: &str, place: Place) -> Object {
1800        Object {
1801            name: name.to_owned(),
1802            bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1803            size: 4,
1804            align: 4,
1805            place,
1806            binding: Binding::Global,
1807            visibility: Visibility::Default,
1808            relocs: Vec::new(),
1809        }
1810    }
1811
1812    /// Two labels in `f` and an image holding the distance between them each way round.
1813    fn measured() -> (Text, Data) {
1814        let mut text = calling("puts");
1815        text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1816        text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1817        let mut table = variable("table", Place::ReadOnly);
1818        table.bytes = vec![0; 8];
1819        table.size = 8;
1820        let apart = |at, to: &str, from: &str| Apart {
1821            object: 0,
1822            at,
1823            to: to.to_owned(),
1824            from: from.to_owned(),
1825            addend: 0,
1826            bytes: 4,
1827        };
1828        let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1829        (text, Data { apart, weak: Vec::new(), objects: vec![table] })
1830    }
1831
1832    #[test]
1833    fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1834        let (text, data) = measured();
1835        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1836            .expect("an object");
1837        let file = object::File::parse(&bytes[..]).expect("a readable object");
1838        let section = file.section_by_name(".rodata").expect("a read only section");
1839        assert_eq!(section.relocations().count(), 0);
1840        let image = section.data().expect("the image");
1841        assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1842    }
1843
1844    #[test]
1845    fn a_distance_between_labels_in_two_sections_is_refused() {
1846        // `.L1` moves to a second function, which `-ffunction-sections` puts in a section of its
1847        // own, and then no number is the distance.
1848        let (mut text, data) = measured();
1849        text.bytes.resize(22, 0x90);
1850        text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1851        text.labels[1].at = 17;
1852        let output =
1853            Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1854        let refused = write(&text, &data, &[], &target(), output, &Info::default());
1855        assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1856    }
1857
1858    /// A file of that one variable and nothing else.
1859    fn holding(object: Object) -> Vec<u8> {
1860        let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
1861        write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1862            .expect("an object")
1863    }
1864
1865    #[test]
1866    fn what_a_variable_is_decides_which_section_it_goes_in() {
1867        for (place, wanted) in [
1868            (Place::Written, ".data"),
1869            (Place::ReadOnly, ".rodata"),
1870            (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1871            (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1872            (Place::Zero, ".bss"),
1873            (Place::Thread { zero: false }, ".tdata"),
1874            (Place::Thread { zero: true }, ".tbss"),
1875            (Place::Named(".init_array".to_owned()), ".init_array"),
1876        ] {
1877            let bytes = holding(variable("x", place.clone()));
1878            let file = object::File::parse(&bytes[..]).expect("a readable object");
1879            let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1880            assert_eq!(section.size(), 4, "{place:?}");
1881            // The zero filled one is as long as it says and carries none of it, which is the
1882            // whole reason the section exists.
1883            let carried = section.data().expect("the bytes").len();
1884            assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1885        }
1886    }
1887
1888    /// The section is half of it and the symbol is the other half.
1889    ///
1890    /// A linker checks a relocation against the kind of the symbol it names, so a variable that is
1891    /// in `.tdata` and is an ordinary data symbol is one an ordinary reference resolves to an
1892    /// address that belongs to no thread. `STT_TLS` is what makes that reference an error instead.
1893    #[test]
1894    fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1895        for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1896            let bytes = holding(variable("counter", place.clone()));
1897            let file = object::File::parse(&bytes[..]).expect("a readable object");
1898            let symbol = file
1899                .symbols()
1900                .find(|symbol| symbol.name() == Ok("counter"))
1901                .unwrap_or_else(|| panic!("{place:?}"));
1902            assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1903        }
1904    }
1905
1906    /// The section type a startup list carries, which is what makes the CRT call what is in it.
1907    ///
1908    /// A section of the ordinary type with the right name is gathered by the linker in the same run
1909    /// and called by nobody, so the type is the whole of what this is about. The numbered name is
1910    /// the same kind of section as the plain one: the number is there so that the linker sorts it.
1911    #[test]
1912    fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1913        for (name, wanted) in [
1914            (".init_array", elf::SHT_INIT_ARRAY),
1915            (".init_array.00101", elf::SHT_INIT_ARRAY),
1916            (".fini_array", elf::SHT_FINI_ARRAY),
1917            (".preinit_array", elf::SHT_PREINIT_ARRAY),
1918            (".init_arrays", elf::SHT_PROGBITS),
1919        ] {
1920            let bytes = holding(variable("x", Place::Named(name.to_owned())));
1921            let file = object::File::parse(&bytes[..]).expect("a readable object");
1922            let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1923            let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1924                panic!("{name} is not an elf section");
1925            };
1926            assert_eq!(sh_type, wanted, "{name}");
1927            assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1928        }
1929    }
1930
1931    /// Two variables the program put one section name on, which belong in one section.
1932    ///
1933    /// A file with ten constructors in it would otherwise carry ten section headers describing eight
1934    /// bytes each, and the order the entries run in would be the order the linker happened to put
1935    /// the headers in rather than the order they were written.
1936    #[test]
1937    fn two_variables_in_one_named_section_share_it() {
1938        let objects = vec![
1939            variable("x", Place::Named(".init_array".to_owned())),
1940            variable("y", Place::Named(".init_array".to_owned())),
1941        ];
1942        let data = Data { apart: Vec::new(), weak: Vec::new(), objects };
1943        let bytes =
1944            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1945                .expect("an object");
1946        let file = object::File::parse(&bytes[..]).expect("a readable object");
1947        let named: Vec<_> =
1948            file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1949        assert_eq!(named.len(), 1);
1950        assert_eq!(named[0].size(), 8);
1951    }
1952
1953    /// What `-fdata-sections` comes down to in an object file: the section a variable would have
1954    /// shared, with its own name after it. The names are gcc 16's, checked against it on a Linux
1955    /// host, and the part in front of the dot is what a linker script and `--gc-sections` match on.
1956    #[test]
1957    fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1958        let sections =
1959            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1960        for (place, wanted) in [
1961            (Place::Written, ".data.x"),
1962            (Place::ReadOnly, ".rodata.x"),
1963            (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1964            (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1965            (Place::Zero, ".bss.x"),
1966            (Place::Thread { zero: false }, ".tdata.x"),
1967            (Place::Thread { zero: true }, ".tbss.x"),
1968        ] {
1969            let data = Data {
1970                apart: Vec::new(),
1971                weak: Vec::new(),
1972                objects: vec![variable("x", place.clone())],
1973            };
1974            let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1975                .expect("object");
1976            let file = object::File::parse(&bytes[..]).expect("a readable object");
1977            assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1978            let section = file.section_by_name(wanted).expect("the section it named");
1979            assert_eq!(section.size(), 4, "{place:?}");
1980            // Which page it lands in is what the section it came out of decided, and splitting
1981            // must not quietly change it: the zero filled one still carries none of its bytes.
1982            let carried = section.data().expect("the bytes").len();
1983            assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1984        }
1985    }
1986
1987    /// The two kinds of variable the flag leaves alone. A tentative definition is a request to the
1988    /// linker for that much zeroed space rather than an image, so there is no section to split off,
1989    /// and one the program named has the answer the source gave, which a flag must not overrule.
1990    #[test]
1991    fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1992        let sections =
1993            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1994        let named = Place::Named(".init_array".to_owned());
1995        let objects = vec![variable("m", Place::Merged), variable("n", named)];
1996        let bytes = write(
1997            &Text::default(),
1998            &Data { apart: Vec::new(), weak: Vec::new(), objects },
1999            &[],
2000            &target(),
2001            sections,
2002            &Info::default(),
2003        )
2004        .expect("object");
2005        let file = object::File::parse(&bytes[..]).expect("a readable object");
2006        let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2007        assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2008        assert_eq!(lives_in(&file, "n"), ".init_array");
2009        assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2010    }
2011
2012    /// A relocation in a variable's image counts from the start of the section it ended up in, the
2013    /// same question the split text has to answer and a shorter answer: a variable alone in a
2014    /// section starts where the section does.
2015    #[test]
2016    fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2017        let sections =
2018            Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2019        let pointer = Object {
2020            bytes: vec![0; 8],
2021            size: 8,
2022            align: 8,
2023            relocs: vec![Reloc {
2024                at: 0,
2025                symbol: "y".to_owned(),
2026                kind: Reference::Address { bytes: 8 },
2027                addend: 0,
2028                after: 0,
2029            }],
2030            ..variable("p", Place::Written)
2031        };
2032        let objects = vec![variable("first", Place::Written), pointer];
2033        let bytes = write(
2034            &Text::default(),
2035            &Data { apart: Vec::new(), weak: Vec::new(), objects },
2036            &[],
2037            &target(),
2038            sections,
2039            &Info::default(),
2040        )
2041        .expect("object");
2042        let file = object::File::parse(&bytes[..]).expect("a readable object");
2043        let section = file.section_by_name(".data.p").expect("the pointer's own section");
2044        let (offset, reloc) = section.relocations().next().expect("one relocation");
2045        // Nothing rather than the eight it would be if the variable in front of it were still
2046        // counted, which is what a section of its own means.
2047        assert_eq!(offset, 0);
2048        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2049    }
2050
2051    /// Two variables that want `.data.rel.ro.local` end up in one section, not two of one name.
2052    ///
2053    /// The writer has no name of its own for that section, so it is added by hand, and asking for
2054    /// it again makes a second section rather than handing back the first. SQLite has enough const
2055    /// tables of function pointers in it to turn that into eighty odd sections in one object, each
2056    /// with its own relocation section beside it, which is a pile of section headers describing
2057    /// eight bytes apiece.
2058    #[test]
2059    fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2060        let place = Place::RelocReadOnly { local: true };
2061        let data = Data {
2062            apart: Vec::new(),
2063            weak: Vec::new(),
2064            objects: vec![variable("first", place.clone()), variable("second", place)],
2065        };
2066        let bytes =
2067            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2068                .expect("an object");
2069        let file = object::File::parse(&bytes[..]).expect("a readable object");
2070        let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2071        assert_eq!(named, 1, "one section holding both, not one each");
2072    }
2073
2074    #[test]
2075    fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2076        let mut data = Data {
2077            apart: Vec::new(),
2078            weak: Vec::new(),
2079            objects: vec![variable("first", Place::Written)],
2080        };
2081        data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2082        let bytes =
2083            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2084                .expect("an object");
2085        let file = object::File::parse(&bytes[..]).expect("a readable object");
2086        let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2087        assert_eq!(second.kind(), SymbolKind::Data);
2088        assert_eq!(second.size(), 4);
2089        // Sixteen rather than four, because the second one asked for sixteen and the first one
2090        // had already used four. Getting this wrong is a variable at an address it said it would
2091        // never be at, which nothing downstream would notice until an aligned load faulted.
2092        assert_eq!(second.address(), 16);
2093    }
2094
2095    #[test]
2096    fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2097        for (binding, global, weak) in [
2098            (Binding::Global, true, false),
2099            (Binding::Local, false, false),
2100            (Binding::Weak, true, true),
2101        ] {
2102            let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2103            let file = object::File::parse(&bytes[..]).expect("a readable object");
2104            let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2105            assert_eq!(x.is_global(), global, "{binding:?}");
2106            assert_eq!(x.is_weak(), weak, "{binding:?}");
2107        }
2108    }
2109
2110    #[test]
2111    fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2112        let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2113        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2114        let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2115        assert!(x.is_common(), "the linker merges every definition of this name into one");
2116        assert_eq!(x.size(), 4);
2117        // What a common symbol records where an ordinary one records its address is what it wants
2118        // to be aligned to, because it has no address yet. The reader deliberately answers nothing
2119        // when asked for the address of one, so this is the field itself.
2120        assert_eq!(x.address(), 0);
2121        assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2122    }
2123
2124    #[test]
2125    fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2126        let object = Object {
2127            bytes: vec![0; 8],
2128            size: 8,
2129            align: 8,
2130            relocs: vec![Reloc {
2131                at: 0,
2132                symbol: "y".to_owned(),
2133                kind: Reference::Address { bytes: 8 },
2134                addend: 16,
2135                after: 0,
2136            }],
2137            ..variable("p", Place::Written)
2138        };
2139        let bytes = holding(object);
2140        let file = object::File::parse(&bytes[..]).expect("a readable object");
2141        let section = file.section_by_name(".data").expect("a data section");
2142        let (offset, reloc) = section.relocations().next().expect("one relocation");
2143        assert_eq!(offset, 0);
2144        assert_eq!(reloc.addend(), 16);
2145        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2146        let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2147        assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2148    }
2149
2150    /// A name a declaration wrote `weak` on is undefined and may stay that way.
2151    ///
2152    /// The difference between this and the case above is one bit and the whole of what a link does
2153    /// about it: an ordinary undefined symbol is a name the linker has to find, and a weak one is a
2154    /// name it may fail to find, in which case every reference reads a zero address. That is what
2155    /// lets a library offer a hook a profiler may fill in, which is tamnd/rucc#1414.
2156    #[test]
2157    fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2158        let mut text = Text::default();
2159        text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2160        text.bytes.resize(8, 0x90);
2161        text.relocs.push(Reloc {
2162            at: 1,
2163            symbol: "hook".to_owned(),
2164            kind: Reference::Call,
2165            addend: -4,
2166            after: 0,
2167        });
2168        let data = Data {
2169            apart: Vec::new(),
2170            weak: vec!["hook".to_owned(), "never_called".to_owned()],
2171            objects: vec![],
2172        };
2173        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2174            .expect("an object");
2175        let file = object::File::parse(&bytes[..]).expect("a readable object");
2176
2177        let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2178        assert!(hook.is_undefined(), "nothing here defines it");
2179        assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2180
2181        // And one nothing refers to is still written down, because the listing writes a directive
2182        // for it and the two paths have to put the same entries in. A linker has nothing to do
2183        // about an undefined weak symbol no relocation names.
2184        let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2185        assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2186    }
2187
2188    /// A name this file reads through the thread pointer is undefined and is still known to be
2189    /// thread-local.
2190    ///
2191    /// The other undefined names here are written with no type at all, because a name this file does
2192    /// not define is a name this file has nothing to say about. A thread-local one is different in
2193    /// the one way that counts: a reference to it is satisfied by an offset into a block rather than
2194    /// by an address, so the linker has to know which of the two is wanted before it has found the
2195    /// definition, and rather than guess it refuses a link where one file says `STT_TLS` about a name
2196    /// and another does not. Writing the type is not extra information, it is the same information
2197    /// the relocation already carried, said where the linker looks for it.
2198    ///
2199    /// That is tamnd/rucc#1461. libmpfr defines `__gmpfr_flags` in `exceptions.c` and reads it in a
2200    /// hundred other files, and the link stopped at the first reader with `TLS definition in
2201    /// exceptions.o section .tdata mismatches non-TLS reference in add.o`.
2202    #[test]
2203    fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2204        let mut text = Text::default();
2205        text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2206        text.bytes.resize(16, 0x90);
2207        text.relocs.push(Reloc {
2208            at: 3,
2209            symbol: "flags".to_owned(),
2210            kind: Reference::Thread,
2211            addend: -4,
2212            after: 0,
2213        });
2214        // One of them reached the ordinary way, so that what the type says is the relocation's doing
2215        // and not something every undefined name here would have got.
2216        text.relocs.push(Reloc {
2217            at: 10,
2218            symbol: "shared".to_owned(),
2219            kind: Reference::Got,
2220            addend: -4,
2221            after: 0,
2222        });
2223        let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![] };
2224        let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2225            .expect("an object");
2226        let file = object::File::parse(&bytes[..]).expect("a readable object");
2227
2228        let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2229        assert!(flags.is_undefined(), "nothing here defines it");
2230        assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2231
2232        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2233        assert!(shared.is_undefined(), "nothing here defines this one either");
2234        assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2235    }
2236
2237    /// Not a rewording of the case above: what is checked is the arithmetic between the two.
2238    #[test]
2239    fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2240        let mut data = Data {
2241            apart: Vec::new(),
2242            weak: Vec::new(),
2243            objects: vec![variable("first", Place::Written)],
2244        };
2245        data.objects.push(Object {
2246            bytes: vec![0; 16],
2247            size: 16,
2248            align: 8,
2249            relocs: vec![Reloc {
2250                at: 8,
2251                symbol: "y".to_owned(),
2252                kind: Reference::Address { bytes: 8 },
2253                addend: 0,
2254                after: 0,
2255            }],
2256            ..variable("second", Place::Written)
2257        });
2258        let bytes =
2259            write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2260                .expect("an object");
2261        let file = object::File::parse(&bytes[..]).expect("a readable object");
2262        let section = file.section_by_name(".data").expect("a data section");
2263        let (offset, _) = section.relocations().next().expect("one relocation");
2264        // Eight into the second image, which starts eight in because the first one is four long
2265        // and the second is eight aligned.
2266        assert_eq!(offset, 16);
2267    }
2268
2269    #[test]
2270    fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2271        let data = Data {
2272            apart: Vec::new(),
2273            weak: Vec::new(),
2274            objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2275        };
2276        let aliases = [Alias {
2277            name: "b".to_owned(),
2278            target: "a".to_owned(),
2279            binding: Binding::Global,
2280            visibility: Visibility::Default,
2281        }];
2282        let bytes = write(
2283            &Text::default(),
2284            &data,
2285            &aliases,
2286            &target(),
2287            Output::default(),
2288            &Info::default(),
2289        )
2290        .expect("an object");
2291        let file = object::File::parse(&bytes[..]).expect("a readable object");
2292        let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2293        let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2294        assert_eq!(b.address(), a.address(), "the same place");
2295        assert_eq!(b.size(), a.size());
2296        assert_eq!(b.section_index(), a.section_index());
2297        // The binding is the one thing the second name does not take from the first, which is
2298        // what `extern int b __attribute__((alias("a")))` on a `static a` asks for.
2299        assert!(a.is_local(), "the target was written `static`");
2300        assert!(b.is_global(), "and the name given to it was not");
2301        // Four bytes of image and not eight, since an alias is a name and not a copy.
2302        assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2303    }
2304
2305    #[test]
2306    fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2307        let text = calling("puts");
2308        let aliases = [Alias {
2309            name: "g".to_owned(),
2310            target: "f".to_owned(),
2311            binding: Binding::Weak,
2312            visibility: Visibility::Default,
2313        }];
2314        let bytes = write(
2315            &text,
2316            &Data::default(),
2317            &aliases,
2318            &target(),
2319            Output::default(),
2320            &Info::default(),
2321        )
2322        .expect("an object");
2323        let file = object::File::parse(&bytes[..]).expect("a readable object");
2324        let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2325        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2326        assert_eq!(g.address(), f.address());
2327        assert_eq!(g.size(), f.size());
2328        assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2329        assert!(g.is_weak(), "so that a program may define the name itself instead");
2330    }
2331
2332    /// The front end is what reports this as a program's mistake, so one arriving here is a bug
2333    /// in this compiler and is said so rather than written as an undefined symbol.
2334    #[test]
2335    fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2336        let aliases = [Alias {
2337            name: "b".to_owned(),
2338            target: "a".to_owned(),
2339            binding: Binding::Global,
2340            visibility: Visibility::Default,
2341        }];
2342        let error = write(
2343            &Text::default(),
2344            &Data::default(),
2345            &aliases,
2346            &target(),
2347            Output::default(),
2348            &Info::default(),
2349        )
2350        .expect_err("nothing to point at");
2351        assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2352    }
2353
2354    #[test]
2355    fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2356        let text = calling("puts");
2357        for triple in [
2358            Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2359            Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2360        ] {
2361            let error = write(
2362                &text,
2363                &Data::default(),
2364                &[],
2365                &TargetInfo::new(triple),
2366                Output::default(),
2367                &Info::default(),
2368            )
2369            .expect_err("no writer");
2370            assert!(matches!(error, Error::Format { .. }), "{error:?}");
2371        }
2372    }
2373
2374    /// What the archive's symbol index is built from is what the linker can find in the member.
2375    ///
2376    /// Written against the object rather than against the list, because the two agreeing is the
2377    /// whole point: a list that says more than the file does is an archive that promises a
2378    /// definition it does not have, and a list that says less is a member nothing pulls out.
2379    #[test]
2380    fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2381        let mut text = calling("puts");
2382        text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2383        text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2384        text.bytes.resize(33, 0x90);
2385        let data = Data {
2386            apart: Vec::new(),
2387            weak: Vec::new(),
2388            objects: vec![variable("seen", Place::Written), {
2389                let mut quiet = variable("quiet", Place::Zero);
2390                quiet.binding = Binding::Local;
2391                quiet
2392            }],
2393        };
2394        let aliases = [Alias {
2395            name: "second".to_owned(),
2396            target: "f".to_owned(),
2397            binding: Binding::Global,
2398            visibility: Visibility::Default,
2399        }];
2400
2401        let names = defines(&text, &data, &aliases, &target()).expect("a list");
2402        assert_eq!(names, ["f", "shared", "seen", "second"]);
2403
2404        let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2405            .expect("an object");
2406        let file = object::File::parse(&bytes[..]).expect("a readable object");
2407        let found: Vec<String> = file
2408            .symbols()
2409            .filter(|symbol| symbol.is_global() && symbol.is_definition())
2410            .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2411            .collect();
2412        let mut sorted = names.clone();
2413        sorted.sort();
2414        let mut theirs = found;
2415        theirs.sort();
2416        assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2417    }
2418
2419    /// A windows x86-64 target, which is the other format this writes.
2420    fn windows() -> TargetInfo {
2421        TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2422    }
2423
2424    /// What the four bytes a relocation covers hold, which is where COFF keeps its addend.
2425    fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2426        let file = object::File::parse(bytes).expect("a readable object");
2427        let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2428        i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2429    }
2430
2431    #[test]
2432    fn a_windows_target_is_written_rather_than_refused() {
2433        let text = calling("puts");
2434        let bytes =
2435            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2436                .expect("an object");
2437        let file = object::File::parse(&bytes[..]).expect("a readable object");
2438        assert_eq!(file.format(), BinaryFormat::Coff);
2439        let section = file.section_by_name(".text").expect("a text section");
2440        assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2441        let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2442        assert!(names.contains(&"f"), "{names:?}");
2443        assert!(names.contains(&"puts"), "{names:?}");
2444    }
2445
2446    /// The whole reason a relocation carries where the instruction ended as well as the addend.
2447    ///
2448    /// A call ends at the four bytes the linker writes over, and a store of a constant through an
2449    /// address counted from the instruction pointer has the constant after them, and ELF tells the
2450    /// two apart by the addend alone. COFF cannot: it says how far the end is in the relocation type
2451    /// and works the addend out from that, so the same four bytes come out of two different types
2452    /// and both have to end up meaning the same distance.
2453    #[test]
2454    fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2455        for (after, typ) in [
2456            (0, pe::IMAGE_REL_AMD64_REL32),
2457            (1, pe::IMAGE_REL_AMD64_REL32_1),
2458            (4, pe::IMAGE_REL_AMD64_REL32_4),
2459            (5, pe::IMAGE_REL_AMD64_REL32_5),
2460        ] {
2461            let mut text = calling("puts");
2462            // The same distance every time, said the way ELF says it: from where the four bytes
2463            // start, with everything else folded in.
2464            text.relocs[0].addend = -4 - i64::from(after);
2465            text.relocs[0].after = after;
2466            text.bytes.resize(6 + after as usize, 0x90);
2467            text.funcs[0].len = text.bytes.len();
2468            let bytes = write(
2469                &text,
2470                &Data::default(),
2471                &[],
2472                &windows(),
2473                Output::default(),
2474                &Info::default(),
2475            )
2476            .expect("an object");
2477            let file = object::File::parse(&bytes[..]).expect("a readable object");
2478            let section = file.section_by_name(".text").expect("a text section");
2479            let (_, reloc) = section.relocations().next().expect("the relocation");
2480            assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2481            // And the bytes come out holding nothing, because the distance the instruction wants
2482            // and the distance the type already says are the same one.
2483            assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2484        }
2485    }
2486
2487    /// The addend a COFF object keeps is in the bytes rather than in the relocation, so the number
2488    /// the caller handed over has to survive the trip through the type.
2489    #[test]
2490    fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2491        let mut text = calling("puts");
2492        text.relocs[0].addend = 12;
2493        let bytes =
2494            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2495                .expect("an object");
2496        assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2497    }
2498
2499    #[test]
2500    fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2501        let object = Object {
2502            bytes: vec![0; 8],
2503            size: 8,
2504            align: 8,
2505            relocs: vec![Reloc {
2506                at: 0,
2507                symbol: "y".to_owned(),
2508                kind: Reference::Address { bytes: 8 },
2509                addend: 0,
2510                after: 0,
2511            }],
2512            ..variable("p", Place::Written)
2513        };
2514        let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
2515        let bytes =
2516            write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2517                .expect("an object");
2518        let file = object::File::parse(&bytes[..]).expect("a readable object");
2519        let section = file.section_by_name(".data").expect("a data section");
2520        let (_, reloc) = section.relocations().next().expect("the relocation");
2521        let typ = pe::IMAGE_REL_AMD64_ADDR64;
2522        assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2523    }
2524
2525    /// `.data.rel.ro` is an ELF answer to a problem this format solves elsewhere, so both halves of
2526    /// it land in ordinary read only data, which is where the platform's own linker puts them.
2527    #[test]
2528    fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2529        for local in [false, true] {
2530            let data = Data {
2531                apart: Vec::new(),
2532                weak: Vec::new(),
2533                objects: vec![variable("p", Place::RelocReadOnly { local })],
2534            };
2535            let bytes = write(
2536                &Text::default(),
2537                &data,
2538                &[],
2539                &windows(),
2540                Output::default(),
2541                &Info::default(),
2542            )
2543            .expect("an object");
2544            let file = object::File::parse(&bytes[..]).expect("a readable object");
2545            assert!(file.section_by_name(".rdata").is_some(), "{local}");
2546            assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2547        }
2548    }
2549
2550    /// No marker and no note, because a PE image says both of those things in the header of the
2551    /// finished image rather than in each of its inputs.
2552    #[test]
2553    fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2554        let text = calling("puts");
2555        let output = Output { property: Property { features: 3 }, ..Output::default() };
2556        let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2557            .expect("an object");
2558        let file = object::File::parse(&bytes[..]).expect("a readable object");
2559        assert!(file.section_by_name(".note.GNU-stack").is_none());
2560        assert!(file.section_by_name(".note.gnu.property").is_none());
2561    }
2562
2563    /// Each of these is something this format has no way to write, and writing the nearest thing
2564    /// would be worse than refusing: a zeroed thread-local variable written as ordinary zeroed
2565    /// space is one copy where the program asked for one per thread, and a constructor list under
2566    /// a name nothing gathers is a program whose constructors never run.
2567    #[test]
2568    fn what_this_format_cannot_say_is_refused_by_name() {
2569        let ordinary = Text::default();
2570        let empty = Data::default();
2571
2572        let mut thread = Data::default();
2573        thread.objects.push(variable("t", Place::Thread { zero: true }));
2574
2575        let mut gathered = Data::default();
2576        gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2577
2578        let mut table = calling("puts");
2579        table.relocs[0].kind = Reference::Got;
2580
2581        let mut room = calling("puts");
2582        room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2583
2584        let cases: [(&str, &Text, &Data); 4] = [
2585            ("thread-local", &ordinary, &thread),
2586            ("startup", &ordinary, &gathered),
2587            ("table", &table, &empty),
2588            ("patcher", &room, &empty),
2589        ];
2590        for (what, text, data) in cases {
2591            let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2592                .expect_err("something this format cannot write");
2593            assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2594        }
2595    }
2596
2597    /// A thread-local variable with an image goes in `.tls$`, which is the section every thread
2598    /// gets a copy of.
2599    #[test]
2600    fn a_thread_local_variable_goes_in_the_tls_section() {
2601        let mut thread = Data::default();
2602        thread.objects.push(variable("t", Place::Thread { zero: false }));
2603        let bytes =
2604            write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
2605                .expect("an object");
2606        let file = object::File::parse(&bytes[..]).expect("a readable object");
2607        assert!(file.section_by_name(".tls$").is_some());
2608    }
2609
2610    /// A visibility is not refused, because there is nothing to refuse: it is a fact about a dynamic
2611    /// symbol table and a COFF symbol has nowhere to keep one, which is what gcc does on the
2612    /// platform as well.
2613    #[test]
2614    fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2615        let mut text = calling("puts");
2616        text.funcs[0].visibility = Visibility::Hidden;
2617        let bytes =
2618            write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2619                .expect("an object");
2620        let file = object::File::parse(&bytes[..]).expect("a readable object");
2621        let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2622        assert!(symbol.is_global(), "a name others may use either way");
2623    }
2624
2625    #[test]
2626    fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2627        let text = calling("puts");
2628        let data = Data {
2629            apart: Vec::new(),
2630            weak: Vec::new(),
2631            objects: vec![variable("shared", Place::Written)],
2632        };
2633        let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2634        assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2635    }
2636
2637    /// The same refusal the writer gives, for the reason the function says: an undecorated name is
2638    /// the wrong answer for a format whose symbols carry an underscore, and a wrong index entry is
2639    /// worse than no archive.
2640    #[test]
2641    fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2642        let text = calling("puts");
2643        for triple in [
2644            Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2645            Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2646        ] {
2647            let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2648                .expect_err("no writer");
2649            assert!(matches!(error, Error::Format { .. }), "{error:?}");
2650        }
2651    }
2652}