Skip to main content

rucc_object/
file.rs

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