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/// A text section, and what the linker has to be told about it.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Text {
22 /// The instructions, in the order they were laid out.
23 pub bytes: Vec<u8>,
24 /// Where each function starts and how long it is, in the order they were written.
25 pub funcs: Vec<Extent>,
26 /// Every place in the bytes that names something the linker has to find.
27 pub relocs: Vec<Reloc>,
28 /// What the whole section has to be aligned to, which is the largest alignment any function
29 /// in it asked for.
30 ///
31 /// A function is at a fixed offset inside the section, so a function at a multiple of two
32 /// hundred and fifty six is one only if the section itself is at one. The padding between the
33 /// functions is the assembler's half of the same job and this is the linker's.
34 pub align: u32,
35 /// What an unwinder is told about the functions, which is empty for a format that has no such
36 /// section or a build that asked for none.
37 pub unwind: Unwind,
38}
39
40impl Default for Text {
41 fn default() -> Self {
42 Self {
43 bytes: Vec::new(),
44 funcs: Vec::new(),
45 relocs: Vec::new(),
46 align: FUNC_ALIGN,
47 unwind: Unwind::default(),
48 }
49 }
50}
51
52/// The unwind table, as the bytes of its own section and what the linker has to be told about them.
53///
54/// Bytes rather than rows, because what a record is is DWARF's answer and not the object format's,
55/// and the layer that knows what a frame did is the one that can say it in the fewest of them. What
56/// is left for the writer is where the section goes and what its relocations are, which is the part
57/// the three formats disagree about.
58///
59/// Each record says where its function is as a distance from the record to the function, which is
60/// a number no compilation knows: a function is at a fixed offset inside its own section and the
61/// section is placed by the linker. So there is one relocation per record and it is the ordinary
62/// instruction pointer relative one, since the distance is between two things in the same file.
63#[derive(Debug, Clone, Default, PartialEq, Eq)]
64pub struct Unwind {
65 /// The records, one shared header and one per function.
66 pub bytes: Vec<u8>,
67 /// Every place in them that names a function the linker has to place.
68 pub relocs: Vec<Reloc>,
69}
70
71/// Where one function ended up.
72///
73/// How long a function is is a fact ELF records and Mach-O has no way to, so it is handed over
74/// rather than worked out again: the writer that wants it has it and the one that does not
75/// ignores it.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct Extent {
78 /// The function's name, as the C program spelled it. The underscore an Apple symbol carries
79 /// is the object writer's business, not this one's.
80 pub name: String,
81 /// Where its first instruction is.
82 pub start: usize,
83 /// How many bytes of instructions it is, not counting the padding in front of the next one.
84 pub len: usize,
85 /// How the linker sees the name, which is what the C `static` reaches the object file as.
86 pub binding: Binding,
87 /// How far outside a shared library holding this the name reaches.
88 pub visibility: Visibility,
89}
90
91/// The variables a file defines, and what the linker has to be told about them.
92///
93/// One entry per variable rather than one section of everything, because where a variable goes is
94/// worked out from what it is and two of them that land in one section still have their own
95/// alignment, their own size and their own symbol. Putting them together is the writer's job and
96/// is the one part of it the three formats disagree about.
97#[derive(Debug, Clone, Default, PartialEq, Eq)]
98pub struct Data {
99 /// Every variable this file defines, in the order the module held them.
100 pub objects: Vec<Object>,
101}
102
103/// A second name for something the same file defines.
104///
105/// Not a section and not a byte of anything, which is the whole point of it: an alias is a symbol
106/// table entry pointing at an address something else already occupies, so a file with one in it is
107/// no larger than the same file without. `.set b, a` is what an assembler is told and a second
108/// entry at the first one's section, value and size is what a writer produces, and the two say the
109/// same thing.
110///
111/// The target is a name rather than an index into anything above, because the two output paths
112/// find it in different places: a listing hands the name to an assembler that resolves it, and a
113/// writer looks it up among the symbols it has already added.
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct Alias {
116 /// The name being defined, as the C program spelled it.
117 pub name: String,
118 /// The name it stands for, which has to be something this same file defines.
119 pub target: String,
120 /// How the linker sees the new name, which is not always how it sees the old one: the target
121 /// of `extern int b __attribute__((alias("a")))` may be a `static`.
122 pub binding: Binding,
123 /// How far outside a shared library holding this the new name reaches, which is its own
124 /// answer for the same reason the binding is: the attribute is written on the alias.
125 pub visibility: Visibility,
126}
127
128/// One global variable, laid out.
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct Object {
131 /// Its name, as the C program spelled it. The underscore an Apple symbol carries is the
132 /// object writer's business, not this one's.
133 pub name: String,
134 /// Its image, and nothing at all when it is zero filled and the file carries none of it.
135 pub bytes: Vec<u8>,
136 /// How many bytes it occupies, which is the length of the image except when there is none.
137 pub size: u64,
138 /// What it has to be aligned to, always a power of two.
139 pub align: u64,
140 /// Which section it goes in.
141 pub place: Place,
142 /// How the linker sees the name.
143 pub binding: Binding,
144 /// How far outside a shared library holding this the name reaches.
145 pub visibility: Visibility,
146 /// Every place in its image that holds the address of a symbol, counted from the start of
147 /// the image rather than from the start of the section it lands in.
148 pub relocs: Vec<Reloc>,
149}
150
151/// Which section a variable goes in.
152///
153/// Worked out from what the variable is rather than named by it, except in the one case where the
154/// program named it. A reader who wants to know why a variable is in `.rodata` should be able to
155/// find the answer in the variable.
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub enum Place {
158 /// Written to, and its image is not all zeros. `.data`.
159 Written,
160 /// Never written to, so it can go in a page the loader maps read only and every process
161 /// running the program can share. `.rodata`.
162 ReadOnly,
163 /// Never written to by the program, but written once by the dynamic linker, because its image
164 /// holds the address of something and an address is not known until the image is loaded.
165 /// `.data.rel.ro`.
166 ///
167 /// The section has to be writable for that one write and read only afterwards, which is what
168 /// the `PT_GNU_RELRO` segment is: the loader maps it, the relocations are applied, and then it
169 /// is turned read only before the program starts. Putting the variable in `.rodata` instead
170 /// means asking the linker to leave a relocation in a section that is never writable, and what
171 /// it does about that is give the whole image `DT_TEXTREL`, which gives up the protection the
172 /// section was for. Some hardened toolchains refuse the link outright.
173 RelocReadOnly {
174 /// Whether every address in the image is of something this file defines and does not
175 /// export, which means the link can resolve them all and none can be interposed.
176 ///
177 /// Those go in `.data.rel.ro.local`, which the linker puts in the first pages of the
178 /// segment, so the pages holding them are the ones the loader is done with soonest. It is
179 /// a hint about layout rather than a difference in what the section is.
180 local: bool,
181 },
182 /// All zeros, so the file says how big it is and carries none of it. `.bss`.
183 Zero,
184 /// A tentative definition, which is not in a section at all: the linker is asked for that
185 /// much zeroed space and merges every definition of the name into one. `.comm`.
186 Merged,
187 /// The section the program named, from `__attribute__((section(...)))`.
188 Named(String),
189}
190
191/// How the linker sees a name.
192///
193/// Three of the five linkages the IR has, because that is how many an object file can say. Which
194/// of the two weak ones a symbol had is a fact the optimizer needs and the linker does not.
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub enum Binding {
197 /// Visible to every other object, and the definition here is the definition.
198 Global,
199 /// Invisible outside this object, which is what `static` at file scope means.
200 Local,
201 /// Visible, and allowed to lose to a definition in another object.
202 Weak,
203}
204
205/// How far outside a shared library a name reaches.
206///
207/// A different question from [`Binding`] and asked of a different linker. The binding is what the
208/// static linker does with a name while it is building the output, and this is what the dynamic
209/// linker may do with it once the output is a shared library and is being loaded. A hidden name is
210/// still global to the static link, so two files in the same library can call each other by it; it
211/// is simply not in the dynamic symbol table afterwards, so nothing outside can name it.
212///
213/// Written down here as its own thing rather than folded into the binding because it is the
214/// mistake tamnd/rucc#733 was: a writer that has one word for both ends up saying something about
215/// visibility while it thinks it is saying something about linkage, and what it said was hidden.
216///
217/// It means nothing for a [`Binding::Local`] name. `static` is already invisible to the whole
218/// world outside the file, and ELF records `STV_DEFAULT` for one, which is what gcc writes.
219#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
220pub enum Visibility {
221 /// In the dynamic symbol table, and a reference from inside the library may be satisfied by a
222 /// definition somewhere else, which is what makes `LD_PRELOAD` work. What a name gets when
223 /// nothing said otherwise.
224 #[default]
225 Default,
226 /// Not in the dynamic symbol table at all, so nothing outside the library can name it and
227 /// every reference to it from inside binds here. `__attribute__((visibility("hidden")))`.
228 Hidden,
229 /// In the dynamic symbol table, so something outside can name it, but a reference from inside
230 /// the library binds to the definition inside it and cannot be interposed.
231 Protected,
232}
233
234/// One reference to something this file does not contain.
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct Reloc {
237 /// Where the bytes the linker writes over begin.
238 pub at: usize,
239 /// What is wanted, as the C program spelled it.
240 pub symbol: String,
241 /// What the linker is being asked for.
242 pub kind: Reference,
243 /// What to add to the distance, which is the constant the instruction already meant plus the
244 /// bytes between the hole and the end of the instruction, negated. An instruction counts from
245 /// where it ends and a relocation counts from where it starts, and this is the difference.
246 pub addend: i64,
247}
248
249/// What kind of thing a relocation is asking the linker for.
250///
251/// The first three are the distance from the end of an instruction to something, which is what
252/// every reference the code makes is, because this compiler generates position independent code and
253/// nothing else. They are told apart by what the linker is allowed to do about each one. The fourth
254/// is not a distance at all and is the only kind an image asks for, since an initializer holding the
255/// address of something holds the address itself.
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum Reference {
258 /// A call, which the linker may satisfy with a stub that reaches further than the four bytes
259 /// would. `R_X86_64_PLT32` on ELF, and the same relocation a branch gets on the other two.
260 Call,
261 /// A datum, reached from the instruction pointer. `R_X86_64_PC32` on ELF.
262 Data,
263 /// A slot of the global offset table, reached from the instruction pointer, holding the
264 /// address of something another object may be the one that defines.
265 ///
266 /// The distance to the slot rather than to the thing, which is the whole difference: the
267 /// distance to the thing is a number only a link that puts the thing in this program can
268 /// work out, and a shared library is a link that does not. `R_X86_64_REX_GOTPCRELX` on ELF,
269 /// which says the instruction is a `mov` with a REX prefix and lets the linker turn it back
270 /// into the `lea` it would have been if the symbol had been here all along.
271 Got,
272 /// The address itself, written into an image. `int *p = &y;` and nothing else in C.
273 Address {
274 /// How many bytes of it are written, which is the pointer width except on a target with
275 /// a narrower relocation for it. `R_X86_64_64` and `R_X86_64_32` on ELF.
276 bytes: u8,
277 },
278}