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::{Object as Writer, Relocation, StandardSection, Symbol, SymbolSection};
29use object::{
30    Architecture, BinaryFormat, Endianness, RelocationFlags, SectionKind, SymbolFlags, SymbolKind,
31    SymbolScope, elf,
32};
33use rucc_target::{ObjectFormat, TargetInfo};
34use rucc_tuple::Arch;
35
36use crate::section::{Alias, Binding, Data, Object, Place, Reference, Reloc, Text};
37
38/// Why an object file could not be written.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum Error {
41    /// A machine or a platform this does not write objects for.
42    Format {
43        /// The triple that was asked for.
44        triple: String,
45    },
46    /// The writer refused something it was given, which is a bug here rather than in a program.
47    Refused {
48        /// What it said, already formatted.
49        why: String,
50    },
51}
52
53impl std::fmt::Display for Error {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        match self {
56            Error::Format { triple } => {
57                write!(f, "there is no object writer for {triple} in this compiler yet")
58            }
59            Error::Refused { why } => {
60                write!(f, "the object writer refused what it was given: {why}")
61            }
62        }
63    }
64}
65
66impl std::error::Error for Error {}
67
68/// One text section and the variables beside it, as a relocatable ELF object.
69///
70/// # Errors
71///
72/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for
73/// anything the writer underneath objected to, which would be a bug here. An alias whose target
74/// this file does not define is refused the same way, since the front end is what reports that as
75/// a program's mistake and one reaching here means it did not. See [`Error`].
76pub fn write(
77    text: &Text,
78    data: &Data,
79    aliases: &[Alias],
80    target: &TargetInfo,
81) -> Result<Vec<u8>, Error> {
82    if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
83        return Err(Error::Format { triple: target.tuple.to_string() });
84    }
85    let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
86    let section = obj.section_id(StandardSection::Text);
87    obj.append_section_data(section, &text.bytes, u64::from(text.align));
88
89    // Every function defined here, then every variable, then every name either of them wanted that
90    // is not. A name is looked up rather than added twice, because two symbols with one name is
91    // not a file a linker accepts.
92    let mut symbols = std::collections::BTreeMap::new();
93    for func in &text.funcs {
94        let id = obj.add_symbol(Symbol {
95            name: func.name.clone().into_bytes(),
96            value: func.start as u64,
97            size: func.len as u64,
98            kind: SymbolKind::Text,
99            scope: scope_of(func.binding),
100            weak: func.binding == Binding::Weak,
101            section: SymbolSection::Section(section),
102            flags: SymbolFlags::None,
103        });
104        symbols.insert(func.name.clone(), id);
105    }
106
107    // Where each variable's image landed in the section it went into, kept because a relocation in
108    // an image counts from the start of the image and one in a file counts from the start of the
109    // section. A variable that is not in a section has no entry, since nothing in a merged one can
110    // hold a relocation: the linker is being asked for zeroed space rather than for an image.
111    let mut placed = Vec::with_capacity(data.objects.len());
112    // The one section the writer has no name of its own for, remembered so that every variable that
113    // wants it lands in the same one. The rest come back from `section_id`, which already answers
114    // with the section it made the first time it was asked.
115    let mut local = None;
116    for object in &data.objects {
117        let (section, offset) = put(&mut obj, object, &mut local);
118        let id = obj.add_symbol(Symbol {
119            name: object.name.clone().into_bytes(),
120            // A common symbol says what it wants rather than where it is, and what it wants is
121            // recorded where an ordinary symbol records its address.
122            value: if object.place == Place::Merged { object.align } else { offset },
123            size: object.size,
124            kind: SymbolKind::Data,
125            scope: scope_of(object.binding),
126            weak: object.binding == Binding::Weak,
127            section,
128            flags: SymbolFlags::None,
129        });
130        symbols.insert(object.name.clone(), id);
131        placed.push((section.id(), offset));
132    }
133
134    // A second name for something already added, which is where the alias's own binding is the
135    // only thing it does not take from what it points at: the target of one may be a `static` and
136    // the alias of it may not be. Before the loop below rather than after it, because a reference
137    // to the new name is a reference to something this file defines and would otherwise be added
138    // as a name this file wants from somewhere else.
139    for alias in aliases {
140        let Some(&id) = symbols.get(&alias.target) else {
141            let why =
142                format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
143            return Err(Error::Refused { why });
144        };
145        let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
146        let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
147        let id = obj.add_symbol(Symbol {
148            name: alias.name.clone().into_bytes(),
149            value,
150            size,
151            kind,
152            scope: scope_of(alias.binding),
153            weak: alias.binding == Binding::Weak,
154            section,
155            flags: SymbolFlags::None,
156        });
157        symbols.insert(alias.name.clone(), id);
158    }
159
160    let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
161    for reloc in wanted {
162        if symbols.contains_key(&reloc.symbol) {
163            continue;
164        }
165        let id = obj.add_symbol(Symbol {
166            name: reloc.symbol.clone().into_bytes(),
167            value: 0,
168            size: 0,
169            // What kind of thing an undefined name is is not known here and does not have to be:
170            // a linker resolves an undefined symbol by its name, and the type of one that is not
171            // defined anywhere in this file is nothing this file can say.
172            kind: SymbolKind::Unknown,
173            scope: SymbolScope::Dynamic,
174            weak: false,
175            section: SymbolSection::Undefined,
176            flags: SymbolFlags::None,
177        });
178        symbols.insert(reloc.symbol.clone(), id);
179    }
180
181    for reloc in &text.relocs {
182        add(&mut obj, section, 0, reloc, &symbols)?;
183    }
184    for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
185        let Some(section) = section else { continue };
186        for reloc in &object.relocs {
187            add(&mut obj, section, offset, reloc, &symbols)?;
188        }
189    }
190
191    // Written as an empty note rather than left out, because a linker that does not find it in
192    // every input marks the stack executable.
193    obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
194
195    obj.write().map_err(|why| Error::Refused { why: why.to_string() })
196}
197
198/// One variable's image into the section it belongs in, and where in that section it landed.
199///
200/// A zero filled variable takes as many bytes of the file as it is long on the way in and none on
201/// the way out, which is the whole point of the section it goes in. A merged one goes in no section
202/// at all: the linker is being asked for that much zeroed space under that name, and where it ends
203/// up is the linker's answer rather than this file's.
204fn put(
205    obj: &mut Writer<'_>,
206    object: &Object,
207    local: &mut Option<object::write::SectionId>,
208) -> (SymbolSection, u64) {
209    let section = match &object.place {
210        Place::Written => obj.section_id(StandardSection::Data),
211        Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
212        // Read only after the loader has written it, which the writer knows as the relocatable
213        // read only data section and which is `.data.rel.ro` on ELF. The `.local` half is a layout
214        // hint the writer has no name for, so it is added by hand and remembered: asking again
215        // would make a second section with the same name, and a file with one of those per variable
216        // is a file whose section headers outweigh what they describe.
217        Place::RelocReadOnly { local: false } => {
218            obj.section_id(StandardSection::ReadOnlyDataWithRel)
219        }
220        Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
221            obj.add_section(
222                Vec::new(),
223                b".data.rel.ro.local".to_vec(),
224                SectionKind::ReadOnlyDataWithRel,
225            )
226        }),
227        Place::Zero => obj.section_id(StandardSection::UninitializedData),
228        Place::Merged => return (SymbolSection::Common, 0),
229        // A named section is the program's word for where this goes, and a program that names one
230        // wants what it named rather than what would have been chosen. It is written as ordinary
231        // data because nothing in the IR says otherwise.
232        Place::Named(name) => {
233            obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
234        }
235    };
236    let offset = if object.place == Place::Zero {
237        obj.append_section_bss(section, object.size, object.align)
238    } else {
239        obj.append_section_data(section, &object.bytes, object.align)
240    };
241    (SymbolSection::Section(section), offset)
242}
243
244/// One relocation, at `offset` bytes into the section its image landed at.
245fn add(
246    obj: &mut Writer<'_>,
247    section: object::write::SectionId,
248    offset: u64,
249    reloc: &Reloc,
250    symbols: &std::collections::BTreeMap<String, object::write::SymbolId>,
251) -> Result<(), Error> {
252    let r_type = r_type(reloc.kind)
253        .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
254    obj.add_relocation(
255        section,
256        Relocation {
257            offset: offset + reloc.at as u64,
258            symbol: symbols[&reloc.symbol],
259            addend: reloc.addend,
260            flags: RelocationFlags::Elf { r_type },
261        },
262    )
263    .map_err(|why| Error::Refused { why: why.to_string() })
264}
265
266/// How far a name reaches, which is the one thing about a symbol ELF calls its binding.
267///
268/// `SymbolScope` is two facts in one word, and the trap is that the middle one is not the neutral
269/// answer it reads as. The writer turns `Compilation` into a local symbol, and it turns the choice
270/// between `Linkage` and `Dynamic` into `st_other`: `Linkage` is `STV_HIDDEN` and `Dynamic` is
271/// `STV_DEFAULT`. So there is no way to say global and decline to say anything about visibility,
272/// and picking the one whose name sounds like the smaller claim is picking hidden.
273///
274/// `Dynamic` is what an ordinary global is. gcc writes `STV_DEFAULT` for one and so does every
275/// other compiler, because a name a program did not mark is a name the dynamic linker is allowed
276/// to see. Hidden is what `__attribute__((visibility("hidden")))` and `-fvisibility=hidden` ask
277/// for, and neither reaches here yet, which is tracked as tamnd/rucc#733 along with the rest of
278/// the visibility plumbing.
279fn scope_of(binding: Binding) -> SymbolScope {
280    match binding {
281        Binding::Local => SymbolScope::Compilation,
282        Binding::Global | Binding::Weak => SymbolScope::Dynamic,
283    }
284}
285
286/// Which relocation of this machine one reference is, and nothing for one this machine has none of.
287///
288/// The first three are the distance from the end of an instruction to something, and they differ in
289/// what the linker is allowed to do about it. A call may go through a stub, which is what lets a
290/// call reach a symbol further away than four bytes can say and what makes a call to a shared
291/// library work at all. A load may not, because there is nowhere to put a stub that a load would
292/// read, so a load of something another object may define reads a table slot the linker fills in
293/// instead, and the relaxing form of the relocation lets the linker undo that when it turns out
294/// nobody else defines it. The fourth is the address itself, at the two widths this machine writes
295/// one at.
296fn r_type(reference: Reference) -> Option<elf::RelocationType> {
297    Some(match reference {
298        Reference::Call => elf::R_X86_64_PLT32,
299        Reference::Data => elf::R_X86_64_PC32,
300        Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
301        Reference::Address { bytes: 8 } => elf::R_X86_64_64,
302        Reference::Address { bytes: 4 } => elf::R_X86_64_32,
303        Reference::Address { .. } => return None,
304    })
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    use object::read::elf::Sym as _;
312    use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
313    use rucc_target::{Arch, Env, Os, Triple};
314
315    use crate::section::{Extent, Reloc};
316
317    /// A linux x86-64 target, which is the only one this writes.
318    fn target() -> TargetInfo {
319        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
320    }
321
322    /// A call to something outside the file, which is the shape every case here starts from.
323    fn calling(name: &str) -> Text {
324        Text {
325            bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
326            funcs: vec![Extent {
327                name: "f".to_owned(),
328                start: 0,
329                len: 6,
330                binding: Binding::Global,
331            }],
332            relocs: vec![Reloc {
333                at: 1,
334                symbol: name.to_owned(),
335                kind: Reference::Call,
336                addend: -4,
337            }],
338            ..Text::default()
339        }
340    }
341
342    #[test]
343    fn the_bytes_come_back_out_of_the_section_they_went_into() {
344        let text = calling("puts");
345        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
346        let file = object::File::parse(&bytes[..]).expect("a readable object");
347        let section = file.section_by_name(".text").expect("a text section");
348        assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
349    }
350
351    #[test]
352    fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
353        let mut text = calling("puts");
354        text.funcs.push(Extent {
355            name: "g".to_owned(),
356            start: 16,
357            len: 1,
358            binding: Binding::Global,
359        });
360        text.bytes.resize(17, 0x90);
361        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
362        let file = object::File::parse(&bytes[..]).expect("a readable object");
363        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
364        assert_eq!(g.address(), 16);
365        assert_eq!(g.size(), 1);
366        assert_eq!(g.kind(), SymbolKind::Text);
367        assert!(g.is_global(), "nothing said otherwise about this one");
368    }
369
370    #[test]
371    fn a_function_no_other_file_can_see_is_a_local_symbol() {
372        let mut text = calling("puts");
373        text.funcs.push(Extent {
374            name: "hidden".to_owned(),
375            start: 16,
376            len: 1,
377            binding: Binding::Local,
378        });
379        text.funcs.push(Extent {
380            name: "shared".to_owned(),
381            start: 32,
382            len: 1,
383            binding: Binding::Weak,
384        });
385        text.bytes.resize(33, 0x90);
386        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
387        let file = object::File::parse(&bytes[..]).expect("a readable object");
388        let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
389        // A symbol the linker keeps and does not let another file reach, which is the whole of
390        // what `static` on a function means and what two files each defining their own need.
391        assert!(hidden.is_local(), "a static function must not be offered to the linker");
392        assert!(!hidden.is_weak());
393        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
394        assert!(shared.is_weak(), "a weak function has to be able to lose");
395        assert!(shared.is_global());
396    }
397
398    /// A global is `STV_DEFAULT`, so a shared library built from these objects exports something.
399    ///
400    /// The bug in tamnd/rucc#733. Every global came out `STV_HIDDEN`, which a static link does not
401    /// look at, so nothing here noticed and SQLite linked and ran and the whole test suite passed.
402    /// What it costs is the dynamic symbol table: `gcc -shared` over one of these objects produced
403    /// a library with an empty one, and `dlsym` could not find a function the file plainly defines.
404    ///
405    /// Written against `st_other` itself rather than against the reader's `scope`, because `scope`
406    /// is the word that was misread in the first place and a test that asks it the same question
407    /// would agree with whatever the writer did.
408    #[test]
409    fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
410        let mut text = calling("puts");
411        text.funcs.push(Extent {
412            name: "g".to_owned(),
413            start: 16,
414            len: 1,
415            binding: Binding::Global,
416        });
417        text.funcs.push(Extent { name: "w".to_owned(), start: 32, len: 1, binding: Binding::Weak });
418        text.funcs.push(Extent {
419            name: "s".to_owned(),
420            start: 48,
421            len: 1,
422            binding: Binding::Local,
423        });
424        text.bytes.resize(49, 0x90);
425        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
426        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
427        let visibility = |name: &str| {
428            file.symbols()
429                .find(|s| s.name() == Ok(name))
430                .expect("the function")
431                .elf_symbol()
432                .st_visibility()
433        };
434        // Nothing said hidden about either of these, so neither is.
435        assert_eq!(visibility("g"), elf::STV_DEFAULT);
436        assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
437        // The `static` one is local, and a local symbol's visibility means nothing either way,
438        // which is why the binding is what this asks about.
439        assert_eq!(visibility("s"), elf::STV_DEFAULT);
440    }
441
442    #[test]
443    fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
444        let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
445        let file = object::File::parse(&bytes[..]).expect("a readable object");
446        let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
447        assert!(puts.is_undefined(), "the file does not define it and must not claim to");
448    }
449
450    #[test]
451    fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
452        for (reference, wanted) in [
453            (Reference::Call, elf::R_X86_64_PLT32),
454            (Reference::Data, elf::R_X86_64_PC32),
455            (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
456        ] {
457            let mut text = calling("puts");
458            text.relocs[0].kind = reference;
459            let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
460            let file = object::File::parse(&bytes[..]).expect("a readable object");
461            let section = file.section_by_name(".text").expect("a text section");
462            let (offset, reloc) = section.relocations().next().expect("one relocation");
463            assert_eq!(offset, 1);
464            assert_eq!(reloc.addend(), -4);
465            assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
466        }
467    }
468
469    #[test]
470    fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
471        let mut text = calling("puts");
472        text.relocs.push(Reloc {
473            at: 1,
474            symbol: "puts".to_owned(),
475            kind: Reference::Call,
476            addend: -4,
477        });
478        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
479        let file = object::File::parse(&bytes[..]).expect("a readable object");
480        assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
481    }
482
483    #[test]
484    fn a_function_that_is_also_called_is_not_a_second_symbol() {
485        let text = calling("f");
486        let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
487        let file = object::File::parse(&bytes[..]).expect("a readable object");
488        let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
489        let f = found.next().expect("the function");
490        assert!(!f.is_undefined(), "the file defines it");
491        assert!(found.next().is_none(), "and defines it once");
492    }
493
494    #[test]
495    fn the_marker_that_says_the_stack_is_not_executable_is_written() {
496        let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
497        let file = object::File::parse(&bytes[..]).expect("a readable object");
498        let note = file.section_by_name(".note.GNU-stack").expect("the marker");
499        assert!(note.data().expect("no bytes").is_empty());
500    }
501
502    /// One variable of four bytes, in whichever section its own answer puts it.
503    fn variable(name: &str, place: Place) -> Object {
504        Object {
505            name: name.to_owned(),
506            bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
507            size: 4,
508            align: 4,
509            place,
510            binding: Binding::Global,
511            relocs: Vec::new(),
512        }
513    }
514
515    /// A file of that one variable and nothing else.
516    fn holding(object: Object) -> Vec<u8> {
517        let data = Data { objects: vec![object] };
518        write(&Text::default(), &data, &[], &target()).expect("an object")
519    }
520
521    #[test]
522    fn what_a_variable_is_decides_which_section_it_goes_in() {
523        for (place, wanted) in [
524            (Place::Written, ".data"),
525            (Place::ReadOnly, ".rodata"),
526            (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
527            (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
528            (Place::Zero, ".bss"),
529            (Place::Named(".init_array".to_owned()), ".init_array"),
530        ] {
531            let bytes = holding(variable("x", place.clone()));
532            let file = object::File::parse(&bytes[..]).expect("a readable object");
533            let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
534            assert_eq!(section.size(), 4, "{place:?}");
535            // The zero filled one is as long as it says and carries none of it, which is the
536            // whole reason the section exists.
537            let carried = section.data().expect("the bytes").len();
538            assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
539        }
540    }
541
542    /// Two variables that want `.data.rel.ro.local` end up in one section, not two of one name.
543    ///
544    /// The writer has no name of its own for that section, so it is added by hand, and asking for
545    /// it again makes a second section rather than handing back the first. SQLite has enough const
546    /// tables of function pointers in it to turn that into eighty odd sections in one object, each
547    /// with its own relocation section beside it, which is a pile of section headers describing
548    /// eight bytes apiece.
549    #[test]
550    fn every_variable_that_wants_the_local_relocated_section_shares_one() {
551        let place = Place::RelocReadOnly { local: true };
552        let data =
553            Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
554        let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
555        let file = object::File::parse(&bytes[..]).expect("a readable object");
556        let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
557        assert_eq!(named, 1, "one section holding both, not one each");
558    }
559
560    #[test]
561    fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
562        let mut data = Data { objects: vec![variable("first", Place::Written)] };
563        data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
564        let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
565        let file = object::File::parse(&bytes[..]).expect("a readable object");
566        let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
567        assert_eq!(second.kind(), SymbolKind::Data);
568        assert_eq!(second.size(), 4);
569        // Sixteen rather than four, because the second one asked for sixteen and the first one
570        // had already used four. Getting this wrong is a variable at an address it said it would
571        // never be at, which nothing downstream would notice until an aligned load faulted.
572        assert_eq!(second.address(), 16);
573    }
574
575    #[test]
576    fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
577        for (binding, global, weak) in [
578            (Binding::Global, true, false),
579            (Binding::Local, false, false),
580            (Binding::Weak, true, true),
581        ] {
582            let bytes = holding(Object { binding, ..variable("x", Place::Written) });
583            let file = object::File::parse(&bytes[..]).expect("a readable object");
584            let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
585            assert_eq!(x.is_global(), global, "{binding:?}");
586            assert_eq!(x.is_weak(), weak, "{binding:?}");
587        }
588    }
589
590    #[test]
591    fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
592        let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
593        let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
594        let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
595        assert!(x.is_common(), "the linker merges every definition of this name into one");
596        assert_eq!(x.size(), 4);
597        // What a common symbol records where an ordinary one records its address is what it wants
598        // to be aligned to, because it has no address yet. The reader deliberately answers nothing
599        // when asked for the address of one, so this is the field itself.
600        assert_eq!(x.address(), 0);
601        assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
602    }
603
604    #[test]
605    fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
606        let object = Object {
607            bytes: vec![0; 8],
608            size: 8,
609            align: 8,
610            relocs: vec![Reloc {
611                at: 0,
612                symbol: "y".to_owned(),
613                kind: Reference::Address { bytes: 8 },
614                addend: 16,
615            }],
616            ..variable("p", Place::Written)
617        };
618        let bytes = holding(object);
619        let file = object::File::parse(&bytes[..]).expect("a readable object");
620        let section = file.section_by_name(".data").expect("a data section");
621        let (offset, reloc) = section.relocations().next().expect("one relocation");
622        assert_eq!(offset, 0);
623        assert_eq!(reloc.addend(), 16);
624        assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
625        let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
626        assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
627    }
628
629    /// Not a rewording of the case above: what is checked is the arithmetic between the two.
630    #[test]
631    fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
632        let mut data = Data { objects: vec![variable("first", Place::Written)] };
633        data.objects.push(Object {
634            bytes: vec![0; 16],
635            size: 16,
636            align: 8,
637            relocs: vec![Reloc {
638                at: 8,
639                symbol: "y".to_owned(),
640                kind: Reference::Address { bytes: 8 },
641                addend: 0,
642            }],
643            ..variable("second", Place::Written)
644        });
645        let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
646        let file = object::File::parse(&bytes[..]).expect("a readable object");
647        let section = file.section_by_name(".data").expect("a data section");
648        let (offset, _) = section.relocations().next().expect("one relocation");
649        // Eight into the second image, which starts eight in because the first one is four long
650        // and the second is eight aligned.
651        assert_eq!(offset, 16);
652    }
653
654    #[test]
655    fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
656        let data = Data {
657            objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
658        };
659        let aliases =
660            [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
661        let bytes = write(&Text::default(), &data, &aliases, &target()).expect("an object");
662        let file = object::File::parse(&bytes[..]).expect("a readable object");
663        let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
664        let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
665        assert_eq!(b.address(), a.address(), "the same place");
666        assert_eq!(b.size(), a.size());
667        assert_eq!(b.section_index(), a.section_index());
668        // The binding is the one thing the second name does not take from the first, which is
669        // what `extern int b __attribute__((alias("a")))` on a `static a` asks for.
670        assert!(a.is_local(), "the target was written `static`");
671        assert!(b.is_global(), "and the name given to it was not");
672        // Four bytes of image and not eight, since an alias is a name and not a copy.
673        assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
674    }
675
676    #[test]
677    fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
678        let text = calling("puts");
679        let aliases =
680            [Alias { name: "g".to_owned(), target: "f".to_owned(), binding: Binding::Weak }];
681        let bytes = write(&text, &Data::default(), &aliases, &target()).expect("an object");
682        let file = object::File::parse(&bytes[..]).expect("a readable object");
683        let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
684        let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
685        assert_eq!(g.address(), f.address());
686        assert_eq!(g.size(), f.size());
687        assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
688        assert!(g.is_weak(), "so that a program may define the name itself instead");
689    }
690
691    /// The front end is what reports this as a program's mistake, so one arriving here is a bug
692    /// in this compiler and is said so rather than written as an undefined symbol.
693    #[test]
694    fn a_second_name_for_something_this_file_does_not_define_is_refused() {
695        let aliases =
696            [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
697        let error = write(&Text::default(), &Data::default(), &aliases, &target())
698            .expect_err("nothing to point at");
699        assert!(matches!(error, Error::Refused { .. }), "{error:?}");
700    }
701
702    #[test]
703    fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
704        let text = calling("puts");
705        for triple in [
706            Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
707            Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
708        ] {
709            let error = write(&text, &Data::default(), &[], &TargetInfo::new(triple))
710                .expect_err("no writer");
711            assert!(matches!(error, Error::Format { .. }), "{error:?}");
712        }
713    }
714}