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 for object in &data.objects {
112 let (section, offset) = put(&mut obj, object);
113 let id = obj.add_symbol(Symbol {
114 name: object.name.clone().into_bytes(),
115 value: if object.place == Place::Merged { object.align } else { offset },
118 size: object.size,
119 kind: SymbolKind::Data,
120 scope: scope_of(object.binding),
121 weak: object.binding == Binding::Weak,
122 section,
123 flags: SymbolFlags::None,
124 });
125 symbols.insert(object.name.clone(), id);
126 placed.push((section.id(), offset));
127 }
128
129 for alias in aliases {
135 let Some(&id) = symbols.get(&alias.target) else {
136 let why =
137 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
138 return Err(Error::Refused { why });
139 };
140 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
141 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
142 let id = obj.add_symbol(Symbol {
143 name: alias.name.clone().into_bytes(),
144 value,
145 size,
146 kind,
147 scope: scope_of(alias.binding),
148 weak: alias.binding == Binding::Weak,
149 section,
150 flags: SymbolFlags::None,
151 });
152 symbols.insert(alias.name.clone(), id);
153 }
154
155 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
156 for reloc in wanted {
157 if symbols.contains_key(&reloc.symbol) {
158 continue;
159 }
160 let id = obj.add_symbol(Symbol {
161 name: reloc.symbol.clone().into_bytes(),
162 value: 0,
163 size: 0,
164 kind: SymbolKind::Unknown,
168 scope: SymbolScope::Dynamic,
169 weak: false,
170 section: SymbolSection::Undefined,
171 flags: SymbolFlags::None,
172 });
173 symbols.insert(reloc.symbol.clone(), id);
174 }
175
176 for reloc in &text.relocs {
177 add(&mut obj, section, 0, reloc, &symbols)?;
178 }
179 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
180 let Some(section) = section else { continue };
181 for reloc in &object.relocs {
182 add(&mut obj, section, offset, reloc, &symbols)?;
183 }
184 }
185
186 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
189
190 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
191}
192
193fn put(obj: &mut Writer<'_>, object: &Object) -> (SymbolSection, u64) {
200 let section = match &object.place {
201 Place::Written => obj.section_id(StandardSection::Data),
202 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
203 Place::Zero => obj.section_id(StandardSection::UninitializedData),
204 Place::Merged => return (SymbolSection::Common, 0),
205 Place::Named(name) => {
209 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
210 }
211 };
212 let offset = if object.place == Place::Zero {
213 obj.append_section_bss(section, object.size, object.align)
214 } else {
215 obj.append_section_data(section, &object.bytes, object.align)
216 };
217 (SymbolSection::Section(section), offset)
218}
219
220fn add(
222 obj: &mut Writer<'_>,
223 section: object::write::SectionId,
224 offset: u64,
225 reloc: &Reloc,
226 symbols: &std::collections::BTreeMap<String, object::write::SymbolId>,
227) -> Result<(), Error> {
228 let r_type = r_type(reloc.kind)
229 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
230 obj.add_relocation(
231 section,
232 Relocation {
233 offset: offset + reloc.at as u64,
234 symbol: symbols[&reloc.symbol],
235 addend: reloc.addend,
236 flags: RelocationFlags::Elf { r_type },
237 },
238 )
239 .map_err(|why| Error::Refused { why: why.to_string() })
240}
241
242fn scope_of(binding: Binding) -> SymbolScope {
244 match binding {
245 Binding::Local => SymbolScope::Compilation,
246 Binding::Global | Binding::Weak => SymbolScope::Linkage,
250 }
251}
252
253fn r_type(reference: Reference) -> Option<elf::RelocationType> {
264 Some(match reference {
265 Reference::Call => elf::R_X86_64_PLT32,
266 Reference::Data => elf::R_X86_64_PC32,
267 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
268 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
269 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
270 Reference::Address { .. } => return None,
271 })
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 use object::read::elf::Sym as _;
279 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
280 use rucc_target::{Env, Triple};
281
282 use crate::section::{Extent, Reloc};
283
284 fn target() -> TargetInfo {
286 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
287 }
288
289 fn calling(name: &str) -> Text {
291 Text {
292 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
293 funcs: vec![Extent {
294 name: "f".to_owned(),
295 start: 0,
296 len: 6,
297 binding: Binding::Global,
298 }],
299 relocs: vec![Reloc {
300 at: 1,
301 symbol: name.to_owned(),
302 kind: Reference::Call,
303 addend: -4,
304 }],
305 ..Text::default()
306 }
307 }
308
309 #[test]
310 fn the_bytes_come_back_out_of_the_section_they_went_into() {
311 let text = calling("puts");
312 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
313 let file = object::File::parse(&bytes[..]).expect("a readable object");
314 let section = file.section_by_name(".text").expect("a text section");
315 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
316 }
317
318 #[test]
319 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
320 let mut text = calling("puts");
321 text.funcs.push(Extent {
322 name: "g".to_owned(),
323 start: 16,
324 len: 1,
325 binding: Binding::Global,
326 });
327 text.bytes.resize(17, 0x90);
328 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
329 let file = object::File::parse(&bytes[..]).expect("a readable object");
330 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
331 assert_eq!(g.address(), 16);
332 assert_eq!(g.size(), 1);
333 assert_eq!(g.kind(), SymbolKind::Text);
334 assert!(g.is_global(), "nothing said otherwise about this one");
335 }
336
337 #[test]
338 fn a_function_no_other_file_can_see_is_a_local_symbol() {
339 let mut text = calling("puts");
340 text.funcs.push(Extent {
341 name: "hidden".to_owned(),
342 start: 16,
343 len: 1,
344 binding: Binding::Local,
345 });
346 text.funcs.push(Extent {
347 name: "shared".to_owned(),
348 start: 32,
349 len: 1,
350 binding: Binding::Weak,
351 });
352 text.bytes.resize(33, 0x90);
353 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
354 let file = object::File::parse(&bytes[..]).expect("a readable object");
355 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
356 assert!(hidden.is_local(), "a static function must not be offered to the linker");
359 assert!(!hidden.is_weak());
360 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
361 assert!(shared.is_weak(), "a weak function has to be able to lose");
362 assert!(shared.is_global());
363 }
364
365 #[test]
366 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
367 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
368 let file = object::File::parse(&bytes[..]).expect("a readable object");
369 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
370 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
371 }
372
373 #[test]
374 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
375 for (reference, wanted) in [
376 (Reference::Call, elf::R_X86_64_PLT32),
377 (Reference::Data, elf::R_X86_64_PC32),
378 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
379 ] {
380 let mut text = calling("puts");
381 text.relocs[0].kind = reference;
382 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
383 let file = object::File::parse(&bytes[..]).expect("a readable object");
384 let section = file.section_by_name(".text").expect("a text section");
385 let (offset, reloc) = section.relocations().next().expect("one relocation");
386 assert_eq!(offset, 1);
387 assert_eq!(reloc.addend(), -4);
388 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
389 }
390 }
391
392 #[test]
393 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
394 let mut text = calling("puts");
395 text.relocs.push(Reloc {
396 at: 1,
397 symbol: "puts".to_owned(),
398 kind: Reference::Call,
399 addend: -4,
400 });
401 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
402 let file = object::File::parse(&bytes[..]).expect("a readable object");
403 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
404 }
405
406 #[test]
407 fn a_function_that_is_also_called_is_not_a_second_symbol() {
408 let text = calling("f");
409 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
410 let file = object::File::parse(&bytes[..]).expect("a readable object");
411 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
412 let f = found.next().expect("the function");
413 assert!(!f.is_undefined(), "the file defines it");
414 assert!(found.next().is_none(), "and defines it once");
415 }
416
417 #[test]
418 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
419 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
420 let file = object::File::parse(&bytes[..]).expect("a readable object");
421 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
422 assert!(note.data().expect("no bytes").is_empty());
423 }
424
425 fn variable(name: &str, place: Place) -> Object {
427 Object {
428 name: name.to_owned(),
429 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
430 size: 4,
431 align: 4,
432 place,
433 binding: Binding::Global,
434 relocs: Vec::new(),
435 }
436 }
437
438 fn holding(object: Object) -> Vec<u8> {
440 let data = Data { objects: vec![object] };
441 write(&Text::default(), &data, &[], &target()).expect("an object")
442 }
443
444 #[test]
445 fn what_a_variable_is_decides_which_section_it_goes_in() {
446 for (place, wanted) in [
447 (Place::Written, ".data"),
448 (Place::ReadOnly, ".rodata"),
449 (Place::Zero, ".bss"),
450 (Place::Named(".init_array".to_owned()), ".init_array"),
451 ] {
452 let bytes = holding(variable("x", place.clone()));
453 let file = object::File::parse(&bytes[..]).expect("a readable object");
454 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
455 assert_eq!(section.size(), 4, "{place:?}");
456 let carried = section.data().expect("the bytes").len();
459 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
460 }
461 }
462
463 #[test]
464 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
465 let mut data = Data { objects: vec![variable("first", Place::Written)] };
466 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
467 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
468 let file = object::File::parse(&bytes[..]).expect("a readable object");
469 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
470 assert_eq!(second.kind(), SymbolKind::Data);
471 assert_eq!(second.size(), 4);
472 assert_eq!(second.address(), 16);
476 }
477
478 #[test]
479 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
480 for (binding, global, weak) in [
481 (Binding::Global, true, false),
482 (Binding::Local, false, false),
483 (Binding::Weak, true, true),
484 ] {
485 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
486 let file = object::File::parse(&bytes[..]).expect("a readable object");
487 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
488 assert_eq!(x.is_global(), global, "{binding:?}");
489 assert_eq!(x.is_weak(), weak, "{binding:?}");
490 }
491 }
492
493 #[test]
494 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
495 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
496 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
497 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
498 assert!(x.is_common(), "the linker merges every definition of this name into one");
499 assert_eq!(x.size(), 4);
500 assert_eq!(x.address(), 0);
504 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
505 }
506
507 #[test]
508 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
509 let object = Object {
510 bytes: vec![0; 8],
511 size: 8,
512 align: 8,
513 relocs: vec![Reloc {
514 at: 0,
515 symbol: "y".to_owned(),
516 kind: Reference::Address { bytes: 8 },
517 addend: 16,
518 }],
519 ..variable("p", Place::Written)
520 };
521 let bytes = holding(object);
522 let file = object::File::parse(&bytes[..]).expect("a readable object");
523 let section = file.section_by_name(".data").expect("a data section");
524 let (offset, reloc) = section.relocations().next().expect("one relocation");
525 assert_eq!(offset, 0);
526 assert_eq!(reloc.addend(), 16);
527 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
528 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
529 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
530 }
531
532 #[test]
534 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
535 let mut data = Data { objects: vec![variable("first", Place::Written)] };
536 data.objects.push(Object {
537 bytes: vec![0; 16],
538 size: 16,
539 align: 8,
540 relocs: vec![Reloc {
541 at: 8,
542 symbol: "y".to_owned(),
543 kind: Reference::Address { bytes: 8 },
544 addend: 0,
545 }],
546 ..variable("second", Place::Written)
547 });
548 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
549 let file = object::File::parse(&bytes[..]).expect("a readable object");
550 let section = file.section_by_name(".data").expect("a data section");
551 let (offset, _) = section.relocations().next().expect("one relocation");
552 assert_eq!(offset, 16);
555 }
556
557 #[test]
558 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
559 let data = Data {
560 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
561 };
562 let aliases =
563 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
564 let bytes = write(&Text::default(), &data, &aliases, &target()).expect("an object");
565 let file = object::File::parse(&bytes[..]).expect("a readable object");
566 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
567 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
568 assert_eq!(b.address(), a.address(), "the same place");
569 assert_eq!(b.size(), a.size());
570 assert_eq!(b.section_index(), a.section_index());
571 assert!(a.is_local(), "the target was written `static`");
574 assert!(b.is_global(), "and the name given to it was not");
575 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
577 }
578
579 #[test]
580 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
581 let text = calling("puts");
582 let aliases =
583 [Alias { name: "g".to_owned(), target: "f".to_owned(), binding: Binding::Weak }];
584 let bytes = write(&text, &Data::default(), &aliases, &target()).expect("an object");
585 let file = object::File::parse(&bytes[..]).expect("a readable object");
586 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
587 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
588 assert_eq!(g.address(), f.address());
589 assert_eq!(g.size(), f.size());
590 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
591 assert!(g.is_weak(), "so that a program may define the name itself instead");
592 }
593
594 #[test]
597 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
598 let aliases =
599 [Alias { name: "b".to_owned(), target: "a".to_owned(), binding: Binding::Global }];
600 let error = write(&Text::default(), &Data::default(), &aliases, &target())
601 .expect_err("nothing to point at");
602 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
603 }
604
605 #[test]
606 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
607 let text = calling("puts");
608 for triple in [
609 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
610 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
611 ] {
612 let error = write(&text, &Data::default(), &[], &TargetInfo::new(triple))
613 .expect_err("no writer");
614 assert!(matches!(error, Error::Format { .. }), "{error:?}");
615 }
616 }
617}