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 on a format that is not ELF. ELF says one with a section flag and a
31//! symbol type, which is what [`place`] and [`crate::format`] write. Windows hands out an index at
32//! load time and reaches a variable through a table the index names, and Mach-O puts a descriptor
33//! in front of every one and reaches it by calling through the descriptor, so neither is this
34//! written a different way and both are refused by name rather than written out as an ordinary
35//! variable that every thread would share.
36//!
37//! An ifunc, which is the other thing an alias in the IR can be. It is resolved once at program
38//! start by calling a function in the same object, which wants a symbol type and a relocation
39//! neither half of this writes yet.
40
41use rucc_base::{Interner, Symbol};
42use rucc_ir as ir;
43use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module, SymbolRef};
44use rucc_object::{Alias, Binding, Data, Object, Place, Reference, Reloc, Visibility};
45use rucc_target::ObjectFormat;
46
47use crate::Error;
48
49/// Every variable a module defines, laid out.
50#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub struct Globals {
52    /// One entry per definition, in the order the module held them. A declaration is not here,
53    /// because a file says nothing about a variable another file defines beyond the references
54    /// that name it, and those are already in the text.
55    pub vars: Vec<Variable>,
56    /// Every name a declaration wrote `weak` on and this file does not define, in the order the
57    /// module held them.
58    ///
59    /// Not a definition and not bytes of anything, which is why it is a list of names beside the
60    /// variables rather than an entry among them. What it asks for is that the link be allowed to
61    /// leave the name undefined and hand every reference a zero address, which is how a library
62    /// offers a hook a profiler may fill in: the calls are written under `if (hook)` and the test
63    /// is false when nobody filled it in. Without it the link of a file that declares one fails
64    /// on an undefined symbol, which is what zstd's four tracing hooks do.
65    ///
66    /// Every one of them is here whether or not anything in the file refers to it, which is what
67    /// keeps the listing and the object saying the same thing: both read this list and neither
68    /// works out the answer for itself. gcc writes the directive only for the ones something
69    /// refers to, so a file that declares a hook and never calls it gets one undefined weak symbol
70    /// here that gcc does not put in. A linker has nothing to do about an undefined weak symbol
71    /// nothing refers to, which is why that difference is a difference and not a bug.
72    pub weak: Vec<String>,
73}
74
75/// One global variable, as the pieces of its image and what the linker is told about it.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct Variable {
78    /// Its name, as the C program spelled it. The underscore an Apple symbol carries is added
79    /// when it is written down, because it is a fact about the object format and not about the
80    /// variable.
81    pub name: String,
82    /// How many bytes it occupies, which the pieces add up to.
83    pub size: u64,
84    /// What it has to be aligned to, always a power of two.
85    pub align: u64,
86    /// Which section it goes in.
87    pub place: Place,
88    /// How the linker sees the name.
89    pub binding: Binding,
90    /// How far outside a shared library holding it the name reaches.
91    pub visibility: Visibility,
92    /// Its image, in order.
93    pub pieces: Vec<Piece>,
94}
95
96/// As much of an image as one directive says.
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum Piece {
99    /// That many zero bytes, which is the tail of a partly initialized array and the whole of a
100    /// variable with no initializer.
101    Zero(u64),
102    /// Those literal bytes, which is what a string literal and anything already laid out is.
103    Bytes(Vec<u8>),
104    /// One number, in the byte order the module was built for, as many bytes wide as its type.
105    Scalar(Vec<u8>),
106    /// The address of a symbol, which is a hole this compiler leaves and the linker fills.
107    Addr {
108        /// Whose address it is, as the C program spelled it.
109        symbol: String,
110        /// What to add to that address. `&array[2]` is the address of `array` plus eight.
111        addend: i64,
112        /// How many bytes it occupies.
113        bytes: u8,
114    },
115}
116
117impl Piece {
118    /// How many bytes it contributes to the image.
119    #[must_use]
120    pub fn size(&self) -> u64 {
121        match self {
122            Piece::Zero(bytes) => *bytes,
123            Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
124            Piece::Addr { bytes, .. } => u64::from(*bytes),
125        }
126    }
127}
128
129impl Globals {
130    /// The image of every variable, and where in each one the linker has to write an address.
131    ///
132    /// A variable in a section that carries no image contributes its size and none of its bytes,
133    /// which is what makes a program with a large zeroed array a small file.
134    #[must_use]
135    pub fn image(&self) -> Data {
136        let mut data = Data { weak: self.weak.clone(), ..Data::default() };
137        for var in &self.vars {
138            let mut object = Object {
139                name: var.name.clone(),
140                bytes: Vec::new(),
141                size: var.size,
142                align: var.align,
143                place: var.place.clone(),
144                binding: var.binding,
145                visibility: var.visibility,
146                relocs: Vec::new(),
147            };
148            if matches!(var.place, Place::Zero | Place::Merged | Place::Thread { zero: true }) {
149                data.objects.push(object);
150                continue;
151            }
152            for piece in &var.pieces {
153                match piece {
154                    Piece::Zero(bytes) => {
155                        object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
156                    }
157                    Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
158                        object.bytes.extend_from_slice(bytes);
159                    }
160                    Piece::Addr { symbol, addend, bytes } => {
161                        // The bytes are left zero rather than holding anything, because a linker
162                        // writes the whole hole from the addend and never reads what was there.
163                        object.relocs.push(Reloc {
164                            at: object.bytes.len(),
165                            symbol: symbol.clone(),
166                            kind: Reference::Address { bytes: *bytes },
167                            addend: *addend,
168                            // An image rather than an instruction, so there is nothing after the
169                            // hole for the question to be about.
170                            after: 0,
171                        });
172                        object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
173                    }
174                }
175            }
176            data.objects.push(object);
177        }
178        data
179    }
180}
181
182/// Every variable a module defines, laid out.
183///
184/// The format is an argument because one question here is the format's rather than the module's:
185/// a thread-local variable is a section flag and a symbol type on ELF and is neither of those on
186/// the other two, so which of them is being written decides whether there is anything to write.
187///
188/// # Errors
189///
190/// [`Error::Thread`] for a thread-local variable on a format that does not spell one this way,
191/// which is a program this compiler is behind on rather than a mistake, and [`Error::Image`] for a
192/// piece of an initializer nothing here can write down. See [`Error`].
193pub fn globals(module: &Module, names: &Interner, format: ObjectFormat) -> Result<Globals, Error> {
194    let mut out = Globals::default();
195    for id in module.globals() {
196        if module[id].is_declaration() {
197            continue;
198        }
199        out.vars.push(variable(module, names, id, format)?);
200    }
201    // The other half, which is names and no bytes. A declaration is not a variable and has no
202    // image, so it is skipped above and picked up here, and only the weak ones are: an ordinary
203    // undefined name needs nothing said about it, since a reference to one is already an
204    // undefined symbol and a link that cannot resolve it is a link that should fail.
205    //
206    // The functions as well as the objects, and the functions are the ones a program actually
207    // writes: a hook a library offers is a function, and `if (hook)` around the call is the test
208    // that reads the zero address a weak reference gets. Both walks are here rather than one in
209    // each caller, for the reason the walk over the definitions above is one walk.
210    for id in module.funcs() {
211        let func = &module[id];
212        if func.is_declaration() && func.linkage == Linkage::Weak {
213            out.weak.push(names.resolve(func.name).to_owned());
214        }
215    }
216    for id in module.globals() {
217        let global = &module[id];
218        if global.is_declaration() && global.linkage == Linkage::Weak {
219            out.weak.push(names.resolve(global.name).to_owned());
220        }
221    }
222    Ok(out)
223}
224
225/// Every second name a module gives something, in the order it gave them.
226///
227/// One walk for the same reason the one over the globals above is one: `.set b, a` in a listing
228/// and a second symbol table entry in an object have to be saying the same thing, and the way to
229/// be sure of that is for both of them to be reading the same list.
230///
231/// # Errors
232///
233/// [`Error::IFunc`] for an ifunc, which is the other thing this shape of the IR carries and is a
234/// program this compiler is behind on rather than a mistake. See [`Error`].
235pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
236    let mut out = Vec::new();
237    for id in module.aliases() {
238        let alias = &module[id];
239        let name = names.resolve(alias.name).to_owned();
240        if alias.kind != AliasKind::Alias {
241            return Err(Error::IFunc { name });
242        }
243        out.push(Alias {
244            name,
245            target: names.resolve(alias.target).to_owned(),
246            binding: binding(alias.linkage),
247            visibility: visibility(alias.visibility),
248        });
249    }
250    Ok(out)
251}
252
253/// One variable, laid out.
254fn variable(
255    module: &Module,
256    names: &Interner,
257    id: GlobalId,
258    format: ObjectFormat,
259) -> Result<Variable, Error> {
260    let global = &module[id];
261    let name = names.resolve(global.name).to_owned();
262    if global.tls.is_some() && format != ObjectFormat::Elf {
263        return Err(Error::Thread { name, format: format.as_str() });
264    }
265    let init = global.init.expect("a definition has an image");
266
267    let mut pieces = Vec::new();
268    // The names the image holds the addresses of, kept as symbols rather than read back off the
269    // pieces, because whether one of them is defined here is a question about this module and the
270    // pieces carry the spelling rather than the name.
271    let mut addrs = Vec::new();
272    let mut written = 0;
273    for datum in &module[init] {
274        let piece = match *datum {
275            Datum::Zero(bytes) => Piece::Zero(bytes),
276            Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
277            Datum::Scalar { ty, value } => {
278                if ty.lanes() != 1 {
279                    let why = format!("a {ty} in an initializer");
280                    return Err(Error::Image { name, why });
281                }
282                let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
283                let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
284                if !module.datalayout.little_endian {
285                    image.reverse();
286                }
287                Piece::Scalar(image)
288            }
289            Datum::Addr(idx) => {
290                let reloc = module[idx];
291                // Four and eight are the widths a machine has a relocation for and a directive
292                // for. Anything else is a module nothing here produced and neither half of the
293                // description could write down, so it is refused rather than rounded to one.
294                let bytes = match reloc.size {
295                    4 | 8 => reloc.size as u8,
296                    size => {
297                        let why = format!("an address {size} bytes wide");
298                        return Err(Error::Image { name, why });
299                    }
300                };
301                let symbol = names.resolve(reloc.symbol).to_owned();
302                addrs.push(reloc.symbol);
303                Piece::Addr { symbol, addend: reloc.addend, bytes }
304            }
305        };
306        written += piece.size();
307        pieces.push(piece);
308    }
309    // An image shorter than the variable is the rest of an array nothing initialized, which the
310    // front end may leave off the end rather than write out as zeros it already said were there.
311    if written < global.size {
312        pieces.push(Piece::Zero(global.size - written));
313    }
314
315    let place = place(module, names, id, &pieces, &addrs);
316    let size = global.size.max(written);
317    let binding = binding(global.linkage);
318    let visibility = visibility(global.visibility);
319    Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
320}
321
322/// What the linker is told about a name, from the linkage the module gave it.
323///
324/// Three of the five, because that is how many an object file can say. Which of the two weak ones
325/// a symbol had is a fact the optimizer needs and the linker does not, and a common one is a
326/// definition every other file may also make, which is a section rather than a binding.
327const fn binding(linkage: Linkage) -> Binding {
328    match linkage {
329        Linkage::Internal => Binding::Local,
330        Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
331        Linkage::External | Linkage::Common => Binding::Global,
332    }
333}
334
335/// What the dynamic linker is told about a name, from the visibility the module gave it.
336///
337/// All three, because ELF says all three, and the two enumerations are the same three answers
338/// written once in a crate that is not allowed to know what an object file is and once in one
339/// that is.
340const fn visibility(visibility: ir::Visibility) -> Visibility {
341    match visibility {
342        ir::Visibility::Default => Visibility::Default,
343        ir::Visibility::Hidden => Visibility::Hidden,
344        ir::Visibility::Protected => Visibility::Protected,
345    }
346}
347
348/// Which section a variable goes in.
349///
350/// The program's answer when it gave one, and otherwise worked out from what the variable is. A
351/// tentative definition is asked of the linker rather than put anywhere, since the whole of what
352/// it says is that the variable exists and that some other file may say so too.
353///
354/// Being constant is not on its own enough to put a variable in a section nothing may ever write.
355/// An image holding the address of something is an image the loader has to write, because an
356/// address is not a number a link knows when everything it links may be moved. So the question
357/// asked of a constant variable is whether its image holds an address, and one that does goes in
358/// the section that is writable for exactly as long as the loader needs it to be.
359///
360/// A variable with no image at all is not zero filled, it is empty, and the two differ. An `asm`
361/// at file scope writes one whenever it puts a label at the end of what it just wrote, and what
362/// that label means is the address after those bytes, so it belongs in the section those bytes
363/// went into. Sending it to the section of zeros instead would move it away from the run it was
364/// written to mark the end of, and the distance a program reads off it would be a different
365/// distance.
366fn place(
367    module: &Module,
368    names: &Interner,
369    id: GlobalId,
370    pieces: &[Piece],
371    addrs: &[Symbol],
372) -> Place {
373    let global = &module[id];
374    // First, because a thread-local variable has to be in one of the two sections a thread gets a
375    // copy of whatever else is true of it. It is never merged, since what `.comm` asks the linker
376    // for is one piece of zeroed space and this wants one per thread, and it is never read only,
377    // since the copy is made by writing it.
378    if global.tls.is_some() {
379        let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
380        return Place::Thread { zero };
381    }
382    if let Some(section) = global.section {
383        return Place::Named(names.resolve(section).to_owned());
384    }
385    if global.linkage == Linkage::Common {
386        return Place::Merged;
387    }
388    if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
389        return Place::Zero;
390    }
391    if global.constant {
392        return match addrs {
393            [] => Place::ReadOnly,
394            _ => Place::RelocReadOnly {
395                local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
396            },
397        };
398    }
399    Place::Written
400}
401
402/// Whether that name is one this file both defines and keeps to itself.
403///
404/// Both halves matter. A name this file does not define is one the link resolves from somewhere
405/// else, and a name this file exports is one another object may define instead, so neither is an
406/// address the first pages of the relocated segment can be laid out around.
407fn resolved_here(module: &Module, symbol: Symbol) -> bool {
408    match module.lookup(symbol) {
409        Some(SymbolRef::Func(id)) => {
410            module[id].linkage == Linkage::Internal && !module[id].is_declaration()
411        }
412        Some(SymbolRef::Global(id)) => {
413            module[id].linkage == Linkage::Internal && !module[id].is_declaration()
414        }
415        Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
416        None => false,
417    }
418}
419
420#[cfg(test)]
421mod tests {
422    use super::*;
423
424    use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
425    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
426
427    /// A module for the one target every case here is written for.
428    fn module(names: &mut Interner) -> Module {
429        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
430        Module::new(names.intern("t.c"), &target)
431    }
432
433    /// A four byte variable with that image.
434    fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
435        let list = module.push_data(data);
436        let mut global = Global::new(names.intern(name), 4, 4);
437        global.init = Some(list);
438        module.add_global(global)
439    }
440
441    #[test]
442    fn a_declaration_is_not_a_variable_this_file_defines() {
443        let mut names = Interner::new();
444        let mut module = module(&mut names);
445        module.add_global(Global::new(names.intern("x"), 4, 4));
446        defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
447        let vars =
448            globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
449        assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
450    }
451
452    /// A variable's visibility comes through the layout and out the other side of the image.
453    ///
454    /// Two hops rather than one, because a variable is laid out here and then turned into an
455    /// object for the writer a hundred lines further up, and a field that survives the first and
456    /// not the second is a field the object file never hears about.
457    #[test]
458    fn the_visibility_a_variable_asked_for_reaches_the_image() {
459        for (asked, wanted) in [
460            (ir::Visibility::Default, Visibility::Default),
461            (ir::Visibility::Hidden, Visibility::Hidden),
462            (ir::Visibility::Protected, Visibility::Protected),
463        ] {
464            let mut names = Interner::new();
465            let mut module = module(&mut names);
466            let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
467            module[id].visibility = asked;
468            let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
469            assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
470            assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
471        }
472    }
473
474    #[test]
475    fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
476        let mut names = Interner::new();
477        let mut module = module(&mut names);
478        let value = module.add_imm(Imm::int(258, Type::int(32)));
479        defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
480        let vars =
481            globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
482        assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
483        // The low byte first, which is what this machine reads and is a fact about the module
484        // rather than about the variable.
485        assert_eq!(vars[0].pieces[0].size(), 4);
486    }
487
488    #[test]
489    fn what_a_variable_is_decides_which_section_it_goes_in() {
490        let mut names = Interner::new();
491        let mut module = module(&mut names);
492        let value = module.add_imm(Imm::int(1, Type::int(32)));
493        let scalar = Datum::Scalar { ty: Type::int(32), value };
494
495        let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
496        let written = defined(&mut module, &mut names, "written", &[scalar]);
497        let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
498        module[read_only].constant = true;
499        let named = defined(&mut module, &mut names, "named", &[scalar]);
500        module[named].section = Some(names.intern(".init_array"));
501        let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
502        module[merged].linkage = Linkage::Common;
503
504        let vars =
505            globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
506        let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
507        assert_eq!(
508            places,
509            [
510                &Place::Zero,
511                &Place::Written,
512                &Place::ReadOnly,
513                &Place::Named(".init_array".to_owned()),
514                &Place::Merged,
515            ]
516        );
517        let _ = (zeroed, written);
518    }
519
520    /// A constant holding an address goes where the loader may write it once, not in `.rodata`.
521    ///
522    /// Three of them, because the question has three answers. One whose address is of something
523    /// this file defines and keeps to itself is local, one whose address is of a name this file
524    /// only declares is not, and one that mixes the two is not either, since it takes only one
525    /// name the link resolves from elsewhere to spoil it. The fourth is the constant with no
526    /// address in it at all, which is the case that has to keep going where it went before.
527    #[test]
528    fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
529        let mut names = Interner::new();
530        let mut module = module(&mut names);
531        let value = module.add_imm(Imm::int(1, Type::int(32)));
532
533        let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
534        module[mine].linkage = Linkage::Internal;
535        let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
536
537        let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
538        let to_theirs =
539            module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
540
541        let plain = defined(
542            &mut module,
543            &mut names,
544            "plain",
545            &[Datum::Scalar { ty: Type::int(32), value }],
546        );
547        module[plain].constant = true;
548        let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
549        module[local].constant = true;
550        module[local].size = 8;
551        let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
552        module[far].constant = true;
553        module[far].size = 8;
554        let both = defined(
555            &mut module,
556            &mut names,
557            "both",
558            &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
559        );
560        module[both].constant = true;
561        module[both].size = 16;
562
563        let vars =
564            globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
565        let places: Vec<(&str, &Place)> =
566            vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
567        assert_eq!(
568            places,
569            [
570                ("mine", &Place::Zero),
571                ("plain", &Place::ReadOnly),
572                ("local", &Place::RelocReadOnly { local: true }),
573                ("far", &Place::RelocReadOnly { local: false }),
574                ("both", &Place::RelocReadOnly { local: false }),
575            ]
576        );
577    }
578
579    #[test]
580    fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
581        let mut names = Interner::new();
582        let mut module = module(&mut names);
583        let value = module.add_imm(Imm::int(7, Type::int(8)));
584        let id =
585            defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
586        module[id].size = 4;
587        let vars =
588            globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
589        assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
590        assert_eq!(vars[0].size, 4);
591    }
592
593    #[test]
594    fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
595        let mut names = Interner::new();
596        let mut module = module(&mut names);
597        let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
598        let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
599        module[id].size = 8;
600        let vars =
601            globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
602        assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
603
604        let data = Globals { vars, weak: Vec::new() }.image();
605        assert_eq!(data.objects[0].bytes, vec![0; 8]);
606        assert_eq!(
607            data.objects[0].relocs,
608            [Reloc {
609                at: 0,
610                symbol: "y".to_owned(),
611                kind: Reference::Address { bytes: 8 },
612                addend: 16,
613                after: 0,
614            }]
615        );
616    }
617
618    #[test]
619    fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
620        let mut names = Interner::new();
621        let mut module = module(&mut names);
622        let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
623        module[id].size = 4096;
624        let data =
625            globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
626        assert_eq!(data.objects[0].place, Place::Zero);
627        assert_eq!(data.objects[0].size, 4096);
628        // The point of the section: a program with a large zeroed array is a small file.
629        assert!(data.objects[0].bytes.is_empty());
630    }
631
632    #[test]
633    fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
634        let mut names = Interner::new();
635        let mut module = module(&mut names);
636        for (index, (linkage, binding)) in [
637            (Linkage::External, Binding::Global),
638            (Linkage::Internal, Binding::Local),
639            (Linkage::Weak, Binding::Weak),
640            (Linkage::LinkOnce, Binding::Weak),
641        ]
642        .into_iter()
643        .enumerate()
644        {
645            let name = format!("x{index}");
646            let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
647            module[id].linkage = linkage;
648            let vars =
649                globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
650            assert_eq!(vars[index].binding, binding, "{linkage:?}");
651        }
652    }
653
654    #[test]
655    fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
656        let mut names = Interner::new();
657        let mut module = module(&mut names);
658        let target = names.intern("a");
659        for (index, (linkage, binding)) in [
660            (Linkage::External, Binding::Global),
661            (Linkage::Internal, Binding::Local),
662            (Linkage::Weak, Binding::Weak),
663        ]
664        .into_iter()
665        .enumerate()
666        {
667            let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
668            alias.linkage = linkage;
669            module.add_alias(alias);
670            let written = aliases(&module, &names).expect("a module of aliases");
671            assert_eq!(written[index].binding, binding, "{linkage:?}");
672            assert_eq!(written[index].target, "a", "{linkage:?}");
673        }
674    }
675
676    /// A different job from a second name for something, and the wrong answer would be an alias
677    /// pointing at the resolver rather than at what the resolver picks.
678    #[test]
679    fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
680        let mut names = Interner::new();
681        let mut module = module(&mut names);
682        let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
683        memcpy.kind = AliasKind::IFunc;
684        module.add_alias(memcpy);
685        let error = aliases(&module, &names).expect_err("an ifunc");
686        assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
687    }
688
689    /// The two sections a thread gets a copy of, told apart the way `.data` and `.bss` are.
690    #[test]
691    fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
692        let mut names = Interner::new();
693        let mut module = module(&mut names);
694        let value = module.add_imm(Imm::int(1, Type::int(32)));
695        let written = defined(
696            &mut module,
697            &mut names,
698            "counted",
699            &[Datum::Scalar { ty: Type::int(32), value }],
700        );
701        module[written].tls = Some(TlsModel::GlobalDynamic);
702        let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
703        module[zeroed].tls = Some(TlsModel::GlobalDynamic);
704
705        let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
706        assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
707        assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
708    }
709
710    /// Being read only loses to being thread-local, because the copy is made by writing it.
711    #[test]
712    fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
713        let mut names = Interner::new();
714        let mut module = module(&mut names);
715        let value = module.add_imm(Imm::int(1, Type::int(32)));
716        let id =
717            defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
718        module[id].tls = Some(TlsModel::GlobalDynamic);
719        module[id].constant = true;
720
721        let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
722        assert_eq!(vars[0].place, Place::Thread { zero: false });
723    }
724
725    /// The image of a thread-local whose image is all zeros costs the file nothing, the same as
726    /// `.bss` does, and the one that is not all zeros carries its bytes.
727    #[test]
728    fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
729        let mut names = Interner::new();
730        let mut module = module(&mut names);
731        let value = module.add_imm(Imm::int(258, Type::int(32)));
732        let written = defined(
733            &mut module,
734            &mut names,
735            "counted",
736            &[Datum::Scalar { ty: Type::int(32), value }],
737        );
738        module[written].tls = Some(TlsModel::GlobalDynamic);
739        let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
740        module[zeroed].tls = Some(TlsModel::GlobalDynamic);
741
742        let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
743        assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
744        assert_eq!(data.objects[0].size, 4);
745        assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
746        assert_eq!(data.objects[1].size, 4);
747    }
748
749    /// Windows and Mach-O reach a thread-local through a table and through a descriptor, neither
750    /// of which is a section with a flag on it, so the variable is refused by name there.
751    #[test]
752    fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
753        for format in [ObjectFormat::MachO, ObjectFormat::Coff] {
754            let mut names = Interner::new();
755            let mut module = module(&mut names);
756            let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
757            module[id].tls = Some(TlsModel::GlobalDynamic);
758            let error = globals(&module, &names, format).expect_err("a thread-local variable");
759            assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
760        }
761    }
762}