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