1use rucc_base::{Interner, Symbol};
40use rucc_ir as ir;
41use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module, SymbolRef};
42use rucc_object::{Alias, Binding, Data, Object, Place, Reference, Reloc, Visibility};
43
44use crate::Error;
45
46#[derive(Debug, Clone, Default, PartialEq, Eq)]
48pub struct Globals {
49 pub vars: Vec<Variable>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Variable {
58 pub name: String,
62 pub size: u64,
64 pub align: u64,
66 pub place: Place,
68 pub binding: Binding,
70 pub visibility: Visibility,
72 pub pieces: Vec<Piece>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub enum Piece {
79 Zero(u64),
82 Bytes(Vec<u8>),
84 Scalar(Vec<u8>),
86 Addr {
88 symbol: String,
90 addend: i64,
92 bytes: u8,
94 },
95}
96
97impl Piece {
98 #[must_use]
100 pub fn size(&self) -> u64 {
101 match self {
102 Piece::Zero(bytes) => *bytes,
103 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
104 Piece::Addr { bytes, .. } => u64::from(*bytes),
105 }
106 }
107}
108
109impl Globals {
110 #[must_use]
115 pub fn image(&self) -> Data {
116 let mut data = Data::default();
117 for var in &self.vars {
118 let mut object = Object {
119 name: var.name.clone(),
120 bytes: Vec::new(),
121 size: var.size,
122 align: var.align,
123 place: var.place.clone(),
124 binding: var.binding,
125 visibility: var.visibility,
126 relocs: Vec::new(),
127 };
128 if matches!(var.place, Place::Zero | Place::Merged) {
129 data.objects.push(object);
130 continue;
131 }
132 for piece in &var.pieces {
133 match piece {
134 Piece::Zero(bytes) => {
135 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
136 }
137 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
138 object.bytes.extend_from_slice(bytes);
139 }
140 Piece::Addr { symbol, addend, bytes } => {
141 object.relocs.push(Reloc {
144 at: object.bytes.len(),
145 symbol: symbol.clone(),
146 kind: Reference::Address { bytes: *bytes },
147 addend: *addend,
148 });
149 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
150 }
151 }
152 }
153 data.objects.push(object);
154 }
155 data
156 }
157}
158
159pub fn globals(module: &Module, names: &Interner) -> Result<Globals, Error> {
167 let mut out = Globals::default();
168 for id in module.globals() {
169 if module[id].is_declaration() {
170 continue;
171 }
172 out.vars.push(variable(module, names, id)?);
173 }
174 Ok(out)
175}
176
177pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
188 let mut out = Vec::new();
189 for id in module.aliases() {
190 let alias = &module[id];
191 let name = names.resolve(alias.name).to_owned();
192 if alias.kind != AliasKind::Alias {
193 return Err(Error::IFunc { name });
194 }
195 out.push(Alias {
196 name,
197 target: names.resolve(alias.target).to_owned(),
198 binding: binding(alias.linkage),
199 visibility: visibility(alias.visibility),
200 });
201 }
202 Ok(out)
203}
204
205fn variable(module: &Module, names: &Interner, id: GlobalId) -> Result<Variable, Error> {
207 let global = &module[id];
208 let name = names.resolve(global.name).to_owned();
209 if global.tls.is_some() {
210 return Err(Error::Thread { name });
211 }
212 let init = global.init.expect("a definition has an image");
213
214 let mut pieces = Vec::new();
215 let mut addrs = Vec::new();
219 let mut written = 0;
220 for datum in &module[init] {
221 let piece = match *datum {
222 Datum::Zero(bytes) => Piece::Zero(bytes),
223 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
224 Datum::Scalar { ty, value } => {
225 if ty.lanes() != 1 {
226 let why = format!("a {ty} in an initializer");
227 return Err(Error::Image { name, why });
228 }
229 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
230 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
231 if !module.datalayout.little_endian {
232 image.reverse();
233 }
234 Piece::Scalar(image)
235 }
236 Datum::Addr(idx) => {
237 let reloc = module[idx];
238 let bytes = match reloc.size {
242 4 | 8 => reloc.size as u8,
243 size => {
244 let why = format!("an address {size} bytes wide");
245 return Err(Error::Image { name, why });
246 }
247 };
248 let symbol = names.resolve(reloc.symbol).to_owned();
249 addrs.push(reloc.symbol);
250 Piece::Addr { symbol, addend: reloc.addend, bytes }
251 }
252 };
253 written += piece.size();
254 pieces.push(piece);
255 }
256 if written < global.size {
259 pieces.push(Piece::Zero(global.size - written));
260 }
261
262 let place = place(module, names, id, &pieces, &addrs);
263 let size = global.size.max(written);
264 let binding = binding(global.linkage);
265 let visibility = visibility(global.visibility);
266 Ok(Variable { name, size, align: u64::from(global.align), place, binding, visibility, pieces })
267}
268
269const fn binding(linkage: Linkage) -> Binding {
275 match linkage {
276 Linkage::Internal => Binding::Local,
277 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
278 Linkage::External | Linkage::Common => Binding::Global,
279 }
280}
281
282const fn visibility(visibility: ir::Visibility) -> Visibility {
288 match visibility {
289 ir::Visibility::Default => Visibility::Default,
290 ir::Visibility::Hidden => Visibility::Hidden,
291 ir::Visibility::Protected => Visibility::Protected,
292 }
293}
294
295fn place(
314 module: &Module,
315 names: &Interner,
316 id: GlobalId,
317 pieces: &[Piece],
318 addrs: &[Symbol],
319) -> Place {
320 let global = &module[id];
321 if let Some(section) = global.section {
322 return Place::Named(names.resolve(section).to_owned());
323 }
324 if global.linkage == Linkage::Common {
325 return Place::Merged;
326 }
327 if !pieces.is_empty() && pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
328 return Place::Zero;
329 }
330 if global.constant {
331 return match addrs {
332 [] => Place::ReadOnly,
333 _ => Place::RelocReadOnly {
334 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
335 },
336 };
337 }
338 Place::Written
339}
340
341fn resolved_here(module: &Module, symbol: Symbol) -> bool {
347 match module.lookup(symbol) {
348 Some(SymbolRef::Func(id)) => {
349 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
350 }
351 Some(SymbolRef::Global(id)) => {
352 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
353 }
354 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
355 None => false,
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
364 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
365
366 fn module(names: &mut Interner) -> Module {
368 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
369 Module::new(names.intern("t.c"), &target)
370 }
371
372 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
374 let list = module.push_data(data);
375 let mut global = Global::new(names.intern(name), 4, 4);
376 global.init = Some(list);
377 module.add_global(global)
378 }
379
380 #[test]
381 fn a_declaration_is_not_a_variable_this_file_defines() {
382 let mut names = Interner::new();
383 let mut module = module(&mut names);
384 module.add_global(Global::new(names.intern("x"), 4, 4));
385 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
386 let vars = globals(&module, &names).expect("a module of two globals").vars;
387 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
388 }
389
390 #[test]
396 fn the_visibility_a_variable_asked_for_reaches_the_image() {
397 for (asked, wanted) in [
398 (ir::Visibility::Default, Visibility::Default),
399 (ir::Visibility::Hidden, Visibility::Hidden),
400 (ir::Visibility::Protected, Visibility::Protected),
401 ] {
402 let mut names = Interner::new();
403 let mut module = module(&mut names);
404 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
405 module[id].visibility = asked;
406 let out = globals(&module, &names).expect("a module of one global");
407 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
408 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
409 }
410 }
411
412 #[test]
413 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
414 let mut names = Interner::new();
415 let mut module = module(&mut names);
416 let value = module.add_imm(Imm::int(258, Type::int(32)));
417 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
418 let vars = globals(&module, &names).expect("a module of one global").vars;
419 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
420 assert_eq!(vars[0].pieces[0].size(), 4);
423 }
424
425 #[test]
426 fn what_a_variable_is_decides_which_section_it_goes_in() {
427 let mut names = Interner::new();
428 let mut module = module(&mut names);
429 let value = module.add_imm(Imm::int(1, Type::int(32)));
430 let scalar = Datum::Scalar { ty: Type::int(32), value };
431
432 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
433 let written = defined(&mut module, &mut names, "written", &[scalar]);
434 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
435 module[read_only].constant = true;
436 let named = defined(&mut module, &mut names, "named", &[scalar]);
437 module[named].section = Some(names.intern(".init_array"));
438 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
439 module[merged].linkage = Linkage::Common;
440
441 let vars = globals(&module, &names).expect("a module of five globals").vars;
442 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
443 assert_eq!(
444 places,
445 [
446 &Place::Zero,
447 &Place::Written,
448 &Place::ReadOnly,
449 &Place::Named(".init_array".to_owned()),
450 &Place::Merged,
451 ]
452 );
453 let _ = (zeroed, written);
454 }
455
456 #[test]
464 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
465 let mut names = Interner::new();
466 let mut module = module(&mut names);
467 let value = module.add_imm(Imm::int(1, Type::int(32)));
468
469 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
470 module[mine].linkage = Linkage::Internal;
471 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
472
473 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
474 let to_theirs =
475 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
476
477 let plain = defined(
478 &mut module,
479 &mut names,
480 "plain",
481 &[Datum::Scalar { ty: Type::int(32), value }],
482 );
483 module[plain].constant = true;
484 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
485 module[local].constant = true;
486 module[local].size = 8;
487 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
488 module[far].constant = true;
489 module[far].size = 8;
490 let both = defined(
491 &mut module,
492 &mut names,
493 "both",
494 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
495 );
496 module[both].constant = true;
497 module[both].size = 16;
498
499 let vars = globals(&module, &names).expect("a module of five globals").vars;
500 let places: Vec<(&str, &Place)> =
501 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
502 assert_eq!(
503 places,
504 [
505 ("mine", &Place::Zero),
506 ("plain", &Place::ReadOnly),
507 ("local", &Place::RelocReadOnly { local: true }),
508 ("far", &Place::RelocReadOnly { local: false }),
509 ("both", &Place::RelocReadOnly { local: false }),
510 ]
511 );
512 }
513
514 #[test]
515 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
516 let mut names = Interner::new();
517 let mut module = module(&mut names);
518 let value = module.add_imm(Imm::int(7, Type::int(8)));
519 let id =
520 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
521 module[id].size = 4;
522 let vars = globals(&module, &names).expect("a module of one global").vars;
523 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
524 assert_eq!(vars[0].size, 4);
525 }
526
527 #[test]
528 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
529 let mut names = Interner::new();
530 let mut module = module(&mut names);
531 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
532 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
533 module[id].size = 8;
534 let vars = globals(&module, &names).expect("a module of one global").vars;
535 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
536
537 let data = Globals { vars }.image();
538 assert_eq!(data.objects[0].bytes, vec![0; 8]);
539 assert_eq!(
540 data.objects[0].relocs,
541 [Reloc {
542 at: 0,
543 symbol: "y".to_owned(),
544 kind: Reference::Address { bytes: 8 },
545 addend: 16,
546 }]
547 );
548 }
549
550 #[test]
551 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
552 let mut names = Interner::new();
553 let mut module = module(&mut names);
554 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
555 module[id].size = 4096;
556 let data = globals(&module, &names).expect("a module of one global").image();
557 assert_eq!(data.objects[0].place, Place::Zero);
558 assert_eq!(data.objects[0].size, 4096);
559 assert!(data.objects[0].bytes.is_empty());
561 }
562
563 #[test]
564 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
565 let mut names = Interner::new();
566 let mut module = module(&mut names);
567 for (index, (linkage, binding)) in [
568 (Linkage::External, Binding::Global),
569 (Linkage::Internal, Binding::Local),
570 (Linkage::Weak, Binding::Weak),
571 (Linkage::LinkOnce, Binding::Weak),
572 ]
573 .into_iter()
574 .enumerate()
575 {
576 let name = format!("x{index}");
577 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
578 module[id].linkage = linkage;
579 let vars = globals(&module, &names).expect("a module of globals").vars;
580 assert_eq!(vars[index].binding, binding, "{linkage:?}");
581 }
582 }
583
584 #[test]
585 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
586 let mut names = Interner::new();
587 let mut module = module(&mut names);
588 let target = names.intern("a");
589 for (index, (linkage, binding)) in [
590 (Linkage::External, Binding::Global),
591 (Linkage::Internal, Binding::Local),
592 (Linkage::Weak, Binding::Weak),
593 ]
594 .into_iter()
595 .enumerate()
596 {
597 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
598 alias.linkage = linkage;
599 module.add_alias(alias);
600 let written = aliases(&module, &names).expect("a module of aliases");
601 assert_eq!(written[index].binding, binding, "{linkage:?}");
602 assert_eq!(written[index].target, "a", "{linkage:?}");
603 }
604 }
605
606 #[test]
609 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
610 let mut names = Interner::new();
611 let mut module = module(&mut names);
612 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
613 memcpy.kind = AliasKind::IFunc;
614 module.add_alias(memcpy);
615 let error = aliases(&module, &names).expect_err("an ifunc");
616 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
617 }
618
619 #[test]
620 fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
621 let mut names = Interner::new();
622 let mut module = module(&mut names);
623 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
624 module[id].tls = Some(TlsModel::GlobalDynamic);
625 let error = globals(&module, &names).expect_err("a thread-local variable");
626 assert_eq!(error, Error::Thread { name: "x".to_owned() });
627 }
628}