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