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