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