Skip to main content

rucc_object/
file.rs

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