Skip to main content

rucc_object/
elf.rs

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