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 {
268 match binding {
269 Binding::Local => SymbolScope::Compilation,
270 Binding::Global | Binding::Weak => SymbolScope::Linkage,
274 }
275}
276
277fn r_type(reference: Reference) -> Option<elf::RelocationType> {
288 Some(match reference {
289 Reference::Call => elf::R_X86_64_PLT32,
290 Reference::Data => elf::R_X86_64_PC32,
291 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
292 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
293 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
294 Reference::Address { .. } => return None,
295 })
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 use object::read::elf::Sym as _;
303 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
304 use rucc_target::{Arch, Env, Os, Triple};
305
306 use crate::section::{Extent, Reloc};
307
308 fn target() -> TargetInfo {
310 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
311 }
312
313 fn calling(name: &str) -> Text {
315 Text {
316 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
317 funcs: vec![Extent {
318 name: "f".to_owned(),
319 start: 0,
320 len: 6,
321 binding: Binding::Global,
322 }],
323 relocs: vec![Reloc {
324 at: 1,
325 symbol: name.to_owned(),
326 kind: Reference::Call,
327 addend: -4,
328 }],
329 ..Text::default()
330 }
331 }
332
333 #[test]
334 fn the_bytes_come_back_out_of_the_section_they_went_into() {
335 let text = calling("puts");
336 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
337 let file = object::File::parse(&bytes[..]).expect("a readable object");
338 let section = file.section_by_name(".text").expect("a text section");
339 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
340 }
341
342 #[test]
343 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
344 let mut text = calling("puts");
345 text.funcs.push(Extent {
346 name: "g".to_owned(),
347 start: 16,
348 len: 1,
349 binding: Binding::Global,
350 });
351 text.bytes.resize(17, 0x90);
352 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
353 let file = object::File::parse(&bytes[..]).expect("a readable object");
354 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
355 assert_eq!(g.address(), 16);
356 assert_eq!(g.size(), 1);
357 assert_eq!(g.kind(), SymbolKind::Text);
358 assert!(g.is_global(), "nothing said otherwise about this one");
359 }
360
361 #[test]
362 fn a_function_no_other_file_can_see_is_a_local_symbol() {
363 let mut text = calling("puts");
364 text.funcs.push(Extent {
365 name: "hidden".to_owned(),
366 start: 16,
367 len: 1,
368 binding: Binding::Local,
369 });
370 text.funcs.push(Extent {
371 name: "shared".to_owned(),
372 start: 32,
373 len: 1,
374 binding: Binding::Weak,
375 });
376 text.bytes.resize(33, 0x90);
377 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
378 let file = object::File::parse(&bytes[..]).expect("a readable object");
379 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
380 assert!(hidden.is_local(), "a static function must not be offered to the linker");
383 assert!(!hidden.is_weak());
384 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
385 assert!(shared.is_weak(), "a weak function has to be able to lose");
386 assert!(shared.is_global());
387 }
388
389 #[test]
390 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
391 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
392 let file = object::File::parse(&bytes[..]).expect("a readable object");
393 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
394 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
395 }
396
397 #[test]
398 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
399 for (reference, wanted) in [
400 (Reference::Call, elf::R_X86_64_PLT32),
401 (Reference::Data, elf::R_X86_64_PC32),
402 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
403 ] {
404 let mut text = calling("puts");
405 text.relocs[0].kind = reference;
406 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
407 let file = object::File::parse(&bytes[..]).expect("a readable object");
408 let section = file.section_by_name(".text").expect("a text section");
409 let (offset, reloc) = section.relocations().next().expect("one relocation");
410 assert_eq!(offset, 1);
411 assert_eq!(reloc.addend(), -4);
412 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
413 }
414 }
415
416 #[test]
417 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
418 let mut text = calling("puts");
419 text.relocs.push(Reloc {
420 at: 1,
421 symbol: "puts".to_owned(),
422 kind: Reference::Call,
423 addend: -4,
424 });
425 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
426 let file = object::File::parse(&bytes[..]).expect("a readable object");
427 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
428 }
429
430 #[test]
431 fn a_function_that_is_also_called_is_not_a_second_symbol() {
432 let text = calling("f");
433 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
434 let file = object::File::parse(&bytes[..]).expect("a readable object");
435 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
436 let f = found.next().expect("the function");
437 assert!(!f.is_undefined(), "the file defines it");
438 assert!(found.next().is_none(), "and defines it once");
439 }
440
441 #[test]
442 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
443 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
444 let file = object::File::parse(&bytes[..]).expect("a readable object");
445 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
446 assert!(note.data().expect("no bytes").is_empty());
447 }
448
449 fn variable(name: &str, place: Place) -> Object {
451 Object {
452 name: name.to_owned(),
453 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
454 size: 4,
455 align: 4,
456 place,
457 binding: Binding::Global,
458 relocs: Vec::new(),
459 }
460 }
461
462 fn holding(object: Object) -> Vec<u8> {
464 let data = Data { objects: vec![object] };
465 write(&Text::default(), &data, &[], &target()).expect("an object")
466 }
467
468 #[test]
469 fn what_a_variable_is_decides_which_section_it_goes_in() {
470 for (place, wanted) in [
471 (Place::Written, ".data"),
472 (Place::ReadOnly, ".rodata"),
473 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
474 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
475 (Place::Zero, ".bss"),
476 (Place::Named(".init_array".to_owned()), ".init_array"),
477 ] {
478 let bytes = holding(variable("x", place.clone()));
479 let file = object::File::parse(&bytes[..]).expect("a readable object");
480 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
481 assert_eq!(section.size(), 4, "{place:?}");
482 let carried = section.data().expect("the bytes").len();
485 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
486 }
487 }
488
489 #[test]
497 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
498 let place = Place::RelocReadOnly { local: true };
499 let data =
500 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
501 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
502 let file = object::File::parse(&bytes[..]).expect("a readable object");
503 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
504 assert_eq!(named, 1, "one section holding both, not one each");
505 }
506
507 #[test]
508 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
509 let mut data = Data { objects: vec![variable("first", Place::Written)] };
510 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
511 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
512 let file = object::File::parse(&bytes[..]).expect("a readable object");
513 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
514 assert_eq!(second.kind(), SymbolKind::Data);
515 assert_eq!(second.size(), 4);
516 assert_eq!(second.address(), 16);
520 }
521
522 #[test]
523 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
524 for (binding, global, weak) in [
525 (Binding::Global, true, false),
526 (Binding::Local, false, false),
527 (Binding::Weak, true, true),
528 ] {
529 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
530 let file = object::File::parse(&bytes[..]).expect("a readable object");
531 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
532 assert_eq!(x.is_global(), global, "{binding:?}");
533 assert_eq!(x.is_weak(), weak, "{binding:?}");
534 }
535 }
536
537 #[test]
538 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
539 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
540 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
541 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
542 assert!(x.is_common(), "the linker merges every definition of this name into one");
543 assert_eq!(x.size(), 4);
544 assert_eq!(x.address(), 0);
548 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
549 }
550
551 #[test]
552 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
553 let object = Object {
554 bytes: vec![0; 8],
555 size: 8,
556 align: 8,
557 relocs: vec![Reloc {
558 at: 0,
559 symbol: "y".to_owned(),
560 kind: Reference::Address { bytes: 8 },
561 addend: 16,
562 }],
563 ..variable("p", Place::Written)
564 };
565 let bytes = holding(object);
566 let file = object::File::parse(&bytes[..]).expect("a readable object");
567 let section = file.section_by_name(".data").expect("a data section");
568 let (offset, reloc) = section.relocations().next().expect("one relocation");
569 assert_eq!(offset, 0);
570 assert_eq!(reloc.addend(), 16);
571 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
572 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
573 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
574 }
575
576 #[test]
578 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
579 let mut data = Data { objects: vec![variable("first", Place::Written)] };
580 data.objects.push(Object {
581 bytes: vec![0; 16],
582 size: 16,
583 align: 8,
584 relocs: vec![Reloc {
585 at: 8,
586 symbol: "y".to_owned(),
587 kind: Reference::Address { bytes: 8 },
588 addend: 0,
589 }],
590 ..variable("second", Place::Written)
591 });
592 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
593 let file = object::File::parse(&bytes[..]).expect("a readable object");
594 let section = file.section_by_name(".data").expect("a data section");
595 let (offset, _) = section.relocations().next().expect("one relocation");
596 assert_eq!(offset, 16);
599 }
600
601 #[test]
602 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
603 let data = Data {
604 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
605 };
606 let aliases =
607 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
608 let bytes = write(&Text::default(), &data, &aliases, &target()).expect("an object");
609 let file = object::File::parse(&bytes[..]).expect("a readable object");
610 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
611 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
612 assert_eq!(b.address(), a.address(), "the same place");
613 assert_eq!(b.size(), a.size());
614 assert_eq!(b.section_index(), a.section_index());
615 assert!(a.is_local(), "the target was written `static`");
618 assert!(b.is_global(), "and the name given to it was not");
619 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
621 }
622
623 #[test]
624 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
625 let text = calling("puts");
626 let aliases =
627 [Alias { name: "g".to_owned(), target: "f".to_owned(), binding: Binding::Weak }];
628 let bytes = write(&text, &Data::default(), &aliases, &target()).expect("an object");
629 let file = object::File::parse(&bytes[..]).expect("a readable object");
630 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
631 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
632 assert_eq!(g.address(), f.address());
633 assert_eq!(g.size(), f.size());
634 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
635 assert!(g.is_weak(), "so that a program may define the name itself instead");
636 }
637
638 #[test]
641 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
642 let aliases =
643 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
644 let error = write(&Text::default(), &Data::default(), &aliases, &target())
645 .expect_err("nothing to point at");
646 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
647 }
648
649 #[test]
650 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
651 let text = calling("puts");
652 for triple in [
653 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
654 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
655 ] {
656 let error = write(&text, &Data::default(), &[], &TargetInfo::new(triple))
657 .expect_err("no writer");
658 assert!(matches!(error, Error::Format { .. }), "{error:?}");
659 }
660 }
661}