1use rucc_base::{Interner, Symbol};
42use rucc_ir as ir;
43use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module, SymbolRef};
44use rucc_object::{Alias, Binding, Data, Object, Place, Reference, Reloc, Visibility};
45use rucc_target::ObjectFormat;
46
47use crate::Error;
48
49#[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}
116
117impl Piece {
118 #[must_use]
120 pub fn size(&self) -> u64 {
121 match self {
122 Piece::Zero(bytes) => *bytes,
123 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
124 Piece::Addr { bytes, .. } => u64::from(*bytes),
125 }
126 }
127}
128
129impl Globals {
130 #[must_use]
135 pub fn image(&self) -> Data {
136 let mut data = Data { weak: self.weak.clone(), ..Data::default() };
137 for var in &self.vars {
138 let mut object = Object {
139 name: var.name.clone(),
140 bytes: Vec::new(),
141 size: var.size,
142 align: var.align,
143 place: var.place.clone(),
144 binding: var.binding,
145 visibility: var.visibility,
146 relocs: Vec::new(),
147 };
148 if matches!(var.place, Place::Zero | Place::Merged | Place::Thread { zero: true }) {
149 data.objects.push(object);
150 continue;
151 }
152 for piece in &var.pieces {
153 match piece {
154 Piece::Zero(bytes) => {
155 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
156 }
157 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
158 object.bytes.extend_from_slice(bytes);
159 }
160 Piece::Addr { symbol, addend, bytes } => {
161 object.relocs.push(Reloc {
164 at: object.bytes.len(),
165 symbol: symbol.clone(),
166 kind: Reference::Address { bytes: *bytes },
167 addend: *addend,
168 after: 0,
171 });
172 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
173 }
174 }
175 }
176 data.objects.push(object);
177 }
178 data
179 }
180}
181
182pub fn globals(module: &Module, names: &Interner, format: ObjectFormat) -> Result<Globals, Error> {
194 let mut out = Globals::default();
195 for id in module.globals() {
196 if module[id].is_declaration() {
197 continue;
198 }
199 out.vars.push(variable(module, names, id, format)?);
200 }
201 for id in module.funcs() {
211 let func = &module[id];
212 if func.is_declaration() && func.linkage == Linkage::Weak {
213 out.weak.push(names.resolve(func.name).to_owned());
214 }
215 }
216 for id in module.globals() {
217 let global = &module[id];
218 if global.is_declaration() && global.linkage == Linkage::Weak {
219 out.weak.push(names.resolve(global.name).to_owned());
220 }
221 }
222 Ok(out)
223}
224
225pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
236 let mut out = Vec::new();
237 for id in module.aliases() {
238 let alias = &module[id];
239 let name = names.resolve(alias.name).to_owned();
240 if alias.kind != AliasKind::Alias {
241 return Err(Error::IFunc { name });
242 }
243 out.push(Alias {
244 name,
245 target: names.resolve(alias.target).to_owned(),
246 binding: binding(alias.linkage),
247 visibility: visibility(alias.visibility),
248 });
249 }
250 Ok(out)
251}
252
253fn variable(
255 module: &Module,
256 names: &Interner,
257 id: GlobalId,
258 format: ObjectFormat,
259) -> Result<Variable, Error> {
260 let global = &module[id];
261 let name = names.resolve(global.name).to_owned();
262 if global.tls.is_some() && format != ObjectFormat::Elf {
263 return Err(Error::Thread { name, format: format.as_str() });
264 }
265 let init = global.init.expect("a definition has an image");
266
267 let mut pieces = Vec::new();
268 let mut addrs = Vec::new();
272 let mut written = 0;
273 for datum in &module[init] {
274 let piece = match *datum {
275 Datum::Zero(bytes) => Piece::Zero(bytes),
276 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
277 Datum::Scalar { ty, value } => {
278 if ty.lanes() != 1 {
279 let why = format!("a {ty} in an initializer");
280 return Err(Error::Image { name, why });
281 }
282 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
283 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
284 if !module.datalayout.little_endian {
285 image.reverse();
286 }
287 Piece::Scalar(image)
288 }
289 Datum::Addr(idx) => {
290 let reloc = module[idx];
291 let bytes = match reloc.size {
295 4 | 8 => reloc.size as u8,
296 size => {
297 let why = format!("an address {size} bytes wide");
298 return Err(Error::Image { name, why });
299 }
300 };
301 let symbol = names.resolve(reloc.symbol).to_owned();
302 addrs.push(reloc.symbol);
303 Piece::Addr { symbol, addend: reloc.addend, bytes }
304 }
305 };
306 written += piece.size();
307 pieces.push(piece);
308 }
309 if written < global.size {
312 pieces.push(Piece::Zero(global.size - written));
313 }
314
315 let place = place(module, names, id, &pieces, &addrs);
316 let size = global.size.max(written);
317 let binding = binding(global.linkage);
318 let visibility = visibility(global.visibility);
319 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
320}
321
322const fn binding(linkage: Linkage) -> Binding {
328 match linkage {
329 Linkage::Internal => Binding::Local,
330 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
331 Linkage::External | Linkage::Common => Binding::Global,
332 }
333}
334
335const fn visibility(visibility: ir::Visibility) -> Visibility {
341 match visibility {
342 ir::Visibility::Default => Visibility::Default,
343 ir::Visibility::Hidden => Visibility::Hidden,
344 ir::Visibility::Protected => Visibility::Protected,
345 }
346}
347
348fn place(
367 module: &Module,
368 names: &Interner,
369 id: GlobalId,
370 pieces: &[Piece],
371 addrs: &[Symbol],
372) -> Place {
373 let global = &module[id];
374 if global.tls.is_some() {
379 let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
380 return Place::Thread { zero };
381 }
382 if let Some(section) = global.section {
383 return Place::Named(names.resolve(section).to_owned());
384 }
385 if global.linkage == Linkage::Common {
386 return Place::Merged;
387 }
388 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
389 return Place::Zero;
390 }
391 if global.constant {
392 return match addrs {
393 [] => Place::ReadOnly,
394 _ => Place::RelocReadOnly {
395 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
396 },
397 };
398 }
399 Place::Written
400}
401
402fn resolved_here(module: &Module, symbol: Symbol) -> bool {
408 match module.lookup(symbol) {
409 Some(SymbolRef::Func(id)) => {
410 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
411 }
412 Some(SymbolRef::Global(id)) => {
413 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
414 }
415 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
416 None => false,
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
425 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
426
427 fn module(names: &mut Interner) -> Module {
429 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
430 Module::new(names.intern("t.c"), &target)
431 }
432
433 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
435 let list = module.push_data(data);
436 let mut global = Global::new(names.intern(name), 4, 4);
437 global.init = Some(list);
438 module.add_global(global)
439 }
440
441 #[test]
442 fn a_declaration_is_not_a_variable_this_file_defines() {
443 let mut names = Interner::new();
444 let mut module = module(&mut names);
445 module.add_global(Global::new(names.intern("x"), 4, 4));
446 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
447 let vars =
448 globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
449 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
450 }
451
452 #[test]
458 fn the_visibility_a_variable_asked_for_reaches_the_image() {
459 for (asked, wanted) in [
460 (ir::Visibility::Default, Visibility::Default),
461 (ir::Visibility::Hidden, Visibility::Hidden),
462 (ir::Visibility::Protected, Visibility::Protected),
463 ] {
464 let mut names = Interner::new();
465 let mut module = module(&mut names);
466 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
467 module[id].visibility = asked;
468 let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
469 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
470 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
471 }
472 }
473
474 #[test]
475 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
476 let mut names = Interner::new();
477 let mut module = module(&mut names);
478 let value = module.add_imm(Imm::int(258, Type::int(32)));
479 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
480 let vars =
481 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
482 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
483 assert_eq!(vars[0].pieces[0].size(), 4);
486 }
487
488 #[test]
489 fn what_a_variable_is_decides_which_section_it_goes_in() {
490 let mut names = Interner::new();
491 let mut module = module(&mut names);
492 let value = module.add_imm(Imm::int(1, Type::int(32)));
493 let scalar = Datum::Scalar { ty: Type::int(32), value };
494
495 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
496 let written = defined(&mut module, &mut names, "written", &[scalar]);
497 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
498 module[read_only].constant = true;
499 let named = defined(&mut module, &mut names, "named", &[scalar]);
500 module[named].section = Some(names.intern(".init_array"));
501 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
502 module[merged].linkage = Linkage::Common;
503
504 let vars =
505 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
506 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
507 assert_eq!(
508 places,
509 [
510 &Place::Zero,
511 &Place::Written,
512 &Place::ReadOnly,
513 &Place::Named(".init_array".to_owned()),
514 &Place::Merged,
515 ]
516 );
517 let _ = (zeroed, written);
518 }
519
520 #[test]
528 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
529 let mut names = Interner::new();
530 let mut module = module(&mut names);
531 let value = module.add_imm(Imm::int(1, Type::int(32)));
532
533 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
534 module[mine].linkage = Linkage::Internal;
535 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
536
537 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
538 let to_theirs =
539 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
540
541 let plain = defined(
542 &mut module,
543 &mut names,
544 "plain",
545 &[Datum::Scalar { ty: Type::int(32), value }],
546 );
547 module[plain].constant = true;
548 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
549 module[local].constant = true;
550 module[local].size = 8;
551 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
552 module[far].constant = true;
553 module[far].size = 8;
554 let both = defined(
555 &mut module,
556 &mut names,
557 "both",
558 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
559 );
560 module[both].constant = true;
561 module[both].size = 16;
562
563 let vars =
564 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
565 let places: Vec<(&str, &Place)> =
566 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
567 assert_eq!(
568 places,
569 [
570 ("mine", &Place::Zero),
571 ("plain", &Place::ReadOnly),
572 ("local", &Place::RelocReadOnly { local: true }),
573 ("far", &Place::RelocReadOnly { local: false }),
574 ("both", &Place::RelocReadOnly { local: false }),
575 ]
576 );
577 }
578
579 #[test]
580 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
581 let mut names = Interner::new();
582 let mut module = module(&mut names);
583 let value = module.add_imm(Imm::int(7, Type::int(8)));
584 let id =
585 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
586 module[id].size = 4;
587 let vars =
588 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
589 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
590 assert_eq!(vars[0].size, 4);
591 }
592
593 #[test]
594 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
595 let mut names = Interner::new();
596 let mut module = module(&mut names);
597 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
598 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
599 module[id].size = 8;
600 let vars =
601 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
602 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
603
604 let data = Globals { vars, weak: Vec::new() }.image();
605 assert_eq!(data.objects[0].bytes, vec![0; 8]);
606 assert_eq!(
607 data.objects[0].relocs,
608 [Reloc {
609 at: 0,
610 symbol: "y".to_owned(),
611 kind: Reference::Address { bytes: 8 },
612 addend: 16,
613 after: 0,
614 }]
615 );
616 }
617
618 #[test]
619 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
620 let mut names = Interner::new();
621 let mut module = module(&mut names);
622 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
623 module[id].size = 4096;
624 let data =
625 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
626 assert_eq!(data.objects[0].place, Place::Zero);
627 assert_eq!(data.objects[0].size, 4096);
628 assert!(data.objects[0].bytes.is_empty());
630 }
631
632 #[test]
633 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
634 let mut names = Interner::new();
635 let mut module = module(&mut names);
636 for (index, (linkage, binding)) in [
637 (Linkage::External, Binding::Global),
638 (Linkage::Internal, Binding::Local),
639 (Linkage::Weak, Binding::Weak),
640 (Linkage::LinkOnce, Binding::Weak),
641 ]
642 .into_iter()
643 .enumerate()
644 {
645 let name = format!("x{index}");
646 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
647 module[id].linkage = linkage;
648 let vars =
649 globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
650 assert_eq!(vars[index].binding, binding, "{linkage:?}");
651 }
652 }
653
654 #[test]
655 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
656 let mut names = Interner::new();
657 let mut module = module(&mut names);
658 let target = names.intern("a");
659 for (index, (linkage, binding)) in [
660 (Linkage::External, Binding::Global),
661 (Linkage::Internal, Binding::Local),
662 (Linkage::Weak, Binding::Weak),
663 ]
664 .into_iter()
665 .enumerate()
666 {
667 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
668 alias.linkage = linkage;
669 module.add_alias(alias);
670 let written = aliases(&module, &names).expect("a module of aliases");
671 assert_eq!(written[index].binding, binding, "{linkage:?}");
672 assert_eq!(written[index].target, "a", "{linkage:?}");
673 }
674 }
675
676 #[test]
679 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
680 let mut names = Interner::new();
681 let mut module = module(&mut names);
682 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
683 memcpy.kind = AliasKind::IFunc;
684 module.add_alias(memcpy);
685 let error = aliases(&module, &names).expect_err("an ifunc");
686 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
687 }
688
689 #[test]
691 fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
692 let mut names = Interner::new();
693 let mut module = module(&mut names);
694 let value = module.add_imm(Imm::int(1, Type::int(32)));
695 let written = defined(
696 &mut module,
697 &mut names,
698 "counted",
699 &[Datum::Scalar { ty: Type::int(32), value }],
700 );
701 module[written].tls = Some(TlsModel::GlobalDynamic);
702 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
703 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
704
705 let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
706 assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
707 assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
708 }
709
710 #[test]
712 fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
713 let mut names = Interner::new();
714 let mut module = module(&mut names);
715 let value = module.add_imm(Imm::int(1, Type::int(32)));
716 let id =
717 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
718 module[id].tls = Some(TlsModel::GlobalDynamic);
719 module[id].constant = true;
720
721 let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
722 assert_eq!(vars[0].place, Place::Thread { zero: false });
723 }
724
725 #[test]
728 fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
729 let mut names = Interner::new();
730 let mut module = module(&mut names);
731 let value = module.add_imm(Imm::int(258, Type::int(32)));
732 let written = defined(
733 &mut module,
734 &mut names,
735 "counted",
736 &[Datum::Scalar { ty: Type::int(32), value }],
737 );
738 module[written].tls = Some(TlsModel::GlobalDynamic);
739 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
740 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
741
742 let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
743 assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
744 assert_eq!(data.objects[0].size, 4);
745 assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
746 assert_eq!(data.objects[1].size, 4);
747 }
748
749 #[test]
752 fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
753 for format in [ObjectFormat::MachO, ObjectFormat::Coff] {
754 let mut names = Interner::new();
755 let mut module = module(&mut names);
756 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
757 module[id].tls = Some(TlsModel::GlobalDynamic);
758 let error = globals(&module, &names, format).expect_err("a thread-local variable");
759 assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
760 }
761 }
762}