Skip to main content

rucc_object/
lib.rs

1//! ELF and COFF object writers.
2//!
3//! Design: `spec/11-asm-objects-debug.md`. Layer rank 9, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! ELF, which is what Linux and the freestanding targets want and what M3 needs, and COFF, which is
8//! what Windows wants. A text section, the symbols that say where each function in it is and how
9//! long it is, the names it wanted that are not in it, the relocations that ask a linker to find
10//! them, and the marker whose absence makes the stack executable. An object this writes links with
11//! the system linker and runs.
12//!
13//! The variables a file defines are written too: the section each one goes in, the symbol that
14//! says where it is and how long it is, the binding that says who can see it, and the relocations
15//! an image asks for when it holds the address of something. A tentative definition is asked of
16//! the linker rather than put in a section, which is the one case where a variable has a symbol
17//! and no bytes anywhere.
18//!
19//! An [`Alias`] is written as a second symbol at the first one's section, value and size, with a
20//! binding of its own and no second copy of the bytes, which is what a file gets from
21//! `__attribute__((alias("target")))` and what makes one name reach another at no cost.
22//!
23//! [`Sections`] says whether each function and each variable gets a section to itself, which is
24//! what `-ffunction-sections` and `-fdata-sections` ask for and what makes `--gc-sections` able to
25//! drop anything: a linker can leave out a section nothing reaches and cannot leave out half of
26//! one. The names and the offsets are the same either way, so the only thing that moves is which
27//! section header a symbol points at.
28//!
29//! [`Array`] is what a section of function addresses for the startup code to call is, which is what
30//! the `constructor` and `destructor` attributes produce. ELF has a section type for each of the
31//! three kinds, and a section of the ordinary type under one of those names is gathered by the
32//! linker in the same run and called by nobody, so the type is written out rather than left to the
33//! default. COFF has nothing of the sort under those names, so a file with one is refused for a
34//! Windows target rather than written with its constructors never run.
35//!
36//! [`Property`] is what the file says it was built to have checked, which is what
37//! `-fcf-protection=` asks for. It is written as a note the linker keeps only the agreed part of
38//! and the loader reads out of the result, which is why a file that says nothing about it turns
39//! the check off for the whole program rather than only for itself.
40//!
41//! [`defines`] says which names a linker can find in what [`write()`] wrote, which is what the symbol
42//! index of an archive is built from. It is here rather than worked out by whoever writes the
43//! archive because the index has to agree with the member, and the writer is the only thing that
44//! knows what it put in one.
45//!
46//! What it is given is [`Text`], [`Data`] and the aliases between them, which are here rather than
47//! beside the assembler that fills them in because they are what an object file is made of and
48//! because a writer cannot depend on the thing that produces its input without the layer graph
49//! going the wrong way round.
50//!
51//! [`assembled`] is the other way in, for an object written from a file of assembly rather than
52//! from a compilation. It takes [`Assembled`], which is a list of sections that each carry their
53//! own name and flags and a flat list of names that point into them at offsets. That is a different
54//! shape from [`Text`] and [`Data`] because a file of assembly says things neither of them can hold:
55//! which section something is in, a name at an offset that no variable covers, and a name that is a
56//! number rather than a place. Both go through the same writer underneath, so there is still one
57//! place that knows how a file is laid out.
58//!
59//! An unwind table is written for both, and the two formats want it laid out differently: ELF has
60//! one section of records that each carry their own codes, and Windows has a table of fixed rows in
61//! `.pdata` pointing at the descriptions in `.xdata`. Both come in as bytes and relocations, because
62//! what a record is is the platform's answer and the layer that knows what a frame did is the one
63//! that can say it. Thread-local storage, a reference through a global offset table and a record of
64//! where a patcher's room is are refused for COFF, each being something that format has no way to
65//! write rather than something not written yet.
66//!
67//! Mach-O is not written yet. It waits on the target that needs it.
68//!
69//! Every crate in the workspace is published, and publishing implies a promise. This one is
70//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
71//! Depend on the `rucc` binary's behaviour, not on this.
72
73#![doc(html_root_url = "https://docs.rs/rucc-object/0.10.65")]
74
75mod coff;
76mod elf;
77mod file;
78mod section;
79mod source;
80
81pub use crate::file::{Error, defines, write};
82pub use crate::section::{
83    Alias, Array, Binding, Data, Extent, FUNC_ALIGN, Marker, Object, Output, Patch, Place,
84    Property, Reference, Reloc, Sections, Text, Unwind, Visibility,
85};
86pub use crate::source::{Assembled, Held, Name, Part, Shape, Sort, assembled, assembled_defines};
87
88/// The milestone in `spec/17-milestones.md` that fills this crate in.
89pub const MILESTONE: &str = "M3";
90
91#[cfg(test)]
92mod tests {
93    #[test]
94    fn milestone_is_recorded() {
95        assert!(super::MILESTONE.starts_with('M'));
96    }
97}