1use rucc_base::{Interner, Symbol};
42use rucc_ir as ir;
43use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module, SymbolRef};
44use rucc_object::{Alias, Apart, Binding, Data, Object, Place, Reference, Reloc, Visibility};
45use rucc_target::ObjectFormat;
46
47use crate::Error;
48
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub struct Globals {
52 pub vars: Vec<Variable>,
56 pub weak: Vec<String>,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct Variable {
78 pub name: String,
82 pub size: u64,
84 pub align: u64,
86 pub place: Place,
88 pub binding: Binding,
90 pub visibility: Visibility,
92 pub pieces: Vec<Piece>,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum Piece {
99 Zero(u64),
102 Bytes(Vec<u8>),
104 Scalar(Vec<u8>),
106 Addr {
108 symbol: String,
110 addend: i64,
112 bytes: u8,
114 },
115 Away {
118 symbol: String,
120 addend: i64,
123 },
124 Apart {
127 to: String,
129 from: String,
131 addend: i64,
133 bytes: u8,
135 },
136}
137
138impl Piece {
139 #[must_use]
141 pub fn size(&self) -> u64 {
142 match self {
143 Piece::Zero(bytes) => *bytes,
144 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
145 Piece::Addr { bytes, .. } | Piece::Apart { bytes, .. } => u64::from(*bytes),
146 Piece::Away { .. } => 4,
147 }
148 }
149}
150
151impl Globals {
152 #[must_use]
157 pub fn image(&self) -> Data {
158 let mut data = Data { weak: self.weak.clone(), ..Data::default() };
159 for var in &self.vars {
160 let mut object = Object {
161 name: var.name.clone(),
162 bytes: Vec::new(),
163 size: var.size,
164 align: var.align,
165 place: var.place.clone(),
166 binding: var.binding,
167 visibility: var.visibility,
168 relocs: Vec::new(),
169 };
170 if matches!(var.place, Place::Zero | Place::Merged | Place::Thread { zero: true }) {
171 data.objects.push(object);
172 continue;
173 }
174 for piece in &var.pieces {
175 match piece {
176 Piece::Zero(bytes) => {
177 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
178 }
179 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
180 object.bytes.extend_from_slice(bytes);
181 }
182 Piece::Addr { symbol, addend, bytes } => {
183 object.relocs.push(Reloc {
186 at: object.bytes.len(),
187 symbol: symbol.clone(),
188 kind: Reference::Address { bytes: *bytes },
189 addend: *addend,
190 after: 0,
193 });
194 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
195 }
196 Piece::Away { symbol, addend } => {
197 object.relocs.push(Reloc {
198 at: object.bytes.len(),
199 symbol: symbol.clone(),
200 kind: Reference::Away,
201 addend: *addend,
202 after: 0,
203 });
204 object.bytes.resize(object.bytes.len() + 4, 0);
205 }
206 Piece::Apart { to, from, addend, bytes } => {
207 data.apart.push(Apart {
208 object: data.objects.len(),
209 at: object.bytes.len(),
210 to: to.clone(),
211 from: from.clone(),
212 addend: *addend,
213 bytes: *bytes,
214 });
215 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
216 }
217 }
218 }
219 data.objects.push(object);
220 }
221 data
222 }
223}
224
225pub fn globals(module: &Module, names: &Interner, format: ObjectFormat) -> Result<Globals, Error> {
237 let mut out = Globals::default();
238 for id in module.globals() {
239 if module[id].is_declaration() {
240 continue;
241 }
242 out.vars.push(variable(module, names, id, format)?);
243 }
244 for id in module.funcs() {
254 let func = &module[id];
255 if func.is_declaration() && func.linkage == Linkage::Weak {
256 out.weak.push(names.resolve(func.name).to_owned());
257 }
258 }
259 for id in module.globals() {
260 let global = &module[id];
261 if global.is_declaration() && global.linkage == Linkage::Weak {
262 out.weak.push(names.resolve(global.name).to_owned());
263 }
264 }
265 Ok(out)
266}
267
268pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
279 let mut out = Vec::new();
280 for id in module.aliases() {
281 let alias = &module[id];
282 let name = names.resolve(alias.name).to_owned();
283 if alias.kind != AliasKind::Alias {
284 return Err(Error::IFunc { name });
285 }
286 out.push(Alias {
287 name,
288 target: names.resolve(alias.target).to_owned(),
289 binding: binding(alias.linkage),
290 visibility: visibility(alias.visibility),
291 });
292 }
293 Ok(out)
294}
295
296fn variable(
298 module: &Module,
299 names: &Interner,
300 id: GlobalId,
301 format: ObjectFormat,
302) -> Result<Variable, Error> {
303 let global = &module[id];
304 let name = names.resolve(global.name).to_owned();
305 if global.tls.is_some() && format != ObjectFormat::Elf {
306 return Err(Error::Thread { name, format: format.as_str() });
307 }
308 let init = global.init.expect("a definition has an image");
309
310 let mut pieces = Vec::new();
311 let mut addrs = Vec::new();
315 let mut written = 0;
316 for datum in &module[init] {
317 let piece = match *datum {
318 Datum::Zero(bytes) => Piece::Zero(bytes),
319 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
320 Datum::Scalar { ty, value } => {
321 if ty.lanes() != 1 {
322 let why = format!("a {ty} in an initializer");
323 return Err(Error::Image { name, why });
324 }
325 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
326 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
327 if !module.datalayout.little_endian {
328 image.reverse();
329 }
330 Piece::Scalar(image)
331 }
332 Datum::Away(idx) => {
337 let reloc = module[idx];
338 if reloc.size != 4 {
339 let why = format!("a distance {} bytes wide", reloc.size);
340 return Err(Error::Image { name, why });
341 }
342 let symbol = names.resolve(reloc.symbol).to_owned();
343 Piece::Away { symbol, addend: reloc.addend }
344 }
345 Datum::Apart { to, from } => {
348 let reloc = module[to];
349 let bytes = match reloc.size {
350 1 | 2 | 4 | 8 => reloc.size as u8,
351 size => {
352 let why = format!("a distance {size} bytes wide");
353 return Err(Error::Image { name, why });
354 }
355 };
356 let to = names.resolve(reloc.symbol).to_owned();
357 let from = names.resolve(from).to_owned();
358 Piece::Apart { to, from, addend: reloc.addend, bytes }
359 }
360 Datum::Addr(idx) => {
361 let reloc = module[idx];
362 let bytes = match reloc.size {
366 4 | 8 => reloc.size as u8,
367 size => {
368 let why = format!("an address {size} bytes wide");
369 return Err(Error::Image { name, why });
370 }
371 };
372 let symbol = names.resolve(reloc.symbol).to_owned();
373 addrs.push(reloc.symbol);
374 Piece::Addr { symbol, addend: reloc.addend, bytes }
375 }
376 };
377 written += piece.size();
378 pieces.push(piece);
379 }
380 if written < global.size {
383 pieces.push(Piece::Zero(global.size - written));
384 }
385
386 let place = place(module, names, id, &pieces, &addrs);
387 let size = global.size.max(written);
388 let binding = binding(global.linkage);
389 let visibility = visibility(global.visibility);
390 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
391}
392
393const fn binding(linkage: Linkage) -> Binding {
399 match linkage {
400 Linkage::Internal => Binding::Local,
401 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
402 Linkage::External | Linkage::Common => Binding::Global,
403 }
404}
405
406const fn visibility(visibility: ir::Visibility) -> Visibility {
412 match visibility {
413 ir::Visibility::Default => Visibility::Default,
414 ir::Visibility::Hidden => Visibility::Hidden,
415 ir::Visibility::Protected => Visibility::Protected,
416 }
417}
418
419fn place(
438 module: &Module,
439 names: &Interner,
440 id: GlobalId,
441 pieces: &[Piece],
442 addrs: &[Symbol],
443) -> Place {
444 let global = &module[id];
445 if global.tls.is_some() {
450 let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
451 return Place::Thread { zero };
452 }
453 if let Some(section) = global.section {
454 return Place::Named(names.resolve(section).to_owned());
455 }
456 if global.linkage == Linkage::Common {
457 return Place::Merged;
458 }
459 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
460 return Place::Zero;
461 }
462 if global.constant {
463 return match addrs {
464 [] => Place::ReadOnly,
465 _ => Place::RelocReadOnly {
466 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
467 },
468 };
469 }
470 Place::Written
471}
472
473fn resolved_here(module: &Module, symbol: Symbol) -> bool {
479 match module.lookup(symbol) {
480 Some(SymbolRef::Func(id)) => {
481 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
482 }
483 Some(SymbolRef::Global(id)) => {
484 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
485 }
486 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
487 None => false,
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
496 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
497
498 fn module(names: &mut Interner) -> Module {
500 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
501 Module::new(names.intern("t.c"), &target)
502 }
503
504 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
506 let list = module.push_data(data);
507 let mut global = Global::new(names.intern(name), 4, 4);
508 global.init = Some(list);
509 module.add_global(global)
510 }
511
512 #[test]
513 fn a_declaration_is_not_a_variable_this_file_defines() {
514 let mut names = Interner::new();
515 let mut module = module(&mut names);
516 module.add_global(Global::new(names.intern("x"), 4, 4));
517 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
518 let vars =
519 globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
520 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
521 }
522
523 #[test]
529 fn the_visibility_a_variable_asked_for_reaches_the_image() {
530 for (asked, wanted) in [
531 (ir::Visibility::Default, Visibility::Default),
532 (ir::Visibility::Hidden, Visibility::Hidden),
533 (ir::Visibility::Protected, Visibility::Protected),
534 ] {
535 let mut names = Interner::new();
536 let mut module = module(&mut names);
537 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
538 module[id].visibility = asked;
539 let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
540 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
541 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
542 }
543 }
544
545 #[test]
546 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
547 let mut names = Interner::new();
548 let mut module = module(&mut names);
549 let value = module.add_imm(Imm::int(258, Type::int(32)));
550 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
551 let vars =
552 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
553 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
554 assert_eq!(vars[0].pieces[0].size(), 4);
557 }
558
559 #[test]
560 fn what_a_variable_is_decides_which_section_it_goes_in() {
561 let mut names = Interner::new();
562 let mut module = module(&mut names);
563 let value = module.add_imm(Imm::int(1, Type::int(32)));
564 let scalar = Datum::Scalar { ty: Type::int(32), value };
565
566 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
567 let written = defined(&mut module, &mut names, "written", &[scalar]);
568 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
569 module[read_only].constant = true;
570 let named = defined(&mut module, &mut names, "named", &[scalar]);
571 module[named].section = Some(names.intern(".init_array"));
572 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
573 module[merged].linkage = Linkage::Common;
574
575 let vars =
576 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
577 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
578 assert_eq!(
579 places,
580 [
581 &Place::Zero,
582 &Place::Written,
583 &Place::ReadOnly,
584 &Place::Named(".init_array".to_owned()),
585 &Place::Merged,
586 ]
587 );
588 let _ = (zeroed, written);
589 }
590
591 #[test]
599 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
600 let mut names = Interner::new();
601 let mut module = module(&mut names);
602 let value = module.add_imm(Imm::int(1, Type::int(32)));
603
604 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
605 module[mine].linkage = Linkage::Internal;
606 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
607
608 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
609 let to_theirs =
610 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
611
612 let plain = defined(
613 &mut module,
614 &mut names,
615 "plain",
616 &[Datum::Scalar { ty: Type::int(32), value }],
617 );
618 module[plain].constant = true;
619 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
620 module[local].constant = true;
621 module[local].size = 8;
622 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
623 module[far].constant = true;
624 module[far].size = 8;
625 let both = defined(
626 &mut module,
627 &mut names,
628 "both",
629 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
630 );
631 module[both].constant = true;
632 module[both].size = 16;
633
634 let vars =
635 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
636 let places: Vec<(&str, &Place)> =
637 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
638 assert_eq!(
639 places,
640 [
641 ("mine", &Place::Zero),
642 ("plain", &Place::ReadOnly),
643 ("local", &Place::RelocReadOnly { local: true }),
644 ("far", &Place::RelocReadOnly { local: false }),
645 ("both", &Place::RelocReadOnly { local: false }),
646 ]
647 );
648 }
649
650 #[test]
651 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
652 let mut names = Interner::new();
653 let mut module = module(&mut names);
654 let value = module.add_imm(Imm::int(7, Type::int(8)));
655 let id =
656 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
657 module[id].size = 4;
658 let vars =
659 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
660 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
661 assert_eq!(vars[0].size, 4);
662 }
663
664 #[test]
665 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
666 let mut names = Interner::new();
667 let mut module = module(&mut names);
668 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
669 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
670 module[id].size = 8;
671 let vars =
672 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
673 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
674
675 let data = Globals { vars, weak: Vec::new() }.image();
676 assert_eq!(data.objects[0].bytes, vec![0; 8]);
677 assert_eq!(
678 data.objects[0].relocs,
679 [Reloc {
680 at: 0,
681 symbol: "y".to_owned(),
682 kind: Reference::Address { bytes: 8 },
683 addend: 16,
684 after: 0,
685 }]
686 );
687 }
688
689 #[test]
690 fn a_variable_holding_a_distance_is_a_hole_the_linker_measures_from_where_it_is() {
691 let mut names = Interner::new();
692 let mut module = module(&mut names);
693 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 1, size: 4 });
694 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
695 module[id].size = 4;
696 module[id].constant = true;
699 let vars =
700 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
701 assert_eq!(vars[0].pieces, [Piece::Away { symbol: "y".to_owned(), addend: 1 }]);
702 assert_eq!(vars[0].place, Place::ReadOnly);
703
704 let data = Globals { vars, weak: Vec::new() }.image();
705 assert_eq!(data.objects[0].bytes, vec![0; 4]);
706 assert_eq!(
707 data.objects[0].relocs,
708 [Reloc { at: 0, symbol: "y".to_owned(), kind: Reference::Away, addend: 1, after: 0 }]
709 );
710 }
711
712 #[test]
713 fn a_distance_of_a_width_no_relocation_writes_is_refused_by_the_width_it_asked_for() {
714 let mut names = Interner::new();
715 let mut module = module(&mut names);
716 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 0, size: 8 });
717 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
718 module[id].size = 8;
719 let failed = globals(&module, &names, ObjectFormat::Elf).expect_err("a distance that wide");
720 assert_eq!(
721 failed,
722 Error::Image { name: "d".to_owned(), why: "a distance 8 bytes wide".to_owned() }
723 );
724 }
725
726 #[test]
727 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
728 let mut names = Interner::new();
729 let mut module = module(&mut names);
730 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
731 module[id].size = 4096;
732 let data =
733 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
734 assert_eq!(data.objects[0].place, Place::Zero);
735 assert_eq!(data.objects[0].size, 4096);
736 assert!(data.objects[0].bytes.is_empty());
738 }
739
740 #[test]
741 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
742 let mut names = Interner::new();
743 let mut module = module(&mut names);
744 for (index, (linkage, binding)) in [
745 (Linkage::External, Binding::Global),
746 (Linkage::Internal, Binding::Local),
747 (Linkage::Weak, Binding::Weak),
748 (Linkage::LinkOnce, Binding::Weak),
749 ]
750 .into_iter()
751 .enumerate()
752 {
753 let name = format!("x{index}");
754 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
755 module[id].linkage = linkage;
756 let vars =
757 globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
758 assert_eq!(vars[index].binding, binding, "{linkage:?}");
759 }
760 }
761
762 #[test]
763 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
764 let mut names = Interner::new();
765 let mut module = module(&mut names);
766 let target = names.intern("a");
767 for (index, (linkage, binding)) in [
768 (Linkage::External, Binding::Global),
769 (Linkage::Internal, Binding::Local),
770 (Linkage::Weak, Binding::Weak),
771 ]
772 .into_iter()
773 .enumerate()
774 {
775 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
776 alias.linkage = linkage;
777 module.add_alias(alias);
778 let written = aliases(&module, &names).expect("a module of aliases");
779 assert_eq!(written[index].binding, binding, "{linkage:?}");
780 assert_eq!(written[index].target, "a", "{linkage:?}");
781 }
782 }
783
784 #[test]
787 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
788 let mut names = Interner::new();
789 let mut module = module(&mut names);
790 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
791 memcpy.kind = AliasKind::IFunc;
792 module.add_alias(memcpy);
793 let error = aliases(&module, &names).expect_err("an ifunc");
794 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
795 }
796
797 #[test]
799 fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
800 let mut names = Interner::new();
801 let mut module = module(&mut names);
802 let value = module.add_imm(Imm::int(1, Type::int(32)));
803 let written = defined(
804 &mut module,
805 &mut names,
806 "counted",
807 &[Datum::Scalar { ty: Type::int(32), value }],
808 );
809 module[written].tls = Some(TlsModel::GlobalDynamic);
810 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
811 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
812
813 let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
814 assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
815 assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
816 }
817
818 #[test]
820 fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
821 let mut names = Interner::new();
822 let mut module = module(&mut names);
823 let value = module.add_imm(Imm::int(1, Type::int(32)));
824 let id =
825 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
826 module[id].tls = Some(TlsModel::GlobalDynamic);
827 module[id].constant = true;
828
829 let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
830 assert_eq!(vars[0].place, Place::Thread { zero: false });
831 }
832
833 #[test]
836 fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
837 let mut names = Interner::new();
838 let mut module = module(&mut names);
839 let value = module.add_imm(Imm::int(258, Type::int(32)));
840 let written = defined(
841 &mut module,
842 &mut names,
843 "counted",
844 &[Datum::Scalar { ty: Type::int(32), value }],
845 );
846 module[written].tls = Some(TlsModel::GlobalDynamic);
847 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
848 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
849
850 let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
851 assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
852 assert_eq!(data.objects[0].size, 4);
853 assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
854 assert_eq!(data.objects[1].size, 4);
855 }
856
857 #[test]
860 fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
861 for format in [ObjectFormat::MachO, ObjectFormat::Coff] {
862 let mut names = Interner::new();
863 let mut module = module(&mut names);
864 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
865 module[id].tls = Some(TlsModel::GlobalDynamic);
866 let error = globals(&module, &names, format).expect_err("a thread-local variable");
867 assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
868 }
869 }
870}