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