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 10 reaches down
9//! to these at rank 8, 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}
36
37impl Default for Text {
38 fn default() -> Self {
39 Self { bytes: Vec::new(), funcs: Vec::new(), relocs: Vec::new(), align: FUNC_ALIGN }
40 }
41}
42
43/// Where one function ended up.
44///
45/// How long a function is is a fact ELF records and Mach-O has no way to, so it is handed over
46/// rather than worked out again: the writer that wants it has it and the one that does not
47/// ignores it.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct Extent {
50 /// The function's name, as the C program spelled it. The underscore an Apple symbol carries
51 /// is the object writer's business, not this one's.
52 pub name: String,
53 /// Where its first instruction is.
54 pub start: usize,
55 /// How many bytes of instructions it is, not counting the padding in front of the next one.
56 pub len: usize,
57 /// How the linker sees the name, which is what the C `static` reaches the object file as.
58 pub binding: Binding,
59}
60
61/// The variables a file defines, and what the linker has to be told about them.
62///
63/// One entry per variable rather than one section of everything, because where a variable goes is
64/// worked out from what it is and two of them that land in one section still have their own
65/// alignment, their own size and their own symbol. Putting them together is the writer's job and
66/// is the one part of it the three formats disagree about.
67#[derive(Debug, Clone, Default, PartialEq, Eq)]
68pub struct Data {
69 /// Every variable this file defines, in the order the module held them.
70 pub objects: Vec<Object>,
71}
72
73/// A second name for something the same file defines.
74///
75/// Not a section and not a byte of anything, which is the whole point of it: an alias is a symbol
76/// table entry pointing at an address something else already occupies, so a file with one in it is
77/// no larger than the same file without. `.set b, a` is what an assembler is told and a second
78/// entry at the first one's section, value and size is what a writer produces, and the two say the
79/// same thing.
80///
81/// The target is a name rather than an index into anything above, because the two output paths
82/// find it in different places: a listing hands the name to an assembler that resolves it, and a
83/// writer looks it up among the symbols it has already added.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct Alias {
86 /// The name being defined, as the C program spelled it.
87 pub name: String,
88 /// The name it stands for, which has to be something this same file defines.
89 pub target: String,
90 /// How the linker sees the new name, which is not always how it sees the old one: the target
91 /// of `extern int b __attribute__((alias("a")))` may be a `static`.
92 pub binding: Binding,
93}
94
95/// One global variable, laid out.
96#[derive(Debug, Clone, PartialEq, Eq)]
97pub struct Object {
98 /// Its name, as the C program spelled it. The underscore an Apple symbol carries is the
99 /// object writer's business, not this one's.
100 pub name: String,
101 /// Its image, and nothing at all when it is zero filled and the file carries none of it.
102 pub bytes: Vec<u8>,
103 /// How many bytes it occupies, which is the length of the image except when there is none.
104 pub size: u64,
105 /// What it has to be aligned to, always a power of two.
106 pub align: u64,
107 /// Which section it goes in.
108 pub place: Place,
109 /// How the linker sees the name.
110 pub binding: Binding,
111 /// Every place in its image that holds the address of a symbol, counted from the start of
112 /// the image rather than from the start of the section it lands in.
113 pub relocs: Vec<Reloc>,
114}
115
116/// Which section a variable goes in.
117///
118/// Worked out from what the variable is rather than named by it, except in the one case where the
119/// program named it. A reader who wants to know why a variable is in `.rodata` should be able to
120/// find the answer in the variable.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub enum Place {
123 /// Written to, and its image is not all zeros. `.data`.
124 Written,
125 /// Never written to, so it can go in a page the loader maps read only and every process
126 /// running the program can share. `.rodata`.
127 ReadOnly,
128 /// All zeros, so the file says how big it is and carries none of it. `.bss`.
129 Zero,
130 /// A tentative definition, which is not in a section at all: the linker is asked for that
131 /// much zeroed space and merges every definition of the name into one. `.comm`.
132 Merged,
133 /// The section the program named, from `__attribute__((section(...)))`.
134 Named(String),
135}
136
137/// How the linker sees a name.
138///
139/// Three of the five linkages the IR has, because that is how many an object file can say. Which
140/// of the two weak ones a symbol had is a fact the optimizer needs and the linker does not.
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum Binding {
143 /// Visible to every other object, and the definition here is the definition.
144 Global,
145 /// Invisible outside this object, which is what `static` at file scope means.
146 Local,
147 /// Visible, and allowed to lose to a definition in another object.
148 Weak,
149}
150
151/// One reference to something this file does not contain.
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct Reloc {
154 /// Where the bytes the linker writes over begin.
155 pub at: usize,
156 /// What is wanted, as the C program spelled it.
157 pub symbol: String,
158 /// What the linker is being asked for.
159 pub kind: Reference,
160 /// What to add to the distance, which is the constant the instruction already meant plus the
161 /// bytes between the hole and the end of the instruction, negated. An instruction counts from
162 /// where it ends and a relocation counts from where it starts, and this is the difference.
163 pub addend: i64,
164}
165
166/// What kind of thing a relocation is asking the linker for.
167///
168/// The first two are the distance from the end of an instruction to something, which is what every
169/// reference the code makes is, because this compiler generates position independent code and
170/// nothing else. They are told apart because the linker may answer one of them with a stub and may
171/// not answer the other one that way. The third is not a distance at all and is the only kind an
172/// image asks for, since an initializer holding the address of something holds the address itself.
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub enum Reference {
175 /// A call, which the linker may satisfy with a stub that reaches further than the four bytes
176 /// would. `R_X86_64_PLT32` on ELF, and the same relocation a branch gets on the other two.
177 Call,
178 /// A datum, reached from the instruction pointer. `R_X86_64_PC32` on ELF.
179 Data,
180 /// The address itself, written into an image. `int *p = &y;` and nothing else in C.
181 Address {
182 /// How many bytes of it are written, which is the pointer width except on a target with
183 /// a narrower relocation for it. `R_X86_64_64` and `R_X86_64_32` on ELF.
184 bytes: u8,
185 },
186}