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(
307 module: &Module,
308 names: &Interner,
309 id: GlobalId,
310 pieces: &[Piece],
311 addrs: &[Symbol],
312) -> Place {
313 let global = &module[id];
314 if let Some(section) = global.section {
315 return Place::Named(names.resolve(section).to_owned());
316 }
317 if global.linkage == Linkage::Common {
318 return Place::Merged;
319 }
320 if pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
321 return Place::Zero;
322 }
323 if global.constant {
324 return match addrs {
325 [] => Place::ReadOnly,
326 _ => Place::RelocReadOnly {
327 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
328 },
329 };
330 }
331 Place::Written
332}
333
334fn resolved_here(module: &Module, symbol: Symbol) -> bool {
340 match module.lookup(symbol) {
341 Some(SymbolRef::Func(id)) => {
342 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
343 }
344 Some(SymbolRef::Global(id)) => {
345 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
346 }
347 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
348 None => false,
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
357 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
358
359 fn module(names: &mut Interner) -> Module {
361 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
362 Module::new(names.intern("t.c"), &target)
363 }
364
365 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
367 let list = module.push_data(data);
368 let mut global = Global::new(names.intern(name), 4, 4);
369 global.init = Some(list);
370 module.add_global(global)
371 }
372
373 #[test]
374 fn a_declaration_is_not_a_variable_this_file_defines() {
375 let mut names = Interner::new();
376 let mut module = module(&mut names);
377 module.add_global(Global::new(names.intern("x"), 4, 4));
378 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
379 let vars = globals(&module, &names).expect("a module of two globals").vars;
380 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
381 }
382
383 #[test]
389 fn the_visibility_a_variable_asked_for_reaches_the_image() {
390 for (asked, wanted) in [
391 (ir::Visibility::Default, Visibility::Default),
392 (ir::Visibility::Hidden, Visibility::Hidden),
393 (ir::Visibility::Protected, Visibility::Protected),
394 ] {
395 let mut names = Interner::new();
396 let mut module = module(&mut names);
397 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
398 module[id].visibility = asked;
399 let out = globals(&module, &names).expect("a module of one global");
400 assert_eq!(out.vars[0].visibility, wanted, "{asked:?}");
401 assert_eq!(out.image().objects[0].visibility, wanted, "{asked:?} through the image");
402 }
403 }
404
405 #[test]
406 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
407 let mut names = Interner::new();
408 let mut module = module(&mut names);
409 let value = module.add_imm(Imm::int(258, Type::int(32)));
410 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
411 let vars = globals(&module, &names).expect("a module of one global").vars;
412 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
413 assert_eq!(vars[0].pieces[0].size(), 4);
416 }
417
418 #[test]
419 fn what_a_variable_is_decides_which_section_it_goes_in() {
420 let mut names = Interner::new();
421 let mut module = module(&mut names);
422 let value = module.add_imm(Imm::int(1, Type::int(32)));
423 let scalar = Datum::Scalar { ty: Type::int(32), value };
424
425 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
426 let written = defined(&mut module, &mut names, "written", &[scalar]);
427 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
428 module[read_only].constant = true;
429 let named = defined(&mut module, &mut names, "named", &[scalar]);
430 module[named].section = Some(names.intern(".init_array"));
431 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
432 module[merged].linkage = Linkage::Common;
433
434 let vars = globals(&module, &names).expect("a module of five globals").vars;
435 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
436 assert_eq!(
437 places,
438 [
439 &Place::Zero,
440 &Place::Written,
441 &Place::ReadOnly,
442 &Place::Named(".init_array".to_owned()),
443 &Place::Merged,
444 ]
445 );
446 let _ = (zeroed, written);
447 }
448
449 #[test]
457 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
458 let mut names = Interner::new();
459 let mut module = module(&mut names);
460 let value = module.add_imm(Imm::int(1, Type::int(32)));
461
462 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
463 module[mine].linkage = Linkage::Internal;
464 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
465
466 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
467 let to_theirs =
468 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
469
470 let plain = defined(
471 &mut module,
472 &mut names,
473 "plain",
474 &[Datum::Scalar { ty: Type::int(32), value }],
475 );
476 module[plain].constant = true;
477 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
478 module[local].constant = true;
479 module[local].size = 8;
480 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
481 module[far].constant = true;
482 module[far].size = 8;
483 let both = defined(
484 &mut module,
485 &mut names,
486 "both",
487 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
488 );
489 module[both].constant = true;
490 module[both].size = 16;
491
492 let vars = globals(&module, &names).expect("a module of five globals").vars;
493 let places: Vec<(&str, &Place)> =
494 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
495 assert_eq!(
496 places,
497 [
498 ("mine", &Place::Zero),
499 ("plain", &Place::ReadOnly),
500 ("local", &Place::RelocReadOnly { local: true }),
501 ("far", &Place::RelocReadOnly { local: false }),
502 ("both", &Place::RelocReadOnly { local: false }),
503 ]
504 );
505 }
506
507 #[test]
508 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
509 let mut names = Interner::new();
510 let mut module = module(&mut names);
511 let value = module.add_imm(Imm::int(7, Type::int(8)));
512 let id =
513 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
514 module[id].size = 4;
515 let vars = globals(&module, &names).expect("a module of one global").vars;
516 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
517 assert_eq!(vars[0].size, 4);
518 }
519
520 #[test]
521 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
522 let mut names = Interner::new();
523 let mut module = module(&mut names);
524 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
525 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
526 module[id].size = 8;
527 let vars = globals(&module, &names).expect("a module of one global").vars;
528 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
529
530 let data = Globals { vars }.image();
531 assert_eq!(data.objects[0].bytes, vec![0; 8]);
532 assert_eq!(
533 data.objects[0].relocs,
534 [Reloc {
535 at: 0,
536 symbol: "y".to_owned(),
537 kind: Reference::Address { bytes: 8 },
538 addend: 16,
539 }]
540 );
541 }
542
543 #[test]
544 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
545 let mut names = Interner::new();
546 let mut module = module(&mut names);
547 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
548 module[id].size = 4096;
549 let data = globals(&module, &names).expect("a module of one global").image();
550 assert_eq!(data.objects[0].place, Place::Zero);
551 assert_eq!(data.objects[0].size, 4096);
552 assert!(data.objects[0].bytes.is_empty());
554 }
555
556 #[test]
557 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
558 let mut names = Interner::new();
559 let mut module = module(&mut names);
560 for (index, (linkage, binding)) in [
561 (Linkage::External, Binding::Global),
562 (Linkage::Internal, Binding::Local),
563 (Linkage::Weak, Binding::Weak),
564 (Linkage::LinkOnce, Binding::Weak),
565 ]
566 .into_iter()
567 .enumerate()
568 {
569 let name = format!("x{index}");
570 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
571 module[id].linkage = linkage;
572 let vars = globals(&module, &names).expect("a module of globals").vars;
573 assert_eq!(vars[index].binding, binding, "{linkage:?}");
574 }
575 }
576
577 #[test]
578 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
579 let mut names = Interner::new();
580 let mut module = module(&mut names);
581 let target = names.intern("a");
582 for (index, (linkage, binding)) in [
583 (Linkage::External, Binding::Global),
584 (Linkage::Internal, Binding::Local),
585 (Linkage::Weak, Binding::Weak),
586 ]
587 .into_iter()
588 .enumerate()
589 {
590 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
591 alias.linkage = linkage;
592 module.add_alias(alias);
593 let written = aliases(&module, &names).expect("a module of aliases");
594 assert_eq!(written[index].binding, binding, "{linkage:?}");
595 assert_eq!(written[index].target, "a", "{linkage:?}");
596 }
597 }
598
599 #[test]
602 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
603 let mut names = Interner::new();
604 let mut module = module(&mut names);
605 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
606 memcpy.kind = AliasKind::IFunc;
607 module.add_alias(memcpy);
608 let error = aliases(&module, &names).expect_err("an ifunc");
609 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
610 }
611
612 #[test]
613 fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
614 let mut names = Interner::new();
615 let mut module = module(&mut names);
616 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
617 module[id].tls = Some(TlsModel::GlobalDynamic);
618 let error = globals(&module, &names).expect_err("a thread-local variable");
619 assert_eq!(error, Error::Thread { name: "x".to_owned() });
620 }
621}