Skip to main content

rucc_asm/
data.rs

1//! Global variables as the image a file carries and the facts a linker needs about it.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.1, which asks that the text path and the
4//! binary path share one description so they cannot disagree. That is what this is for data: the
5//! walk over a module's globals happens once, here, and what it produces is a list of pieces that
6//! [`crate::att`] writes down as directives and [`Globals::image`] writes down as bytes. A `.long`
7//! in a listing and the four bytes in the object beside it come from the same piece.
8//!
9//! # What a piece is
10//!
11//! As much of an image as one directive says. The four kinds are the four things C can put in an
12//! initializer: a run of zeros, a run of literal bytes, one scalar, and the address of a symbol.
13//! The first three are bytes the compiler knows and the fourth is a hole the linker fills, which
14//! is the only reason data has relocations at all.
15//!
16//! Where a variable goes is worked out here too, from what the variable is rather than from
17//! anything the object format says: a variable nothing writes through goes in a page the loader
18//! can map read only, one whose image is all zeros goes in the section that carries no image, and
19//! one the program named a section for goes where the program said. What those sections are
20//! called is the format's business and is in [`crate::format`].
21//!
22//! # The second names
23//!
24//! [`aliases`] is the same idea for what `__attribute__((alias("target")))` asks for, and is here
25//! for the same reason: `.set b, a` in a listing and a second symbol table entry in an object have
26//! to be saying the same thing. There is no image in one, which is what makes an alias free.
27//!
28//! # What is refused
29//!
30//! A thread-local variable. Reaching one is a call to `__tls_get_addr` or a load from the thread
31//! pointer depending on the model, none of which the back end builds yet, so a file with one in it
32//! is refused by name rather than written out as an ordinary variable that every thread would
33//! share.
34//!
35//! An ifunc, which is the other thing an alias in the IR can be. It is resolved once at program
36//! start by calling a function in the same object, which wants a symbol type and a relocation
37//! neither half of this writes yet.
38
39use rucc_base::Interner;
40use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module};
41use rucc_object::{Alias, Binding, Data, Object, Place, Reference, Reloc};
42
43use crate::Error;
44
45/// Every variable a module defines, laid out.
46#[derive(Debug, Clone, Default, PartialEq, Eq)]
47pub struct Globals {
48    /// One entry per definition, in the order the module held them. A declaration is not here,
49    /// because a file says nothing about a variable another file defines beyond the references
50    /// that name it, and those are already in the text.
51    pub vars: Vec<Variable>,
52}
53
54/// One global variable, as the pieces of its image and what the linker is told about it.
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Variable {
57    /// Its name, as the C program spelled it. The underscore an Apple symbol carries is added
58    /// when it is written down, because it is a fact about the object format and not about the
59    /// variable.
60    pub name: String,
61    /// How many bytes it occupies, which the pieces add up to.
62    pub size: u64,
63    /// What it has to be aligned to, always a power of two.
64    pub align: u64,
65    /// Which section it goes in.
66    pub place: Place,
67    /// How the linker sees the name.
68    pub binding: Binding,
69    /// Its image, in order.
70    pub pieces: Vec<Piece>,
71}
72
73/// As much of an image as one directive says.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum Piece {
76    /// That many zero bytes, which is the tail of a partly initialized array and the whole of a
77    /// variable with no initializer.
78    Zero(u64),
79    /// Those literal bytes, which is what a string literal and anything already laid out is.
80    Bytes(Vec<u8>),
81    /// One number, in the byte order the module was built for, as many bytes wide as its type.
82    Scalar(Vec<u8>),
83    /// The address of a symbol, which is a hole this compiler leaves and the linker fills.
84    Addr {
85        /// Whose address it is, as the C program spelled it.
86        symbol: String,
87        /// What to add to that address. `&array[2]` is the address of `array` plus eight.
88        addend: i64,
89        /// How many bytes it occupies.
90        bytes: u8,
91    },
92}
93
94impl Piece {
95    /// How many bytes it contributes to the image.
96    #[must_use]
97    pub fn size(&self) -> u64 {
98        match self {
99            Piece::Zero(bytes) => *bytes,
100            Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
101            Piece::Addr { bytes, .. } => u64::from(*bytes),
102        }
103    }
104}
105
106impl Globals {
107    /// The image of every variable, and where in each one the linker has to write an address.
108    ///
109    /// A variable in a section that carries no image contributes its size and none of its bytes,
110    /// which is what makes a program with a large zeroed array a small file.
111    #[must_use]
112    pub fn image(&self) -> Data {
113        let mut data = Data::default();
114        for var in &self.vars {
115            let mut object = Object {
116                name: var.name.clone(),
117                bytes: Vec::new(),
118                size: var.size,
119                align: var.align,
120                place: var.place.clone(),
121                binding: var.binding,
122                relocs: Vec::new(),
123            };
124            if matches!(var.place, Place::Zero | Place::Merged) {
125                data.objects.push(object);
126                continue;
127            }
128            for piece in &var.pieces {
129                match piece {
130                    Piece::Zero(bytes) => {
131                        object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
132                    }
133                    Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
134                        object.bytes.extend_from_slice(bytes);
135                    }
136                    Piece::Addr { symbol, addend, bytes } => {
137                        // The bytes are left zero rather than holding anything, because a linker
138                        // writes the whole hole from the addend and never reads what was there.
139                        object.relocs.push(Reloc {
140                            at: object.bytes.len(),
141                            symbol: symbol.clone(),
142                            kind: Reference::Address { bytes: *bytes },
143                            addend: *addend,
144                        });
145                        object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
146                    }
147                }
148            }
149            data.objects.push(object);
150        }
151        data
152    }
153}
154
155/// Every variable a module defines, laid out.
156///
157/// # Errors
158///
159/// [`Error::Thread`] for a thread-local variable, which is a program this compiler is behind on
160/// rather than a mistake, and [`Error::Image`] for a piece of an initializer nothing here can
161/// write down. See [`Error`].
162pub fn globals(module: &Module, names: &Interner) -> Result<Globals, Error> {
163    let mut out = Globals::default();
164    for id in module.globals() {
165        if module[id].is_declaration() {
166            continue;
167        }
168        out.vars.push(variable(module, names, id)?);
169    }
170    Ok(out)
171}
172
173/// Every second name a module gives something, in the order it gave them.
174///
175/// One walk for the same reason the one over the globals above is one: `.set b, a` in a listing
176/// and a second symbol table entry in an object have to be saying the same thing, and the way to
177/// be sure of that is for both of them to be reading the same list.
178///
179/// # Errors
180///
181/// [`Error::IFunc`] for an ifunc, which is the other thing this shape of the IR carries and is a
182/// program this compiler is behind on rather than a mistake. See [`Error`].
183pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
184    let mut out = Vec::new();
185    for id in module.aliases() {
186        let alias = &module[id];
187        let name = names.resolve(alias.name).to_owned();
188        if alias.kind != AliasKind::Alias {
189            return Err(Error::IFunc { name });
190        }
191        out.push(Alias {
192            name,
193            target: names.resolve(alias.target).to_owned(),
194            binding: binding(alias.linkage),
195        });
196    }
197    Ok(out)
198}
199
200/// One variable, laid out.
201fn variable(module: &Module, names: &Interner, id: GlobalId) -> Result<Variable, Error> {
202    let global = &module[id];
203    let name = names.resolve(global.name).to_owned();
204    if global.tls.is_some() {
205        return Err(Error::Thread { name });
206    }
207    let init = global.init.expect("a definition has an image");
208
209    let mut pieces = Vec::new();
210    let mut written = 0;
211    for datum in &module[init] {
212        let piece = match *datum {
213            Datum::Zero(bytes) => Piece::Zero(bytes),
214            Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
215            Datum::Scalar { ty, value } => {
216                if ty.lanes() != 1 {
217                    let why = format!("a {ty} in an initializer");
218                    return Err(Error::Image { name, why });
219                }
220                let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
221                let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
222                if !module.datalayout.little_endian {
223                    image.reverse();
224                }
225                Piece::Scalar(image)
226            }
227            Datum::Addr(idx) => {
228                let reloc = module[idx];
229                // Four and eight are the widths a machine has a relocation for and a directive
230                // for. Anything else is a module nothing here produced and neither half of the
231                // description could write down, so it is refused rather than rounded to one.
232                let bytes = match reloc.size {
233                    4 | 8 => reloc.size as u8,
234                    size => {
235                        let why = format!("an address {size} bytes wide");
236                        return Err(Error::Image { name, why });
237                    }
238                };
239                let symbol = names.resolve(reloc.symbol).to_owned();
240                Piece::Addr { symbol, addend: reloc.addend, bytes }
241            }
242        };
243        written += piece.size();
244        pieces.push(piece);
245    }
246    // An image shorter than the variable is the rest of an array nothing initialized, which the
247    // front end may leave off the end rather than write out as zeros it already said were there.
248    if written < global.size {
249        pieces.push(Piece::Zero(global.size - written));
250    }
251
252    let place = place(module, names, id, &pieces);
253    let size = global.size.max(written);
254    let binding = binding(global.linkage);
255    Ok(Variable { name, size, align: u64::from(global.align), place, binding, pieces })
256}
257
258/// What the linker is told about a name, from the linkage the module gave it.
259///
260/// Three of the five, because that is how many an object file can say. Which of the two weak ones
261/// a symbol had is a fact the optimizer needs and the linker does not, and a common one is a
262/// definition every other file may also make, which is a section rather than a binding.
263const fn binding(linkage: Linkage) -> Binding {
264    match linkage {
265        Linkage::Internal => Binding::Local,
266        Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
267        Linkage::External | Linkage::Common => Binding::Global,
268    }
269}
270
271/// Which section a variable goes in.
272///
273/// The program's answer when it gave one, and otherwise worked out from what the variable is. A
274/// tentative definition is asked of the linker rather than put anywhere, since the whole of what
275/// it says is that the variable exists and that some other file may say so too.
276fn place(module: &Module, names: &Interner, id: GlobalId, pieces: &[Piece]) -> Place {
277    let global = &module[id];
278    if let Some(section) = global.section {
279        return Place::Named(names.resolve(section).to_owned());
280    }
281    if global.linkage == Linkage::Common {
282        return Place::Merged;
283    }
284    if pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
285        return Place::Zero;
286    }
287    if global.constant {
288        return Place::ReadOnly;
289    }
290    Place::Written
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
298    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
299
300    /// A module for the one target every case here is written for.
301    fn module(names: &mut Interner) -> Module {
302        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
303        Module::new(names.intern("t.c"), &target)
304    }
305
306    /// A four byte variable with that image.
307    fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
308        let list = module.push_data(data);
309        let mut global = Global::new(names.intern(name), 4, 4);
310        global.init = Some(list);
311        module.add_global(global)
312    }
313
314    #[test]
315    fn a_declaration_is_not_a_variable_this_file_defines() {
316        let mut names = Interner::new();
317        let mut module = module(&mut names);
318        module.add_global(Global::new(names.intern("x"), 4, 4));
319        defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
320        let vars = globals(&module, &names).expect("a module of two globals").vars;
321        assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
322    }
323
324    #[test]
325    fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
326        let mut names = Interner::new();
327        let mut module = module(&mut names);
328        let value = module.add_imm(Imm::int(258, Type::int(32)));
329        defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
330        let vars = globals(&module, &names).expect("a module of one global").vars;
331        assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
332        // The low byte first, which is what this machine reads and is a fact about the module
333        // rather than about the variable.
334        assert_eq!(vars[0].pieces[0].size(), 4);
335    }
336
337    #[test]
338    fn what_a_variable_is_decides_which_section_it_goes_in() {
339        let mut names = Interner::new();
340        let mut module = module(&mut names);
341        let value = module.add_imm(Imm::int(1, Type::int(32)));
342        let scalar = Datum::Scalar { ty: Type::int(32), value };
343
344        let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
345        let written = defined(&mut module, &mut names, "written", &[scalar]);
346        let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
347        module[read_only].constant = true;
348        let named = defined(&mut module, &mut names, "named", &[scalar]);
349        module[named].section = Some(names.intern(".init_array"));
350        let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
351        module[merged].linkage = Linkage::Common;
352
353        let vars = globals(&module, &names).expect("a module of five globals").vars;
354        let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
355        assert_eq!(
356            places,
357            [
358                &Place::Zero,
359                &Place::Written,
360                &Place::ReadOnly,
361                &Place::Named(".init_array".to_owned()),
362                &Place::Merged,
363            ]
364        );
365        let _ = (zeroed, written);
366    }
367
368    #[test]
369    fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
370        let mut names = Interner::new();
371        let mut module = module(&mut names);
372        let value = module.add_imm(Imm::int(7, Type::int(8)));
373        let id =
374            defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
375        module[id].size = 4;
376        let vars = globals(&module, &names).expect("a module of one global").vars;
377        assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
378        assert_eq!(vars[0].size, 4);
379    }
380
381    #[test]
382    fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
383        let mut names = Interner::new();
384        let mut module = module(&mut names);
385        let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
386        let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
387        module[id].size = 8;
388        let vars = globals(&module, &names).expect("a module of one global").vars;
389        assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
390
391        let data = Globals { vars }.image();
392        assert_eq!(data.objects[0].bytes, vec![0; 8]);
393        assert_eq!(
394            data.objects[0].relocs,
395            [Reloc {
396                at: 0,
397                symbol: "y".to_owned(),
398                kind: Reference::Address { bytes: 8 },
399                addend: 16,
400            }]
401        );
402    }
403
404    #[test]
405    fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
406        let mut names = Interner::new();
407        let mut module = module(&mut names);
408        let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
409        module[id].size = 4096;
410        let data = globals(&module, &names).expect("a module of one global").image();
411        assert_eq!(data.objects[0].place, Place::Zero);
412        assert_eq!(data.objects[0].size, 4096);
413        // The point of the section: a program with a large zeroed array is a small file.
414        assert!(data.objects[0].bytes.is_empty());
415    }
416
417    #[test]
418    fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
419        let mut names = Interner::new();
420        let mut module = module(&mut names);
421        for (index, (linkage, binding)) in [
422            (Linkage::External, Binding::Global),
423            (Linkage::Internal, Binding::Local),
424            (Linkage::Weak, Binding::Weak),
425            (Linkage::LinkOnce, Binding::Weak),
426        ]
427        .into_iter()
428        .enumerate()
429        {
430            let name = format!("x{index}");
431            let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
432            module[id].linkage = linkage;
433            let vars = globals(&module, &names).expect("a module of globals").vars;
434            assert_eq!(vars[index].binding, binding, "{linkage:?}");
435        }
436    }
437
438    #[test]
439    fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
440        let mut names = Interner::new();
441        let mut module = module(&mut names);
442        let target = names.intern("a");
443        for (index, (linkage, binding)) in [
444            (Linkage::External, Binding::Global),
445            (Linkage::Internal, Binding::Local),
446            (Linkage::Weak, Binding::Weak),
447        ]
448        .into_iter()
449        .enumerate()
450        {
451            let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
452            alias.linkage = linkage;
453            module.add_alias(alias);
454            let written = aliases(&module, &names).expect("a module of aliases");
455            assert_eq!(written[index].binding, binding, "{linkage:?}");
456            assert_eq!(written[index].target, "a", "{linkage:?}");
457        }
458    }
459
460    /// A different job from a second name for something, and the wrong answer would be an alias
461    /// pointing at the resolver rather than at what the resolver picks.
462    #[test]
463    fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
464        let mut names = Interner::new();
465        let mut module = module(&mut names);
466        let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
467        memcpy.kind = AliasKind::IFunc;
468        module.add_alias(memcpy);
469        let error = aliases(&module, &names).expect_err("an ifunc");
470        assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
471    }
472
473    #[test]
474    fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
475        let mut names = Interner::new();
476        let mut module = module(&mut names);
477        let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
478        module[id].tls = Some(TlsModel::GlobalDynamic);
479        let error = globals(&module, &names).expect_err("a thread-local variable");
480        assert_eq!(error, Error::Thread { name: "x".to_owned() });
481    }
482}