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