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