1use object::write::{Object as Writer, Relocation, StandardSection, Symbol, SymbolSection};
29use object::{
30 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionKind, SymbolFlags, SymbolKind,
31 SymbolScope, elf,
32};
33use rucc_target::{ObjectFormat, TargetInfo};
34use rucc_tuple::Arch;
35
36use crate::section::{Alias, Binding, Data, Object, Place, Reference, Reloc, Text};
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum Error {
41 Format {
43 triple: String,
45 },
46 Refused {
48 why: String,
50 },
51}
52
53impl std::fmt::Display for Error {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Error::Format { triple } => {
57 write!(f, "there is no object writer for {triple} in this compiler yet")
58 }
59 Error::Refused { why } => {
60 write!(f, "the object writer refused what it was given: {why}")
61 }
62 }
63 }
64}
65
66impl std::error::Error for Error {}
67
68pub fn write(
77 text: &Text,
78 data: &Data,
79 aliases: &[Alias],
80 target: &TargetInfo,
81) -> Result<Vec<u8>, Error> {
82 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
83 return Err(Error::Format { triple: target.tuple.to_string() });
84 }
85 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
86 let section = obj.section_id(StandardSection::Text);
87 obj.append_section_data(section, &text.bytes, u64::from(text.align));
88
89 let mut symbols = std::collections::BTreeMap::new();
93 for func in &text.funcs {
94 let id = obj.add_symbol(Symbol {
95 name: func.name.clone().into_bytes(),
96 value: func.start as u64,
97 size: func.len as u64,
98 kind: SymbolKind::Text,
99 scope: scope_of(func.binding),
100 weak: func.binding == Binding::Weak,
101 section: SymbolSection::Section(section),
102 flags: SymbolFlags::None,
103 });
104 symbols.insert(func.name.clone(), id);
105 }
106
107 let mut placed = Vec::with_capacity(data.objects.len());
112 let mut local = None;
116 for object in &data.objects {
117 let (section, offset) = put(&mut obj, object, &mut local);
118 let id = obj.add_symbol(Symbol {
119 name: object.name.clone().into_bytes(),
120 value: if object.place == Place::Merged { object.align } else { offset },
123 size: object.size,
124 kind: SymbolKind::Data,
125 scope: scope_of(object.binding),
126 weak: object.binding == Binding::Weak,
127 section,
128 flags: SymbolFlags::None,
129 });
130 symbols.insert(object.name.clone(), id);
131 placed.push((section.id(), offset));
132 }
133
134 for alias in aliases {
140 let Some(&id) = symbols.get(&alias.target) else {
141 let why =
142 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
143 return Err(Error::Refused { why });
144 };
145 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
146 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
147 let id = obj.add_symbol(Symbol {
148 name: alias.name.clone().into_bytes(),
149 value,
150 size,
151 kind,
152 scope: scope_of(alias.binding),
153 weak: alias.binding == Binding::Weak,
154 section,
155 flags: SymbolFlags::None,
156 });
157 symbols.insert(alias.name.clone(), id);
158 }
159
160 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
161 for reloc in wanted {
162 if symbols.contains_key(&reloc.symbol) {
163 continue;
164 }
165 let id = obj.add_symbol(Symbol {
166 name: reloc.symbol.clone().into_bytes(),
167 value: 0,
168 size: 0,
169 kind: SymbolKind::Unknown,
173 scope: SymbolScope::Dynamic,
174 weak: false,
175 section: SymbolSection::Undefined,
176 flags: SymbolFlags::None,
177 });
178 symbols.insert(reloc.symbol.clone(), id);
179 }
180
181 for reloc in &text.relocs {
182 add(&mut obj, section, 0, reloc, &symbols)?;
183 }
184 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
185 let Some(section) = section else { continue };
186 for reloc in &object.relocs {
187 add(&mut obj, section, offset, reloc, &symbols)?;
188 }
189 }
190
191 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
194
195 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
196}
197
198fn put(
205 obj: &mut Writer<'_>,
206 object: &Object,
207 local: &mut Option<object::write::SectionId>,
208) -> (SymbolSection, u64) {
209 let section = match &object.place {
210 Place::Written => obj.section_id(StandardSection::Data),
211 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
212 Place::RelocReadOnly { local: false } => {
218 obj.section_id(StandardSection::ReadOnlyDataWithRel)
219 }
220 Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
221 obj.add_section(
222 Vec::new(),
223 b".data.rel.ro.local".to_vec(),
224 SectionKind::ReadOnlyDataWithRel,
225 )
226 }),
227 Place::Zero => obj.section_id(StandardSection::UninitializedData),
228 Place::Merged => return (SymbolSection::Common, 0),
229 Place::Named(name) => {
233 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
234 }
235 };
236 let offset = if object.place == Place::Zero {
237 obj.append_section_bss(section, object.size, object.align)
238 } else {
239 obj.append_section_data(section, &object.bytes, object.align)
240 };
241 (SymbolSection::Section(section), offset)
242}
243
244fn add(
246 obj: &mut Writer<'_>,
247 section: object::write::SectionId,
248 offset: u64,
249 reloc: &Reloc,
250 symbols: &std::collections::BTreeMap<String, object::write::SymbolId>,
251) -> Result<(), Error> {
252 let r_type = r_type(reloc.kind)
253 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
254 obj.add_relocation(
255 section,
256 Relocation {
257 offset: offset + reloc.at as u64,
258 symbol: symbols[&reloc.symbol],
259 addend: reloc.addend,
260 flags: RelocationFlags::Elf { r_type },
261 },
262 )
263 .map_err(|why| Error::Refused { why: why.to_string() })
264}
265
266fn scope_of(binding: Binding) -> SymbolScope {
280 match binding {
281 Binding::Local => SymbolScope::Compilation,
282 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
283 }
284}
285
286fn r_type(reference: Reference) -> Option<elf::RelocationType> {
297 Some(match reference {
298 Reference::Call => elf::R_X86_64_PLT32,
299 Reference::Data => elf::R_X86_64_PC32,
300 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
301 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
302 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
303 Reference::Address { .. } => return None,
304 })
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 use object::read::elf::Sym as _;
312 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
313 use rucc_target::{Arch, Env, Os, Triple};
314
315 use crate::section::{Extent, Reloc};
316
317 fn target() -> TargetInfo {
319 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
320 }
321
322 fn calling(name: &str) -> Text {
324 Text {
325 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
326 funcs: vec![Extent {
327 name: "f".to_owned(),
328 start: 0,
329 len: 6,
330 binding: Binding::Global,
331 }],
332 relocs: vec![Reloc {
333 at: 1,
334 symbol: name.to_owned(),
335 kind: Reference::Call,
336 addend: -4,
337 }],
338 ..Text::default()
339 }
340 }
341
342 #[test]
343 fn the_bytes_come_back_out_of_the_section_they_went_into() {
344 let text = calling("puts");
345 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
346 let file = object::File::parse(&bytes[..]).expect("a readable object");
347 let section = file.section_by_name(".text").expect("a text section");
348 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
349 }
350
351 #[test]
352 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
353 let mut text = calling("puts");
354 text.funcs.push(Extent {
355 name: "g".to_owned(),
356 start: 16,
357 len: 1,
358 binding: Binding::Global,
359 });
360 text.bytes.resize(17, 0x90);
361 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
362 let file = object::File::parse(&bytes[..]).expect("a readable object");
363 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
364 assert_eq!(g.address(), 16);
365 assert_eq!(g.size(), 1);
366 assert_eq!(g.kind(), SymbolKind::Text);
367 assert!(g.is_global(), "nothing said otherwise about this one");
368 }
369
370 #[test]
371 fn a_function_no_other_file_can_see_is_a_local_symbol() {
372 let mut text = calling("puts");
373 text.funcs.push(Extent {
374 name: "hidden".to_owned(),
375 start: 16,
376 len: 1,
377 binding: Binding::Local,
378 });
379 text.funcs.push(Extent {
380 name: "shared".to_owned(),
381 start: 32,
382 len: 1,
383 binding: Binding::Weak,
384 });
385 text.bytes.resize(33, 0x90);
386 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
387 let file = object::File::parse(&bytes[..]).expect("a readable object");
388 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
389 assert!(hidden.is_local(), "a static function must not be offered to the linker");
392 assert!(!hidden.is_weak());
393 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
394 assert!(shared.is_weak(), "a weak function has to be able to lose");
395 assert!(shared.is_global());
396 }
397
398 #[test]
409 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
410 let mut text = calling("puts");
411 text.funcs.push(Extent {
412 name: "g".to_owned(),
413 start: 16,
414 len: 1,
415 binding: Binding::Global,
416 });
417 text.funcs.push(Extent { name: "w".to_owned(), start: 32, len: 1, binding: Binding::Weak });
418 text.funcs.push(Extent {
419 name: "s".to_owned(),
420 start: 48,
421 len: 1,
422 binding: Binding::Local,
423 });
424 text.bytes.resize(49, 0x90);
425 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
426 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
427 let visibility = |name: &str| {
428 file.symbols()
429 .find(|s| s.name() == Ok(name))
430 .expect("the function")
431 .elf_symbol()
432 .st_visibility()
433 };
434 assert_eq!(visibility("g"), elf::STV_DEFAULT);
436 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
437 assert_eq!(visibility("s"), elf::STV_DEFAULT);
440 }
441
442 #[test]
443 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
444 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
445 let file = object::File::parse(&bytes[..]).expect("a readable object");
446 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
447 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
448 }
449
450 #[test]
451 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
452 for (reference, wanted) in [
453 (Reference::Call, elf::R_X86_64_PLT32),
454 (Reference::Data, elf::R_X86_64_PC32),
455 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
456 ] {
457 let mut text = calling("puts");
458 text.relocs[0].kind = reference;
459 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
460 let file = object::File::parse(&bytes[..]).expect("a readable object");
461 let section = file.section_by_name(".text").expect("a text section");
462 let (offset, reloc) = section.relocations().next().expect("one relocation");
463 assert_eq!(offset, 1);
464 assert_eq!(reloc.addend(), -4);
465 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
466 }
467 }
468
469 #[test]
470 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
471 let mut text = calling("puts");
472 text.relocs.push(Reloc {
473 at: 1,
474 symbol: "puts".to_owned(),
475 kind: Reference::Call,
476 addend: -4,
477 });
478 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
479 let file = object::File::parse(&bytes[..]).expect("a readable object");
480 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
481 }
482
483 #[test]
484 fn a_function_that_is_also_called_is_not_a_second_symbol() {
485 let text = calling("f");
486 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
487 let file = object::File::parse(&bytes[..]).expect("a readable object");
488 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
489 let f = found.next().expect("the function");
490 assert!(!f.is_undefined(), "the file defines it");
491 assert!(found.next().is_none(), "and defines it once");
492 }
493
494 #[test]
495 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
496 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
497 let file = object::File::parse(&bytes[..]).expect("a readable object");
498 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
499 assert!(note.data().expect("no bytes").is_empty());
500 }
501
502 fn variable(name: &str, place: Place) -> Object {
504 Object {
505 name: name.to_owned(),
506 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
507 size: 4,
508 align: 4,
509 place,
510 binding: Binding::Global,
511 relocs: Vec::new(),
512 }
513 }
514
515 fn holding(object: Object) -> Vec<u8> {
517 let data = Data { objects: vec![object] };
518 write(&Text::default(), &data, &[], &target()).expect("an object")
519 }
520
521 #[test]
522 fn what_a_variable_is_decides_which_section_it_goes_in() {
523 for (place, wanted) in [
524 (Place::Written, ".data"),
525 (Place::ReadOnly, ".rodata"),
526 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
527 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
528 (Place::Zero, ".bss"),
529 (Place::Named(".init_array".to_owned()), ".init_array"),
530 ] {
531 let bytes = holding(variable("x", place.clone()));
532 let file = object::File::parse(&bytes[..]).expect("a readable object");
533 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
534 assert_eq!(section.size(), 4, "{place:?}");
535 let carried = section.data().expect("the bytes").len();
538 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
539 }
540 }
541
542 #[test]
550 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
551 let place = Place::RelocReadOnly { local: true };
552 let data =
553 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
554 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
555 let file = object::File::parse(&bytes[..]).expect("a readable object");
556 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
557 assert_eq!(named, 1, "one section holding both, not one each");
558 }
559
560 #[test]
561 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
562 let mut data = Data { objects: vec![variable("first", Place::Written)] };
563 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
564 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
565 let file = object::File::parse(&bytes[..]).expect("a readable object");
566 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
567 assert_eq!(second.kind(), SymbolKind::Data);
568 assert_eq!(second.size(), 4);
569 assert_eq!(second.address(), 16);
573 }
574
575 #[test]
576 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
577 for (binding, global, weak) in [
578 (Binding::Global, true, false),
579 (Binding::Local, false, false),
580 (Binding::Weak, true, true),
581 ] {
582 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
583 let file = object::File::parse(&bytes[..]).expect("a readable object");
584 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
585 assert_eq!(x.is_global(), global, "{binding:?}");
586 assert_eq!(x.is_weak(), weak, "{binding:?}");
587 }
588 }
589
590 #[test]
591 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
592 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
593 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
594 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
595 assert!(x.is_common(), "the linker merges every definition of this name into one");
596 assert_eq!(x.size(), 4);
597 assert_eq!(x.address(), 0);
601 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
602 }
603
604 #[test]
605 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
606 let object = Object {
607 bytes: vec![0; 8],
608 size: 8,
609 align: 8,
610 relocs: vec![Reloc {
611 at: 0,
612 symbol: "y".to_owned(),
613 kind: Reference::Address { bytes: 8 },
614 addend: 16,
615 }],
616 ..variable("p", Place::Written)
617 };
618 let bytes = holding(object);
619 let file = object::File::parse(&bytes[..]).expect("a readable object");
620 let section = file.section_by_name(".data").expect("a data section");
621 let (offset, reloc) = section.relocations().next().expect("one relocation");
622 assert_eq!(offset, 0);
623 assert_eq!(reloc.addend(), 16);
624 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
625 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
626 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
627 }
628
629 #[test]
631 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
632 let mut data = Data { objects: vec![variable("first", Place::Written)] };
633 data.objects.push(Object {
634 bytes: vec![0; 16],
635 size: 16,
636 align: 8,
637 relocs: vec![Reloc {
638 at: 8,
639 symbol: "y".to_owned(),
640 kind: Reference::Address { bytes: 8 },
641 addend: 0,
642 }],
643 ..variable("second", Place::Written)
644 });
645 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
646 let file = object::File::parse(&bytes[..]).expect("a readable object");
647 let section = file.section_by_name(".data").expect("a data section");
648 let (offset, _) = section.relocations().next().expect("one relocation");
649 assert_eq!(offset, 16);
652 }
653
654 #[test]
655 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
656 let data = Data {
657 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
658 };
659 let aliases =
660 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
661 let bytes = write(&Text::default(), &data, &aliases, &target()).expect("an object");
662 let file = object::File::parse(&bytes[..]).expect("a readable object");
663 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
664 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
665 assert_eq!(b.address(), a.address(), "the same place");
666 assert_eq!(b.size(), a.size());
667 assert_eq!(b.section_index(), a.section_index());
668 assert!(a.is_local(), "the target was written `static`");
671 assert!(b.is_global(), "and the name given to it was not");
672 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
674 }
675
676 #[test]
677 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
678 let text = calling("puts");
679 let aliases =
680 [Alias { name: "g".to_owned(), target: "f".to_owned(), binding: Binding::Weak }];
681 let bytes = write(&text, &Data::default(), &aliases, &target()).expect("an object");
682 let file = object::File::parse(&bytes[..]).expect("a readable object");
683 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
684 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
685 assert_eq!(g.address(), f.address());
686 assert_eq!(g.size(), f.size());
687 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
688 assert!(g.is_weak(), "so that a program may define the name itself instead");
689 }
690
691 #[test]
694 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
695 let aliases =
696 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
697 let error = write(&Text::default(), &Data::default(), &aliases, &target())
698 .expect_err("nothing to point at");
699 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
700 }
701
702 #[test]
703 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
704 let text = calling("puts");
705 for triple in [
706 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
707 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
708 ] {
709 let error = write(&text, &Data::default(), &[], &TargetInfo::new(triple))
710 .expect_err("no writer");
711 assert!(matches!(error, Error::Format { .. }), "{error:?}");
712 }
713 }
714}