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() && !matches!(format, ObjectFormat::Elf | ObjectFormat::MachO) {
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 = place(module, names, id, &pieces, &addrs);
405 let size = global.size.max(written);
406 let binding = binding(global.linkage);
407 let visibility = visibility(global.visibility);
408 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
409}
410
411const fn binding(linkage: Linkage) -> Binding {
417 match linkage {
418 Linkage::Internal => Binding::Local,
419 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
420 Linkage::External | Linkage::Common => Binding::Global,
421 }
422}
423
424const fn visibility(visibility: ir::Visibility) -> Visibility {
430 match visibility {
431 ir::Visibility::Default => Visibility::Default,
432 ir::Visibility::Hidden => Visibility::Hidden,
433 ir::Visibility::Protected => Visibility::Protected,
434 }
435}
436
437fn place(
456 module: &Module,
457 names: &Interner,
458 id: GlobalId,
459 pieces: &[Piece],
460 addrs: &[Symbol],
461) -> Place {
462 let global = &module[id];
463 if global.tls.is_some() {
468 let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
469 return Place::Thread { zero };
470 }
471 if let Some(section) = global.section {
472 return Place::Named(names.resolve(section).to_owned());
473 }
474 if global.linkage == Linkage::Common {
475 return Place::Merged;
476 }
477 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
478 return Place::Zero;
479 }
480 if global.constant {
481 return match addrs {
482 [] => Place::ReadOnly,
483 _ => Place::RelocReadOnly {
484 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
485 },
486 };
487 }
488 Place::Written
489}
490
491fn resolved_here(module: &Module, symbol: Symbol) -> bool {
497 match module.lookup(symbol) {
498 Some(SymbolRef::Func(id)) => {
499 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
500 }
501 Some(SymbolRef::Global(id)) => {
502 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
503 }
504 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
505 None => false,
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512
513 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
514 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
515
516 fn module(names: &mut Interner) -> Module {
518 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
519 Module::new(names.intern("t.c"), &target)
520 }
521
522 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
524 let list = module.push_data(data);
525 let mut global = Global::new(names.intern(name), 4, 4);
526 global.init = Some(list);
527 module.add_global(global)
528 }
529
530 #[test]
531 fn a_declaration_is_not_a_variable_this_file_defines() {
532 let mut names = Interner::new();
533 let mut module = module(&mut names);
534 module.add_global(Global::new(names.intern("x"), 4, 4));
535 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
536 let vars =
537 globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
538 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
539 }
540
541 #[test]
547 fn the_visibility_a_variable_asked_for_reaches_the_image() {
548 for (asked, wanted) in [
549 (ir::Visibility::Default, Visibility::Default),
550 (ir::Visibility::Hidden, Visibility::Hidden),
551 (ir::Visibility::Protected, Visibility::Protected),
552 ] {
553 let mut names = Interner::new();
554 let mut module = module(&mut names);
555 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
556 module[id].visibility = asked;
557 let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
558 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
559 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
560 }
561 }
562
563 #[test]
564 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
565 let mut names = Interner::new();
566 let mut module = module(&mut names);
567 let value = module.add_imm(Imm::int(258, Type::int(32)));
568 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
569 let vars =
570 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
571 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
572 assert_eq!(vars[0].pieces[0].size(), 4);
575 }
576
577 #[test]
578 fn what_a_variable_is_decides_which_section_it_goes_in() {
579 let mut names = Interner::new();
580 let mut module = module(&mut names);
581 let value = module.add_imm(Imm::int(1, Type::int(32)));
582 let scalar = Datum::Scalar { ty: Type::int(32), value };
583
584 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
585 let written = defined(&mut module, &mut names, "written", &[scalar]);
586 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
587 module[read_only].constant = true;
588 let named = defined(&mut module, &mut names, "named", &[scalar]);
589 module[named].section = Some(names.intern(".init_array"));
590 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
591 module[merged].linkage = Linkage::Common;
592
593 let vars =
594 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
595 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
596 assert_eq!(
597 places,
598 [
599 &Place::Zero,
600 &Place::Written,
601 &Place::ReadOnly,
602 &Place::Named(".init_array".to_owned()),
603 &Place::Merged,
604 ]
605 );
606 let _ = (zeroed, written);
607 }
608
609 #[test]
617 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
618 let mut names = Interner::new();
619 let mut module = module(&mut names);
620 let value = module.add_imm(Imm::int(1, Type::int(32)));
621
622 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
623 module[mine].linkage = Linkage::Internal;
624 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
625
626 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
627 let to_theirs =
628 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
629
630 let plain = defined(
631 &mut module,
632 &mut names,
633 "plain",
634 &[Datum::Scalar { ty: Type::int(32), value }],
635 );
636 module[plain].constant = true;
637 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
638 module[local].constant = true;
639 module[local].size = 8;
640 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
641 module[far].constant = true;
642 module[far].size = 8;
643 let both = defined(
644 &mut module,
645 &mut names,
646 "both",
647 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
648 );
649 module[both].constant = true;
650 module[both].size = 16;
651
652 let vars =
653 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
654 let places: Vec<(&str, &Place)> =
655 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
656 assert_eq!(
657 places,
658 [
659 ("mine", &Place::Zero),
660 ("plain", &Place::ReadOnly),
661 ("local", &Place::RelocReadOnly { local: true }),
662 ("far", &Place::RelocReadOnly { local: false }),
663 ("both", &Place::RelocReadOnly { local: false }),
664 ]
665 );
666 }
667
668 #[test]
669 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
670 let mut names = Interner::new();
671 let mut module = module(&mut names);
672 let value = module.add_imm(Imm::int(7, Type::int(8)));
673 let id =
674 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
675 module[id].size = 4;
676 let vars =
677 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
678 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
679 assert_eq!(vars[0].size, 4);
680 }
681
682 #[test]
683 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
684 let mut names = Interner::new();
685 let mut module = module(&mut names);
686 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
687 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
688 module[id].size = 8;
689 let vars =
690 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
691 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
692
693 let data = Globals { vars, ..Globals::default() }.image();
694 assert_eq!(data.objects[0].bytes, vec![0; 8]);
695 assert_eq!(
696 data.objects[0].relocs,
697 [Reloc {
698 at: 0,
699 symbol: "y".to_owned(),
700 kind: Reference::Address { bytes: 8 },
701 addend: 16,
702 after: 0,
703 }]
704 );
705 }
706
707 #[test]
708 fn a_variable_holding_a_distance_is_a_hole_the_linker_measures_from_where_it_is() {
709 let mut names = Interner::new();
710 let mut module = module(&mut names);
711 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 1, size: 4 });
712 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
713 module[id].size = 4;
714 module[id].constant = true;
717 let vars =
718 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
719 assert_eq!(vars[0].pieces, [Piece::Away { symbol: "y".to_owned(), addend: 1 }]);
720 assert_eq!(vars[0].place, Place::ReadOnly);
721
722 let data = Globals { vars, ..Globals::default() }.image();
723 assert_eq!(data.objects[0].bytes, vec![0; 4]);
724 assert_eq!(
725 data.objects[0].relocs,
726 [Reloc { at: 0, symbol: "y".to_owned(), kind: Reference::Away, addend: 1, after: 0 }]
727 );
728 }
729
730 #[test]
731 fn a_distance_of_a_width_no_relocation_writes_is_refused_by_the_width_it_asked_for() {
732 let mut names = Interner::new();
733 let mut module = module(&mut names);
734 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 0, size: 8 });
735 let id = defined(&mut module, &mut names, "d", &[Datum::Away(reloc)]);
736 module[id].size = 8;
737 let failed = globals(&module, &names, ObjectFormat::Elf).expect_err("a distance that wide");
738 assert_eq!(
739 failed,
740 Error::Image { name: "d".to_owned(), why: "a distance 8 bytes wide".to_owned() }
741 );
742 }
743
744 #[test]
745 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
746 let mut names = Interner::new();
747 let mut module = module(&mut names);
748 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
749 module[id].size = 4096;
750 let data =
751 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
752 assert_eq!(data.objects[0].place, Place::Zero);
753 assert_eq!(data.objects[0].size, 4096);
754 assert!(data.objects[0].bytes.is_empty());
756 }
757
758 #[test]
759 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
760 let mut names = Interner::new();
761 let mut module = module(&mut names);
762 for (index, (linkage, binding)) in [
763 (Linkage::External, Binding::Global),
764 (Linkage::Internal, Binding::Local),
765 (Linkage::Weak, Binding::Weak),
766 (Linkage::LinkOnce, Binding::Weak),
767 ]
768 .into_iter()
769 .enumerate()
770 {
771 let name = format!("x{index}");
772 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
773 module[id].linkage = linkage;
774 let vars =
775 globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
776 assert_eq!(vars[index].binding, binding, "{linkage:?}");
777 }
778 }
779
780 #[test]
781 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
782 let mut names = Interner::new();
783 let mut module = module(&mut names);
784 let target = names.intern("a");
785 for (index, (linkage, binding)) in [
786 (Linkage::External, Binding::Global),
787 (Linkage::Internal, Binding::Local),
788 (Linkage::Weak, Binding::Weak),
789 ]
790 .into_iter()
791 .enumerate()
792 {
793 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
794 alias.linkage = linkage;
795 module.add_alias(alias);
796 let written = aliases(&module, &names).expect("a module of aliases");
797 assert_eq!(written[index].binding, binding, "{linkage:?}");
798 assert_eq!(written[index].target, "a", "{linkage:?}");
799 }
800 }
801
802 #[test]
805 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
806 let mut names = Interner::new();
807 let mut module = module(&mut names);
808 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
809 memcpy.kind = AliasKind::IFunc;
810 module.add_alias(memcpy);
811 let error = aliases(&module, &names).expect_err("an ifunc");
812 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
813 }
814
815 #[test]
817 fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
818 let mut names = Interner::new();
819 let mut module = module(&mut names);
820 let value = module.add_imm(Imm::int(1, Type::int(32)));
821 let written = defined(
822 &mut module,
823 &mut names,
824 "counted",
825 &[Datum::Scalar { ty: Type::int(32), value }],
826 );
827 module[written].tls = Some(TlsModel::GlobalDynamic);
828 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
829 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
830
831 let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
832 assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
833 assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
834 }
835
836 #[test]
838 fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
839 let mut names = Interner::new();
840 let mut module = module(&mut names);
841 let value = module.add_imm(Imm::int(1, Type::int(32)));
842 let id =
843 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
844 module[id].tls = Some(TlsModel::GlobalDynamic);
845 module[id].constant = true;
846
847 let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
848 assert_eq!(vars[0].place, Place::Thread { zero: false });
849 }
850
851 #[test]
854 fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
855 let mut names = Interner::new();
856 let mut module = module(&mut names);
857 let value = module.add_imm(Imm::int(258, Type::int(32)));
858 let written = defined(
859 &mut module,
860 &mut names,
861 "counted",
862 &[Datum::Scalar { ty: Type::int(32), value }],
863 );
864 module[written].tls = Some(TlsModel::GlobalDynamic);
865 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
866 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
867
868 let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
869 assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
870 assert_eq!(data.objects[0].size, 4);
871 assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
872 assert_eq!(data.objects[1].size, 4);
873 }
874
875 #[test]
878 fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
879 let mut names = Interner::new();
880 let mut module = module(&mut names);
881 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
882 module[id].tls = Some(TlsModel::GlobalDynamic);
883 let format = ObjectFormat::Coff;
884 let error = globals(&module, &names, format).expect_err("a thread-local variable");
885 assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
886 }
887}