Skip to main content

rucc_object/
section.rs

1//! What an object writer is given, which is a section of bytes and what the linker has to be
2//! told about them.
3//!
4//! Design: `spec/11-asm-objects-debug.md` sections 11.1 and 11.3.
5//!
6//! These types are here rather than beside the assembler that fills them in because they are what
7//! an object file is made of, and because a writer cannot depend on the thing that produces its
8//! input without the graph going the wrong way round. The assembler at layer rank 11 reaches down
9//! to these at rank 9, which is the direction `spec/18-package-layout.md` asks for.
10
11/// What a function is aligned to when nothing asked for more.
12///
13/// Sixteen because that is what every x86-64 toolchain puts a function at, and because it is what
14/// keeps the loop inside one from straddling one more cache line than it has to. Here rather than
15/// beside the assembler because the assembler pads to it and the writer records it, and two
16/// copies of one number is how the padding and the record come apart.
17pub const FUNC_ALIGN: u32 = 16;
18
19/// Whether each function and each variable gets a section to itself.
20///
21/// Design: `spec/11-asm-objects-debug.md` section 11.3, and `spec/04-driver-and-cli.md` section 4.7
22/// for the flags that ask for it.
23///
24/// A linker can drop a section nothing reaches and cannot drop half of one, so a file whose
25/// functions share a section keeps every function that file defines in the output as soon as any
26/// one of them is called. Splitting them is what makes `--gc-sections` do anything, which is how an
27/// embedded image or a kernel gets small, and it is the whole of what these two flags are for. The
28/// cost is a section header per name, which is why it is asked for rather than always done.
29///
30/// Not one flag, because gcc has two and a build that wants one of them and not the other is a
31/// build that measured something. Splitting the code is nearly free at link time; splitting the
32/// data can defeat the linker's ordering of what is next to what.
33#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
34pub struct Sections {
35    /// `-ffunction-sections`. Each function in `.text.<name>` rather than all of them in `.text`.
36    pub functions: bool,
37    /// `-fdata-sections`. Each variable in a section named after it rather than in the one its
38    /// contents would otherwise have chosen.
39    pub data: bool,
40}
41
42impl Sections {
43    /// Whether either of them was asked for.
44    #[must_use]
45    pub const fn any(self) -> bool {
46        self.functions || self.data
47    }
48}
49
50/// A text section, and what the linker has to be told about it.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct Text {
53    /// The instructions, in the order they were laid out.
54    pub bytes: Vec<u8>,
55    /// Where each function starts and how long it is, in the order they were written.
56    pub funcs: Vec<Extent>,
57    /// Every place in the bytes that names something the linker has to find.
58    pub relocs: Vec<Reloc>,
59    /// What the whole section has to be aligned to, which is the largest alignment any function
60    /// in it asked for.
61    ///
62    /// A function is at a fixed offset inside the section, so a function at a multiple of two
63    /// hundred and fifty six is one only if the section itself is at one. The padding between the
64    /// functions is the assembler's half of the same job and this is the linker's.
65    pub align: u32,
66    /// What an unwinder is told about the functions, which is empty for a format that has no such
67    /// section or a build that asked for none.
68    pub unwind: Unwind,
69}
70
71impl Default for Text {
72    fn default() -> Self {
73        Self {
74            bytes: Vec::new(),
75            funcs: Vec::new(),
76            relocs: Vec::new(),
77            align: FUNC_ALIGN,
78            unwind: Unwind::default(),
79        }
80    }
81}
82
83/// The unwind table, as the bytes of its own section and what the linker has to be told about them.
84///
85/// Bytes rather than rows, because what a record is is DWARF's answer and not the object format's,
86/// and the layer that knows what a frame did is the one that can say it in the fewest of them. What
87/// is left for the writer is where the section goes and what its relocations are, which is the part
88/// the three formats disagree about.
89///
90/// Each record says where its function is as a distance from the record to the function, which is
91/// a number no compilation knows: a function is at a fixed offset inside its own section and the
92/// section is placed by the linker. So there is one relocation per record and it is the ordinary
93/// instruction pointer relative one, since the distance is between two things in the same file.
94#[derive(Debug, Clone, Default, PartialEq, Eq)]
95pub struct Unwind {
96    /// The records, one shared header and one per function.
97    pub bytes: Vec<u8>,
98    /// Every place in them that names a function the linker has to place.
99    pub relocs: Vec<Reloc>,
100}
101
102/// Where one function ended up.
103///
104/// How long a function is is a fact ELF records and Mach-O has no way to, so it is handed over
105/// rather than worked out again: the writer that wants it has it and the one that does not
106/// ignores it.
107#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct Extent {
109    /// The function's name, as the C program spelled it. The underscore an Apple symbol carries
110    /// is the object writer's business, not this one's.
111    pub name: String,
112    /// Where its first instruction is.
113    pub start: usize,
114    /// How many bytes of instructions it is, not counting the padding in front of the next one.
115    pub len: usize,
116    /// What this one function asked to be aligned to, which is not always what the section it is
117    /// in was aligned to.
118    ///
119    /// The two are the same number only when this function is the one that asked for the most.
120    /// Under [`Sections::functions`] each function is a section of its own and this is what that
121    /// section is aligned to, so the number has to survive the trip rather than be recovered from
122    /// the offset, which says nothing once the function is at zero in a section of its own.
123    pub align: u32,
124    /// How the linker sees the name, which is what the C `static` reaches the object file as.
125    pub binding: Binding,
126    /// How far outside a shared library holding this the name reaches.
127    pub visibility: Visibility,
128}
129
130/// The variables a file defines, and what the linker has to be told about them.
131///
132/// One entry per variable rather than one section of everything, because where a variable goes is
133/// worked out from what it is and two of them that land in one section still have their own
134/// alignment, their own size and their own symbol. Putting them together is the writer's job and
135/// is the one part of it the three formats disagree about.
136#[derive(Debug, Clone, Default, PartialEq, Eq)]
137pub struct Data {
138    /// Every variable this file defines, in the order the module held them.
139    pub objects: Vec<Object>,
140}
141
142/// A second name for something the same file defines.
143///
144/// Not a section and not a byte of anything, which is the whole point of it: an alias is a symbol
145/// table entry pointing at an address something else already occupies, so a file with one in it is
146/// no larger than the same file without. `.set b, a` is what an assembler is told and a second
147/// entry at the first one's section, value and size is what a writer produces, and the two say the
148/// same thing.
149///
150/// The target is a name rather than an index into anything above, because the two output paths
151/// find it in different places: a listing hands the name to an assembler that resolves it, and a
152/// writer looks it up among the symbols it has already added.
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub struct Alias {
155    /// The name being defined, as the C program spelled it.
156    pub name: String,
157    /// The name it stands for, which has to be something this same file defines.
158    pub target: String,
159    /// How the linker sees the new name, which is not always how it sees the old one: the target
160    /// of `extern int b __attribute__((alias("a")))` may be a `static`.
161    pub binding: Binding,
162    /// How far outside a shared library holding this the new name reaches, which is its own
163    /// answer for the same reason the binding is: the attribute is written on the alias.
164    pub visibility: Visibility,
165}
166
167/// One global variable, laid out.
168#[derive(Debug, Clone, PartialEq, Eq)]
169pub struct Object {
170    /// Its name, as the C program spelled it. The underscore an Apple symbol carries is the
171    /// object writer's business, not this one's.
172    pub name: String,
173    /// Its image, and nothing at all when it is zero filled and the file carries none of it.
174    pub bytes: Vec<u8>,
175    /// How many bytes it occupies, which is the length of the image except when there is none.
176    pub size: u64,
177    /// What it has to be aligned to, always a power of two.
178    pub align: u64,
179    /// Which section it goes in.
180    pub place: Place,
181    /// How the linker sees the name.
182    pub binding: Binding,
183    /// How far outside a shared library holding this the name reaches.
184    pub visibility: Visibility,
185    /// Every place in its image that holds the address of a symbol, counted from the start of
186    /// the image rather than from the start of the section it lands in.
187    pub relocs: Vec<Reloc>,
188}
189
190/// Which section a variable goes in.
191///
192/// Worked out from what the variable is rather than named by it, except in the one case where the
193/// program named it. A reader who wants to know why a variable is in `.rodata` should be able to
194/// find the answer in the variable.
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum Place {
197    /// Written to, and its image is not all zeros. `.data`.
198    Written,
199    /// Never written to, so it can go in a page the loader maps read only and every process
200    /// running the program can share. `.rodata`.
201    ReadOnly,
202    /// Never written to by the program, but written once by the dynamic linker, because its image
203    /// holds the address of something and an address is not known until the image is loaded.
204    /// `.data.rel.ro`.
205    ///
206    /// The section has to be writable for that one write and read only afterwards, which is what
207    /// the `PT_GNU_RELRO` segment is: the loader maps it, the relocations are applied, and then it
208    /// is turned read only before the program starts. Putting the variable in `.rodata` instead
209    /// means asking the linker to leave a relocation in a section that is never writable, and what
210    /// it does about that is give the whole image `DT_TEXTREL`, which gives up the protection the
211    /// section was for. Some hardened toolchains refuse the link outright.
212    RelocReadOnly {
213        /// Whether every address in the image is of something this file defines and does not
214        /// export, which means the link can resolve them all and none can be interposed.
215        ///
216        /// Those go in `.data.rel.ro.local`, which the linker puts in the first pages of the
217        /// segment, so the pages holding them are the ones the loader is done with soonest. It is
218        /// a hint about layout rather than a difference in what the section is.
219        local: bool,
220    },
221    /// All zeros, so the file says how big it is and carries none of it. `.bss`.
222    Zero,
223    /// A tentative definition, which is not in a section at all: the linker is asked for that
224    /// much zeroed space and merges every definition of the name into one. `.comm`.
225    Merged,
226    /// The section the program named, from `__attribute__((section(...)))`.
227    Named(String),
228}
229
230impl Place {
231    /// What the section this variable goes in is called under [`Sections::data`], and nothing at
232    /// all for a variable that has no section of its own to be given.
233    ///
234    /// The name is the section it would otherwise have shared with a dot and the variable's name
235    /// after it, which is what gcc writes and is not merely a convention: `--gc-sections`, the
236    /// linker scripts a kernel and an embedded image are linked with, and the default placement
237    /// rules all match on the part in front of the dot, so a section called anything else would be
238    /// placed by whatever the catch all rule is.
239    ///
240    /// Two kinds of variable are left alone. A merged one is a request to the linker for that much
241    /// zeroed space rather than an image, so there is no section to split, and one the program put
242    /// a name on already has the answer the source gave, which this must not overrule.
243    ///
244    /// Here rather than beside either output path, so that the listing `-S` writes and the object
245    /// `-c` writes cannot come to disagree about where a variable went.
246    #[must_use]
247    pub fn split(&self, name: &str) -> Option<String> {
248        Some(format!("{}.{name}", self.base()?))
249    }
250
251    /// The section this variable goes in when nothing is being split up, and nothing at all for
252    /// the two kinds that are not in one.
253    #[must_use]
254    pub fn base(&self) -> Option<&'static str> {
255        Some(match self {
256            Place::Written => ".data",
257            Place::ReadOnly => ".rodata",
258            Place::RelocReadOnly { local: false } => ".data.rel.ro",
259            Place::RelocReadOnly { local: true } => ".data.rel.ro.local",
260            Place::Zero => ".bss",
261            Place::Merged | Place::Named(_) => return None,
262        })
263    }
264}
265
266/// How the linker sees a name.
267///
268/// Three of the five linkages the IR has, because that is how many an object file can say. Which
269/// of the two weak ones a symbol had is a fact the optimizer needs and the linker does not.
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub enum Binding {
272    /// Visible to every other object, and the definition here is the definition.
273    Global,
274    /// Invisible outside this object, which is what `static` at file scope means.
275    Local,
276    /// Visible, and allowed to lose to a definition in another object.
277    Weak,
278}
279
280/// How far outside a shared library a name reaches.
281///
282/// A different question from [`Binding`] and asked of a different linker. The binding is what the
283/// static linker does with a name while it is building the output, and this is what the dynamic
284/// linker may do with it once the output is a shared library and is being loaded. A hidden name is
285/// still global to the static link, so two files in the same library can call each other by it; it
286/// is simply not in the dynamic symbol table afterwards, so nothing outside can name it.
287///
288/// Written down here as its own thing rather than folded into the binding because it is the
289/// mistake tamnd/rucc#733 was: a writer that has one word for both ends up saying something about
290/// visibility while it thinks it is saying something about linkage, and what it said was hidden.
291///
292/// It means nothing for a [`Binding::Local`] name. `static` is already invisible to the whole
293/// world outside the file, and ELF records `STV_DEFAULT` for one, which is what gcc writes.
294#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
295pub enum Visibility {
296    /// In the dynamic symbol table, and a reference from inside the library may be satisfied by a
297    /// definition somewhere else, which is what makes `LD_PRELOAD` work. What a name gets when
298    /// nothing said otherwise.
299    #[default]
300    Default,
301    /// Not in the dynamic symbol table at all, so nothing outside the library can name it and
302    /// every reference to it from inside binds here. `__attribute__((visibility("hidden")))`.
303    Hidden,
304    /// In the dynamic symbol table, so something outside can name it, but a reference from inside
305    /// the library binds to the definition inside it and cannot be interposed.
306    Protected,
307}
308
309/// One reference to something this file does not contain.
310#[derive(Debug, Clone, PartialEq, Eq)]
311pub struct Reloc {
312    /// Where the bytes the linker writes over begin.
313    pub at: usize,
314    /// What is wanted, as the C program spelled it.
315    pub symbol: String,
316    /// What the linker is being asked for.
317    pub kind: Reference,
318    /// What to add to the distance, which is the constant the instruction already meant plus the
319    /// bytes between the hole and the end of the instruction, negated. An instruction counts from
320    /// where it ends and a relocation counts from where it starts, and this is the difference.
321    pub addend: i64,
322}
323
324/// What kind of thing a relocation is asking the linker for.
325///
326/// The first three are the distance from the end of an instruction to something, which is what
327/// every reference the code makes is, because this compiler generates position independent code and
328/// nothing else. They are told apart by what the linker is allowed to do about each one. The fourth
329/// is not a distance at all and is the only kind an image asks for, since an initializer holding the
330/// address of something holds the address itself.
331#[derive(Debug, Clone, Copy, PartialEq, Eq)]
332pub enum Reference {
333    /// A call, which the linker may satisfy with a stub that reaches further than the four bytes
334    /// would. `R_X86_64_PLT32` on ELF, and the same relocation a branch gets on the other two.
335    Call,
336    /// A datum, reached from the instruction pointer. `R_X86_64_PC32` on ELF.
337    Data,
338    /// A slot of the global offset table, reached from the instruction pointer, holding the
339    /// address of something another object may be the one that defines.
340    ///
341    /// The distance to the slot rather than to the thing, which is the whole difference: the
342    /// distance to the thing is a number only a link that puts the thing in this program can
343    /// work out, and a shared library is a link that does not. `R_X86_64_REX_GOTPCRELX` on ELF,
344    /// which says the instruction is a `mov` with a REX prefix and lets the linker turn it back
345    /// into the `lea` it would have been if the symbol had been here all along.
346    Got,
347    /// The address itself, written into an image. `int *p = &y;` and nothing else in C.
348    Address {
349        /// How many bytes of it are written, which is the pointer width except on a target with
350        /// a narrower relocation for it. `R_X86_64_64` and `R_X86_64_32` on ELF.
351        bytes: u8,
352    },
353}