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}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Variable {
61 pub name: String,
65 pub size: u64,
67 pub align: u64,
69 pub place: Place,
71 pub binding: Binding,
73 pub visibility: Visibility,
75 pub pieces: Vec<Piece>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub enum Piece {
82 Zero(u64),
85 Bytes(Vec<u8>),
87 Scalar(Vec<u8>),
89 Addr {
91 symbol: String,
93 addend: i64,
95 bytes: u8,
97 },
98}
99
100impl Piece {
101 #[must_use]
103 pub fn size(&self) -> u64 {
104 match self {
105 Piece::Zero(bytes) => *bytes,
106 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
107 Piece::Addr { bytes, .. } => u64::from(*bytes),
108 }
109 }
110}
111
112impl Globals {
113 #[must_use]
118 pub fn image(&self) -> Data {
119 let mut data = Data::default();
120 for var in &self.vars {
121 let mut object = Object {
122 name: var.name.clone(),
123 bytes: Vec::new(),
124 size: var.size,
125 align: var.align,
126 place: var.place.clone(),
127 binding: var.binding,
128 visibility: var.visibility,
129 relocs: Vec::new(),
130 };
131 if matches!(var.place, Place::Zero | Place::Merged | Place::Thread { zero: true }) {
132 data.objects.push(object);
133 continue;
134 }
135 for piece in &var.pieces {
136 match piece {
137 Piece::Zero(bytes) => {
138 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
139 }
140 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
141 object.bytes.extend_from_slice(bytes);
142 }
143 Piece::Addr { symbol, addend, bytes } => {
144 object.relocs.push(Reloc {
147 at: object.bytes.len(),
148 symbol: symbol.clone(),
149 kind: Reference::Address { bytes: *bytes },
150 addend: *addend,
151 });
152 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
153 }
154 }
155 }
156 data.objects.push(object);
157 }
158 data
159 }
160}
161
162pub fn globals(module: &Module, names: &Interner, format: ObjectFormat) -> Result<Globals, Error> {
174 let mut out = Globals::default();
175 for id in module.globals() {
176 if module[id].is_declaration() {
177 continue;
178 }
179 out.vars.push(variable(module, names, id, format)?);
180 }
181 Ok(out)
182}
183
184pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
195 let mut out = Vec::new();
196 for id in module.aliases() {
197 let alias = &module[id];
198 let name = names.resolve(alias.name).to_owned();
199 if alias.kind != AliasKind::Alias {
200 return Err(Error::IFunc { name });
201 }
202 out.push(Alias {
203 name,
204 target: names.resolve(alias.target).to_owned(),
205 binding: binding(alias.linkage),
206 visibility: visibility(alias.visibility),
207 });
208 }
209 Ok(out)
210}
211
212fn variable(
214 module: &Module,
215 names: &Interner,
216 id: GlobalId,
217 format: ObjectFormat,
218) -> Result<Variable, Error> {
219 let global = &module[id];
220 let name = names.resolve(global.name).to_owned();
221 if global.tls.is_some() && format != ObjectFormat::Elf {
222 return Err(Error::Thread { name, format: format.as_str() });
223 }
224 let init = global.init.expect("a definition has an image");
225
226 let mut pieces = Vec::new();
227 let mut addrs = Vec::new();
231 let mut written = 0;
232 for datum in &module[init] {
233 let piece = match *datum {
234 Datum::Zero(bytes) => Piece::Zero(bytes),
235 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
236 Datum::Scalar { ty, value } => {
237 if ty.lanes() != 1 {
238 let why = format!("a {ty} in an initializer");
239 return Err(Error::Image { name, why });
240 }
241 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
242 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
243 if !module.datalayout.little_endian {
244 image.reverse();
245 }
246 Piece::Scalar(image)
247 }
248 Datum::Addr(idx) => {
249 let reloc = module[idx];
250 let bytes = match reloc.size {
254 4 | 8 => reloc.size as u8,
255 size => {
256 let why = format!("an address {size} bytes wide");
257 return Err(Error::Image { name, why });
258 }
259 };
260 let symbol = names.resolve(reloc.symbol).to_owned();
261 addrs.push(reloc.symbol);
262 Piece::Addr { symbol, addend: reloc.addend, bytes }
263 }
264 };
265 written += piece.size();
266 pieces.push(piece);
267 }
268 if written < global.size {
271 pieces.push(Piece::Zero(global.size - written));
272 }
273
274 let place = place(module, names, id, &pieces, &addrs);
275 let size = global.size.max(written);
276 let binding = binding(global.linkage);
277 let visibility = visibility(global.visibility);
278 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
279}
280
281const fn binding(linkage: Linkage) -> Binding {
287 match linkage {
288 Linkage::Internal => Binding::Local,
289 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
290 Linkage::External | Linkage::Common => Binding::Global,
291 }
292}
293
294const fn visibility(visibility: ir::Visibility) -> Visibility {
300 match visibility {
301 ir::Visibility::Default => Visibility::Default,
302 ir::Visibility::Hidden => Visibility::Hidden,
303 ir::Visibility::Protected => Visibility::Protected,
304 }
305}
306
307fn place(
326 module: &Module,
327 names: &Interner,
328 id: GlobalId,
329 pieces: &[Piece],
330 addrs: &[Symbol],
331) -> Place {
332 let global = &module[id];
333 if global.tls.is_some() {
338 let zero = !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_)));
339 return Place::Thread { zero };
340 }
341 if let Some(section) = global.section {
342 return Place::Named(names.resolve(section).to_owned());
343 }
344 if global.linkage == Linkage::Common {
345 return Place::Merged;
346 }
347 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
348 return Place::Zero;
349 }
350 if global.constant {
351 return match addrs {
352 [] => Place::ReadOnly,
353 _ => Place::RelocReadOnly {
354 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
355 },
356 };
357 }
358 Place::Written
359}
360
361fn resolved_here(module: &Module, symbol: Symbol) -> bool {
367 match module.lookup(symbol) {
368 Some(SymbolRef::Func(id)) => {
369 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
370 }
371 Some(SymbolRef::Global(id)) => {
372 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
373 }
374 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
375 None => false,
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
384 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
385
386 fn module(names: &mut Interner) -> Module {
388 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
389 Module::new(names.intern("t.c"), &target)
390 }
391
392 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
394 let list = module.push_data(data);
395 let mut global = Global::new(names.intern(name), 4, 4);
396 global.init = Some(list);
397 module.add_global(global)
398 }
399
400 #[test]
401 fn a_declaration_is_not_a_variable_this_file_defines() {
402 let mut names = Interner::new();
403 let mut module = module(&mut names);
404 module.add_global(Global::new(names.intern("x"), 4, 4));
405 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
406 let vars =
407 globals(&module, &names, ObjectFormat::Elf).expect("a module of two globals").vars;
408 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
409 }
410
411 #[test]
417 fn the_visibility_a_variable_asked_for_reaches_the_image() {
418 for (asked, wanted) in [
419 (ir::Visibility::Default, Visibility::Default),
420 (ir::Visibility::Hidden, Visibility::Hidden),
421 (ir::Visibility::Protected, Visibility::Protected),
422 ] {
423 let mut names = Interner::new();
424 let mut module = module(&mut names);
425 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
426 module[id].visibility = asked;
427 let out = globals(&module, &names, ObjectFormat::Elf).expect("a module of one global");
428 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
429 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
430 }
431 }
432
433 #[test]
434 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
435 let mut names = Interner::new();
436 let mut module = module(&mut names);
437 let value = module.add_imm(Imm::int(258, Type::int(32)));
438 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
439 let vars =
440 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
441 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
442 assert_eq!(vars[0].pieces[0].size(), 4);
445 }
446
447 #[test]
448 fn what_a_variable_is_decides_which_section_it_goes_in() {
449 let mut names = Interner::new();
450 let mut module = module(&mut names);
451 let value = module.add_imm(Imm::int(1, Type::int(32)));
452 let scalar = Datum::Scalar { ty: Type::int(32), value };
453
454 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
455 let written = defined(&mut module, &mut names, "written", &[scalar]);
456 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
457 module[read_only].constant = true;
458 let named = defined(&mut module, &mut names, "named", &[scalar]);
459 module[named].section = Some(names.intern(".init_array"));
460 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
461 module[merged].linkage = Linkage::Common;
462
463 let vars =
464 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
465 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
466 assert_eq!(
467 places,
468 [
469 &Place::Zero,
470 &Place::Written,
471 &Place::ReadOnly,
472 &Place::Named(".init_array".to_owned()),
473 &Place::Merged,
474 ]
475 );
476 let _ = (zeroed, written);
477 }
478
479 #[test]
487 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
488 let mut names = Interner::new();
489 let mut module = module(&mut names);
490 let value = module.add_imm(Imm::int(1, Type::int(32)));
491
492 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
493 module[mine].linkage = Linkage::Internal;
494 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
495
496 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
497 let to_theirs =
498 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
499
500 let plain = defined(
501 &mut module,
502 &mut names,
503 "plain",
504 &[Datum::Scalar { ty: Type::int(32), value }],
505 );
506 module[plain].constant = true;
507 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
508 module[local].constant = true;
509 module[local].size = 8;
510 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
511 module[far].constant = true;
512 module[far].size = 8;
513 let both = defined(
514 &mut module,
515 &mut names,
516 "both",
517 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
518 );
519 module[both].constant = true;
520 module[both].size = 16;
521
522 let vars =
523 globals(&module, &names, ObjectFormat::Elf).expect("a module of five globals").vars;
524 let places: Vec<(&str, &Place)> =
525 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
526 assert_eq!(
527 places,
528 [
529 ("mine", &Place::Zero),
530 ("plain", &Place::ReadOnly),
531 ("local", &Place::RelocReadOnly { local: true }),
532 ("far", &Place::RelocReadOnly { local: false }),
533 ("both", &Place::RelocReadOnly { local: false }),
534 ]
535 );
536 }
537
538 #[test]
539 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
540 let mut names = Interner::new();
541 let mut module = module(&mut names);
542 let value = module.add_imm(Imm::int(7, Type::int(8)));
543 let id =
544 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
545 module[id].size = 4;
546 let vars =
547 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
548 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
549 assert_eq!(vars[0].size, 4);
550 }
551
552 #[test]
553 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
554 let mut names = Interner::new();
555 let mut module = module(&mut names);
556 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
557 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
558 module[id].size = 8;
559 let vars =
560 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").vars;
561 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
562
563 let data = Globals { vars }.image();
564 assert_eq!(data.objects[0].bytes, vec![0; 8]);
565 assert_eq!(
566 data.objects[0].relocs,
567 [Reloc {
568 at: 0,
569 symbol: "y".to_owned(),
570 kind: Reference::Address { bytes: 8 },
571 addend: 16,
572 }]
573 );
574 }
575
576 #[test]
577 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
578 let mut names = Interner::new();
579 let mut module = module(&mut names);
580 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
581 module[id].size = 4096;
582 let data =
583 globals(&module, &names, ObjectFormat::Elf).expect("a module of one global").image();
584 assert_eq!(data.objects[0].place, Place::Zero);
585 assert_eq!(data.objects[0].size, 4096);
586 assert!(data.objects[0].bytes.is_empty());
588 }
589
590 #[test]
591 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
592 let mut names = Interner::new();
593 let mut module = module(&mut names);
594 for (index, (linkage, binding)) in [
595 (Linkage::External, Binding::Global),
596 (Linkage::Internal, Binding::Local),
597 (Linkage::Weak, Binding::Weak),
598 (Linkage::LinkOnce, Binding::Weak),
599 ]
600 .into_iter()
601 .enumerate()
602 {
603 let name = format!("x{index}");
604 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
605 module[id].linkage = linkage;
606 let vars =
607 globals(&module, &names, ObjectFormat::Elf).expect("a module of globals").vars;
608 assert_eq!(vars[index].binding, binding, "{linkage:?}");
609 }
610 }
611
612 #[test]
613 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
614 let mut names = Interner::new();
615 let mut module = module(&mut names);
616 let target = names.intern("a");
617 for (index, (linkage, binding)) in [
618 (Linkage::External, Binding::Global),
619 (Linkage::Internal, Binding::Local),
620 (Linkage::Weak, Binding::Weak),
621 ]
622 .into_iter()
623 .enumerate()
624 {
625 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
626 alias.linkage = linkage;
627 module.add_alias(alias);
628 let written = aliases(&module, &names).expect("a module of aliases");
629 assert_eq!(written[index].binding, binding, "{linkage:?}");
630 assert_eq!(written[index].target, "a", "{linkage:?}");
631 }
632 }
633
634 #[test]
637 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
638 let mut names = Interner::new();
639 let mut module = module(&mut names);
640 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
641 memcpy.kind = AliasKind::IFunc;
642 module.add_alias(memcpy);
643 let error = aliases(&module, &names).expect_err("an ifunc");
644 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
645 }
646
647 #[test]
649 fn a_thread_local_variable_goes_in_the_section_a_thread_gets_a_copy_of() {
650 let mut names = Interner::new();
651 let mut module = module(&mut names);
652 let value = module.add_imm(Imm::int(1, Type::int(32)));
653 let written = defined(
654 &mut module,
655 &mut names,
656 "counted",
657 &[Datum::Scalar { ty: Type::int(32), value }],
658 );
659 module[written].tls = Some(TlsModel::GlobalDynamic);
660 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
661 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
662
663 let vars = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").vars;
664 assert_eq!(vars[0].place, Place::Thread { zero: false }, ".tdata");
665 assert_eq!(vars[1].place, Place::Thread { zero: true }, ".tbss");
666 }
667
668 #[test]
670 fn a_constant_thread_local_is_still_in_the_section_a_thread_gets_a_copy_of() {
671 let mut names = Interner::new();
672 let mut module = module(&mut names);
673 let value = module.add_imm(Imm::int(1, Type::int(32)));
674 let id =
675 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
676 module[id].tls = Some(TlsModel::GlobalDynamic);
677 module[id].constant = true;
678
679 let vars = globals(&module, &names, ObjectFormat::Elf).expect("a thread-local").vars;
680 assert_eq!(vars[0].place, Place::Thread { zero: false });
681 }
682
683 #[test]
686 fn the_image_of_a_thread_local_is_carried_only_when_it_is_not_all_zeros() {
687 let mut names = Interner::new();
688 let mut module = module(&mut names);
689 let value = module.add_imm(Imm::int(258, Type::int(32)));
690 let written = defined(
691 &mut module,
692 &mut names,
693 "counted",
694 &[Datum::Scalar { ty: Type::int(32), value }],
695 );
696 module[written].tls = Some(TlsModel::GlobalDynamic);
697 let zeroed = defined(&mut module, &mut names, "empty", &[Datum::Zero(4)]);
698 module[zeroed].tls = Some(TlsModel::GlobalDynamic);
699
700 let data = globals(&module, &names, ObjectFormat::Elf).expect("two thread-locals").image();
701 assert_eq!(data.objects[0].bytes, [2, 1, 0, 0]);
702 assert_eq!(data.objects[0].size, 4);
703 assert!(data.objects[1].bytes.is_empty(), "a zeroed one carries its size and no bytes");
704 assert_eq!(data.objects[1].size, 4);
705 }
706
707 #[test]
710 fn a_thread_local_variable_is_refused_on_a_format_that_does_not_spell_one_this_way() {
711 for format in [ObjectFormat::MachO, ObjectFormat::Coff] {
712 let mut names = Interner::new();
713 let mut module = module(&mut names);
714 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
715 module[id].tls = Some(TlsModel::GlobalDynamic);
716 let error = globals(&module, &names, format).expect_err("a thread-local variable");
717 assert_eq!(error, Error::Thread { name: "x".to_owned(), format: format.as_str() });
718 }
719 }
720}