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 pub file_asm: Vec<String>,
80}
81
82impl Globals {
83 #[must_use]
86 pub fn kept(&self) -> bool {
87 !self.file_asm.is_empty()
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct Variable {
94 pub name: String,
98 pub size: u64,
100 pub align: u64,
102 pub place: Place,
104 pub binding: Binding,
106 pub visibility: Visibility,
108 pub pieces: Vec<Piece>,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum Piece {
115 Zero(u64),
118 Bytes(Vec<u8>),
120 Scalar(Vec<u8>),
122 Addr {
124 symbol: String,
126 addend: i64,
128 bytes: u8,
130 },
131 Away {
134 symbol: String,
136 addend: i64,
139 },
140 Apart {
143 to: String,
145 from: String,
147 addend: i64,
149 bytes: u8,
151 },
152}
153
154impl Piece {
155 #[must_use]
157 pub fn size(&self) -> u64 {
158 match self {
159 Piece::Zero(bytes) => *bytes,
160 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
161 Piece::Addr { bytes, .. } | Piece::Apart { bytes, .. } => u64::from(*bytes),
162 Piece::Away { .. } => 4,
163 }
164 }
165}
166
167impl Globals {
168 #[must_use]
173 pub fn image(&self) -> Data {
174 let mut data = Data { weak: self.weak.clone(), ..Data::default() };
175 for var in &self.vars {
176 let mut object = Object {
177 name: var.name.clone(),
178 bytes: Vec::new(),
179 size: var.size,
180 align: var.align,
181 place: var.place.clone(),
182 binding: var.binding,
183 visibility: var.visibility,
184 relocs: Vec::new(),
185 };
186 if matches!(var.place, Place::Zero | Place::Merged | Place::Thread { zero: true }) {
187 data.objects.push(object);
188 continue;
189 }
190 for piece in &var.pieces {
191 match piece {
192 Piece::Zero(bytes) => {
193 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
194 }
195 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
196 object.bytes.extend_from_slice(bytes);
197 }
198 Piece::Addr { symbol, addend, bytes } => {
199 object.relocs.push(Reloc {
202 at: object.bytes.len(),
203 symbol: symbol.clone(),
204 kind: Reference::Address { bytes: *bytes },
205 addend: *addend,
206 after: 0,
209 });
210 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
211 }
212 Piece::Away { symbol, addend } => {
213 object.relocs.push(Reloc {
214 at: object.bytes.len(),
215 symbol: symbol.clone(),
216 kind: Reference::Away,
217 addend: *addend,
218 after: 0,
219 });
220 object.bytes.resize(object.bytes.len() + 4, 0);
221 }
222 Piece::Apart { to, from, addend, bytes } => {
223 data.apart.push(Apart {
224 object: data.objects.len(),
225 at: object.bytes.len(),
226 to: to.clone(),
227 from: from.clone(),
228 addend: *addend,
229 bytes: *bytes,
230 });
231 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
232 }
233 }
234 }
235 data.objects.push(object);
236 }
237 data
238 }
239}
240
241pub fn globals(module: &Module, names: &Interner, format: ObjectFormat) -> Result<Globals, Error> {
254 let mut out = Globals::default();
255 for id in module.globals() {
256 if module[id].is_declaration() {
257 continue;
258 }
259 out.vars.push(variable(module, names, id, format)?);
260 }
261 for id in module.funcs() {
271 let func = &module[id];
272 if func.is_declaration() && func.linkage == Linkage::Weak {
273 out.weak.push(names.resolve(func.name).to_owned());
274 }
275 }
276 for id in module.globals() {
277 let global = &module[id];
278 if global.is_declaration() && global.linkage == Linkage::Weak {
279 out.weak.push(names.resolve(global.name).to_owned());
280 }
281 }
282 out.file_asm = module.file_asms().to_vec();
283 Ok(out)
284}
285
286pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
297 let mut out = Vec::new();
298 for id in module.aliases() {
299 let alias = &module[id];
300 let name = names.resolve(alias.name).to_owned();
301 if alias.kind != AliasKind::Alias {
302 return Err(Error::IFunc { name });
303 }
304 out.push(Alias {
305 name,
306 target: names.resolve(alias.target).to_owned(),
307 binding: binding(alias.linkage),
308 visibility: visibility(alias.visibility),
309 });
310 }
311 Ok(out)
312}
313
314fn variable(
316 module: &Module,
317 names: &Interner,
318 id: GlobalId,
319 format: ObjectFormat,
320) -> Result<Variable, Error> {
321 let global = &module[id];
322 let name = names.resolve(global.name).to_owned();
323 if global.tls.is_some() && format == ObjectFormat::Wasm {
324 return Err(Error::Thread { name, format: format.as_str() });
325 }
326 let init = global.init.expect("a definition has an image");
327
328 let mut pieces = Vec::new();
329 let mut addrs = Vec::new();
333 let mut written = 0;
334 for datum in &module[init] {
335 let piece = match *datum {
336 Datum::Zero(bytes) => Piece::Zero(bytes),
337 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
338 Datum::Scalar { ty, value } => {
339 if ty.lanes() != 1 {
340 let why = format!("a {ty} in an initializer");
341 return Err(Error::Image { name, why });
342 }
343 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
344 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
345 if !module.datalayout.little_endian {
346 image.reverse();
347 }
348 Piece::Scalar(image)
349 }
350 Datum::Away(idx) => {
355 let reloc = module[idx];
356 if reloc.size != 4 {
357 let why = format!("a distance {} bytes wide", reloc.size);
358 return Err(Error::Image { name, why });
359 }
360 let symbol = names.resolve(reloc.symbol).to_owned();
361 Piece::Away { symbol, addend: reloc.addend }
362 }
363 Datum::Apart { to, from } => {
366 let reloc = module[to];
367 let bytes = match reloc.size {
368 1 | 2 | 4 | 8 => reloc.size as u8,
369 size => {
370 let why = format!("a distance {size} bytes wide");
371 return Err(Error::Image { name, why });
372 }
373 };
374 let to = names.resolve(reloc.symbol).to_owned();
375 let from = names.resolve(from).to_owned();
376 Piece::Apart { to, from, addend: reloc.addend, bytes }
377 }
378 Datum::Addr(idx) => {
379 let reloc = module[idx];
380 let bytes = match reloc.size {
384 4 | 8 => reloc.size as u8,
385 size => {
386 let why = format!("an address {size} bytes wide");
387 return Err(Error::Image { name, why });
388 }
389 };
390 let symbol = names.resolve(reloc.symbol).to_owned();
391 addrs.push(reloc.symbol);
392 Piece::Addr { symbol, addend: reloc.addend, bytes }
393 }
394 };
395 written += piece.size();
396 pieces.push(piece);
397 }
398 if written < global.size {
401 pieces.push(Piece::Zero(global.size - written));
402 }
403
404 let place = match place(module, names, id, &pieces, &addrs) {
405 Place::Thread { .. } if format == ObjectFormat::Coff => Place::Thread { zero: false },
408 place => place,
409 };
410 let size = global.size.max(written);
411 let binding = binding(global.linkage);
412 let visibility = visibility(global.visibility);
413 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
414}
415
416const fn binding(linkage: Linkage) -> Binding {
422 match linkage {
423 Linkage::Internal => Binding::Local,
424 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
425 Linkage::External | Linkage::Common => Binding::Global,
426 }
427}
428
429const fn visibility(visibility: ir::Visibility) -> Visibility {
435 match visibility {
436 ir::Visibility::Default => Visibility::Default,
437 ir::Visibility::Hidden => Visibility::Hidden,
438 ir::Visibility::Protected => Visibility::Protected,
439 }
440}
441
442fn place(
461 module: &Module,
462 names: &Interner,
463 id: GlobalId,
464 pieces: &[Piece],
465 addrs: &[Symbol],
466) -> Place {
467 let global = &module[id];
468 if global.tls.is_some() {
473 let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
474 return Place::Thread { zero };
475 }
476 if let Some(section) = global.section {
477 return Place::Named(names.resolve(section).to_owned());
478 }
479 if global.linkage == Linkage::Common {
480 return Place::Merged;
481 }
482 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
483 return Place::Zero;
484 }
485 if global.constant {
486 return match addrs {
487 [] => Place::ReadOnly,
488 _ => Place::RelocReadOnly {
489 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
490 },
491 };
492 }
493 Place::Written
494}
495
496fn resolved_here(module: &Module, symbol: Symbol) -> bool {
502 match module.lookup(symbol) {
503 Some(SymbolRef::Func(id)) => {
504 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
505 }
506 Some(SymbolRef::Global(id)) => {
507 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
508 }
509 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
510 None => false,
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517
518 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
519 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
520
521 fn module(names: &mut Interner) -> Module {
523 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
524 Module::new(names.intern("t.c"), &target)
525 }
526
527 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
529 let list = module.push_data(data);
530 let mut global = Global::new(names.intern(name), 4, 4);
531 global.init = Some(list);
532 module.add_global(global)
533 }
534
535 #[test]
536 fn a_declaration_is_not_a_variable_this_file_defines() {
537 let mut names = Interner::new();
538 let mut module = module(&mut names);
539 module.add_global(Global::new(names.intern("x"), 4, 4));
540 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
541 let vars =
542 globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
543 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
544 }
545
546 #[test]
552 fn the_visibility_a_variable_asked_for_reaches_the_image() {
553 for (asked, wanted) in [
554 (ir::Visibility::Default, Visibility::Default),
555 (ir::Visibility::Hidden, Visibility::Hidden),
556 (ir::Visibility::Protected, Visibility::Protected),
557 ] {
558 let mut names = Interner::new();
559 let mut module = module(&mut names);
560 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
561 module[id].visibility = asked;
562 let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
563 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
564 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
565 }
566 }
567
568 #[test]
569 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
570 let mut names = Interner::new();
571 let mut module = module(&mut names);
572 let value = module.add_imm(Imm::int(258, Type::int(32)));
573 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
574 let vars =
575 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
576 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
577 assert_eq!(vars[0].pieces[0].size(), 4);
580 }
581
582 #[test]
583 fn what_a_variable_is_decides_which_section_it_goes_in() {
584 let mut names = Interner::new();
585 let mut module = module(&mut names);
586 let value = module.add_imm(Imm::int(1, Type::int(32)));
587 let scalar = Datum::Scalar { ty: Type::int(32), value };
588
589 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
590 let written = defined(&mut module, &mut names, "written", &[scalar]);
591 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
592 module[read_only].constant = true;
593 let named = defined(&mut module, &mut names, "named", &[scalar]);
594 module[named].section = Some(names.intern(".init_array"));
595 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
596 module[merged].linkage = Linkage::Common;
597
598 let vars =
599 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
600 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
601 assert_eq!(
602 places,
603 [
604 &Place::Zero,
605 &Place::Written,
606 &Place::ReadOnly,
607 &Place::Named(".init_array".to_owned()),
608 &Place::Merged,
609 ]
610 );
611 let _ = (zeroed, written);
612 }
613
614 #[test]
622 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
623 let mut names = Interner::new();
624 let mut module = module(&mut names);
625 let value = module.add_imm(Imm::int(1, Type::int(32)));
626
627 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
628 module[mine].linkage = Linkage::Internal;
629 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
630
631 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
632 let to_theirs =
633 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
634
635 let plain = defined(
636 &mut module,
637 &mut names,
638 "plain",
639 &[Datum::Scalar { ty: Type::int(32), value }],
640 );
641 module[plain].constant = true;
642 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
643 module[local].constant = true;
644 module[local].size = 8;
645 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
646 module[far].constant = true;
647 module[far].size = 8;
648 let both = defined(
649 &mut module,
650 &mut names,
651 "both",
652 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
653 );
654 module[both].constant = true;
655 module[both].size = 16;
656
657 let vars =
658 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
659 let places: Vec<(&str, &Place)> =
660 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
661 assert_eq!(
662 places,
663 [
664 ("mine", &Place::Zero),
665 ("plain", &Place::ReadOnly),
666 ("local", &Place::RelocReadOnly { local: true }),
667 ("far", &Place::RelocReadOnly { local: false }),
668 ("both", &Place::RelocReadOnly { local: false }),
669 ]
670 );
671 }
672
673 #[test]
674 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
675 let mut names = Interner::new();
676 let mut module = module(&mut names);
677 let value = module.add_imm(Imm::int(7, Type::int(8)));
678 let id =
679 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
680 module[id].size = 4;
681 let vars =
682 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
683 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
684 assert_eq!(vars[0].size, 4);
685 }
686
687 #[test]
688 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
689 let mut names = Interner::new();
690 let mut module = module(&mut names);
691 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
692 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
693 module[id].size = 8;
694 let vars =
695 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
696 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
697
698 let data = Globals { vars, ..Globals::default() }.image();
699 assert_eq!(data.objects[0].bytes, vec![0; 8]);
700 assert_eq!(
701 data.objects[0].relocs,
702 [Reloc {
703 at: 0,
704 symbol: "y".to_owned(),
705 kind: Reference::Address { bytes: 8 },
706 addend: 16,
707 after: 0,
708 }]
709 );
710 }
711
712 #[test]
713 fn a_variable_holding_a_distance_is_a_hole_the_linker_measures_from_where_it_is() {
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: 1, size: 4 });
717 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
718 module[id].size = 4;
719 module[id].constant = true;
722 let vars =
723 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
724 assert_eq!(vars[0].pieces, [Piece::Away { symbol: "y".to_owned(), addend: 1 }]);
725 assert_eq!(vars[0].place, Place::ReadOnly);
726
727 let data = Globals { vars, ..Globals::default() }.image();
728 assert_eq!(data.objects[0].bytes, vec![0; 4]);
729 assert_eq!(
730 data.objects[0].relocs,
731 [Reloc { at: 0, symbol: "y".to_owned(), kind: Reference::Away, addend: 1, after: 0 }]
732 );
733 }
734
735 #[test]
736 fn a_distance_of_a_width_no_relocation_writes_is_refused_by_the_width_it_asked_for() {
737 let mut names = Interner::new();
738 let mut module = module(&mut names);
739 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 0, size: 8 });
740 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
741 module[id].size = 8;
742 let failed = globals(&module, &names, ObjectFormat::Elf).expect_err("a distance that wide");
743 assert_eq!(
744 failed,
745 Error::Image { name: "d".to_owned(), why: "a distance 8 bytes wide".to_owned() }
746 );
747 }
748
749 #[test]
750 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
751 let mut names = Interner::new();
752 let mut module = module(&mut names);
753 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
754 module[id].size = 4096;
755 let data =
756 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
757 assert_eq!(data.objects[0].place, Place::Zero);
758 assert_eq!(data.objects[0].size, 4096);
759 assert!(data.objects[0].bytes.is_empty());
761 }
762
763 #[test]
764 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
765 let mut names = Interner::new();
766 let mut module = module(&mut names);
767 for (index, (linkage, binding)) in [
768 (Linkage::External, Binding::Global),
769 (Linkage::Internal, Binding::Local),
770 (Linkage::Weak, Binding::Weak),
771 (Linkage::LinkOnce, Binding::Weak),
772 ]
773 .into_iter()
774 .enumerate()
775 {
776 let name = format!("x{index}");
777 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
778 module[id].linkage = linkage;
779 let vars =
780 globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
781 assert_eq!(vars[index].binding, binding, "{linkage:?}");
782 }
783 }
784
785 #[test]
786 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
787 let mut names = Interner::new();
788 let mut module = module(&mut names);
789 let target = names.intern("a");
790 for (index, (linkage, binding)) in [
791 (Linkage::External, Binding::Global),
792 (Linkage::Internal, Binding::Local),
793 (Linkage::Weak, Binding::Weak),
794 ]
795 .into_iter()
796 .enumerate()
797 {
798 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
799 alias.linkage = linkage;
800 module.add_alias(alias);
801 let written = aliases(&module, &names).expect("a module of aliases");
802 assert_eq!(written[index].binding, binding, "{linkage:?}");
803 assert_eq!(written[index].target, "a", "{linkage:?}");
804 }
805 }
806
807 #[test]
810 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
811 let mut names = Interner::new();
812 let mut module = module(&mut names);
813 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
814 memcpy.kind = AliasKind::IFunc;
815 module.add_alias(memcpy);
816 let error = aliases(&module, &names).expect_err("an ifunc");
817 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
818 }
819
820 #[test]
822 fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
823 let mut names = Interner::new();
824 let mut module = module(&mut names);
825 let value = module.add_imm(Imm::int(1, Type::int(32)));
826 let written = defined(
827 &mut module,
828 &mut names,
829 "counted",
830 &[Datum::Scalar { ty: Type::int(32), value }],
831 );
832 module[written].tls = Some(TlsModel::GlobalDynamic);
833 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
834 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
835
836 let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
837 assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
838 assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
839 }
840
841 #[test]
843 fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
844 let mut names = Interner::new();
845 let mut module = module(&mut names);
846 let value = module.add_imm(Imm::int(1, Type::int(32)));
847 let id =
848 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
849 module[id].tls = Some(TlsModel::GlobalDynamic);
850 module[id].constant = true;
851
852 let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
853 assert_eq!(vars[0].place, Place::Thread { zero: false });
854 }
855
856 #[test]
859 fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
860 let mut names = Interner::new();
861 let mut module = module(&mut names);
862 let value = module.add_imm(Imm::int(258, Type::int(32)));
863 let written = defined(
864 &mut module,
865 &mut names,
866 "counted",
867 &[Datum::Scalar { ty: Type::int(32), value }],
868 );
869 module[written].tls = Some(TlsModel::GlobalDynamic);
870 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
871 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
872
873 let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
874 assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
875 assert_eq!(data.objects[0].size, 4);
876 assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
877 assert_eq!(data.objects[1].size, 4);
878 }
879
880 #[test]
882 fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
883 let mut names = Interner::new();
884 let mut module = module(&mut names);
885 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
886 module[id].tls = Some(TlsModel::GlobalDynamic);
887 let format = ObjectFormat::Wasm;
888 let error = globals(&module, &names, format).expect_err("a thread-local variable");
889 assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
890 }
891
892 #[test]
895 fn a_zeroed_thread_local_on_windows_is_written_out() {
896 let mut names = Interner::new();
897 let mut module = module(&mut names);
898 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
899 module[id].tls = Some(TlsModel::GlobalDynamic);
900 let vars = globals(&module, &names, ObjectFormat::Coff).expect("a thread-local variable");
901 let data = vars.image();
902 assert_eq!(data.objects[0].place, Place::Thread { zero: false });
903 assert_eq!(data.objects[0].bytes, [0, 0, 0, 0]);
904 }
905}