1use object::write::{
29 Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
30};
31use object::{
32 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionKind, SymbolFlags, SymbolKind,
33 SymbolScope, elf,
34};
35use rucc_target::{ObjectFormat, TargetInfo};
36use rucc_tuple::Arch;
37
38use crate::section::{Alias, Binding, Data, Object, Place, Reference, Reloc, Text, Visibility};
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum Error {
43 Format {
45 triple: String,
47 },
48 Refused {
50 why: String,
52 },
53}
54
55impl std::fmt::Display for Error {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Error::Format { triple } => {
59 write!(f, "there is no object writer for {triple} in this compiler yet")
60 }
61 Error::Refused { why } => {
62 write!(f, "the object writer refused what it was given: {why}")
63 }
64 }
65 }
66}
67
68impl std::error::Error for Error {}
69
70pub fn write(
79 text: &Text,
80 data: &Data,
81 aliases: &[Alias],
82 target: &TargetInfo,
83) -> Result<Vec<u8>, Error> {
84 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
85 return Err(Error::Format { triple: target.tuple.to_string() });
86 }
87 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
88 let section = obj.section_id(StandardSection::Text);
89 obj.append_section_data(section, &text.bytes, u64::from(text.align));
90
91 let mut symbols = std::collections::BTreeMap::new();
95 for func in &text.funcs {
96 let id = obj.add_symbol(Symbol {
97 name: func.name.clone().into_bytes(),
98 value: func.start as u64,
99 size: func.len as u64,
100 kind: SymbolKind::Text,
101 scope: scope_of(func.binding),
102 weak: func.binding == Binding::Weak,
103 section: SymbolSection::Section(section),
104 flags: SymbolFlags::None,
105 });
106 see(&mut obj, id, func.binding, func.visibility);
107 symbols.insert(func.name.clone(), id);
108 }
109
110 let mut placed = Vec::with_capacity(data.objects.len());
115 let mut local = None;
119 for object in &data.objects {
120 let (section, offset) = put(&mut obj, object, &mut local);
121 let id = obj.add_symbol(Symbol {
122 name: object.name.clone().into_bytes(),
123 value: if object.place == Place::Merged { object.align } else { offset },
126 size: object.size,
127 kind: SymbolKind::Data,
128 scope: scope_of(object.binding),
129 weak: object.binding == Binding::Weak,
130 section,
131 flags: SymbolFlags::None,
132 });
133 see(&mut obj, id, object.binding, object.visibility);
134 symbols.insert(object.name.clone(), id);
135 placed.push((section.id(), offset));
136 }
137
138 for alias in aliases {
144 let Some(&id) = symbols.get(&alias.target) else {
145 let why =
146 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
147 return Err(Error::Refused { why });
148 };
149 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
150 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
151 let id = obj.add_symbol(Symbol {
152 name: alias.name.clone().into_bytes(),
153 value,
154 size,
155 kind,
156 scope: scope_of(alias.binding),
157 weak: alias.binding == Binding::Weak,
158 section,
159 flags: SymbolFlags::None,
160 });
161 see(&mut obj, id, alias.binding, alias.visibility);
162 symbols.insert(alias.name.clone(), id);
163 }
164
165 let wanted = text
166 .relocs
167 .iter()
168 .chain(text.unwind.relocs.iter())
169 .chain(data.objects.iter().flat_map(|object| &object.relocs));
170 for reloc in wanted {
171 if symbols.contains_key(&reloc.symbol) {
172 continue;
173 }
174 let id = obj.add_symbol(Symbol {
175 name: reloc.symbol.clone().into_bytes(),
176 value: 0,
177 size: 0,
178 kind: SymbolKind::Unknown,
182 scope: SymbolScope::Dynamic,
183 weak: false,
184 section: SymbolSection::Undefined,
185 flags: SymbolFlags::None,
186 });
187 symbols.insert(reloc.symbol.clone(), id);
188 }
189
190 for reloc in &text.relocs {
191 add(&mut obj, section, 0, reloc, &symbols)?;
192 }
193
194 if !text.unwind.bytes.is_empty() {
200 let frames = obj.add_section(Vec::new(), b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
201 obj.append_section_data(frames, &text.unwind.bytes, 8);
202 for reloc in &text.unwind.relocs {
203 add(&mut obj, frames, 0, reloc, &symbols)?;
204 }
205 }
206 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
207 let Some(section) = section else { continue };
208 for reloc in &object.relocs {
209 add(&mut obj, section, offset, reloc, &symbols)?;
210 }
211 }
212
213 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
216
217 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
218}
219
220fn put(
227 obj: &mut Writer<'_>,
228 object: &Object,
229 local: &mut Option<object::write::SectionId>,
230) -> (SymbolSection, u64) {
231 let section = match &object.place {
232 Place::Written => obj.section_id(StandardSection::Data),
233 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
234 Place::RelocReadOnly { local: false } => {
240 obj.section_id(StandardSection::ReadOnlyDataWithRel)
241 }
242 Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
243 obj.add_section(
244 Vec::new(),
245 b".data.rel.ro.local".to_vec(),
246 SectionKind::ReadOnlyDataWithRel,
247 )
248 }),
249 Place::Zero => obj.section_id(StandardSection::UninitializedData),
250 Place::Merged => return (SymbolSection::Common, 0),
251 Place::Named(name) => {
255 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
256 }
257 };
258 let offset = if object.place == Place::Zero {
259 obj.append_section_bss(section, object.size, object.align)
260 } else {
261 obj.append_section_data(section, &object.bytes, object.align)
262 };
263 (SymbolSection::Section(section), offset)
264}
265
266fn add(
268 obj: &mut Writer<'_>,
269 section: object::write::SectionId,
270 offset: u64,
271 reloc: &Reloc,
272 symbols: &std::collections::BTreeMap<String, SymbolId>,
273) -> Result<(), Error> {
274 let r_type = r_type(reloc.kind)
275 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
276 obj.add_relocation(
277 section,
278 Relocation {
279 offset: offset + reloc.at as u64,
280 symbol: symbols[&reloc.symbol],
281 addend: reloc.addend,
282 flags: RelocationFlags::Elf { r_type },
283 },
284 )
285 .map_err(|why| Error::Refused { why: why.to_string() })
286}
287
288fn scope_of(binding: Binding) -> SymbolScope {
301 match binding {
302 Binding::Local => SymbolScope::Compilation,
303 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
304 }
305}
306
307fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
319 if binding == Binding::Local {
320 return;
321 }
322 let wanted = match visibility {
323 Visibility::Default => elf::STV_DEFAULT,
324 Visibility::Hidden => elf::STV_HIDDEN,
325 Visibility::Protected => elf::STV_PROTECTED,
326 };
327 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
328 *st_other = st_other.with_visibility(wanted);
329 }
330}
331
332fn r_type(reference: Reference) -> Option<elf::RelocationType> {
343 Some(match reference {
344 Reference::Call => elf::R_X86_64_PLT32,
345 Reference::Data => elf::R_X86_64_PC32,
346 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
347 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
348 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
349 Reference::Address { .. } => return None,
350 })
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 use object::read::elf::Sym as _;
358 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
359 use rucc_target::{Arch, Env, Os, Triple};
360
361 use crate::section::{Extent, Reloc};
362
363 fn target() -> TargetInfo {
365 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
366 }
367
368 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
373 Extent { name, start, len, binding, visibility: Visibility::Default }
374 }
375
376 fn calling(name: &str) -> Text {
378 Text {
379 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
380 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
381 relocs: vec![Reloc {
382 at: 1,
383 symbol: name.to_owned(),
384 kind: Reference::Call,
385 addend: -4,
386 }],
387 ..Text::default()
388 }
389 }
390
391 #[test]
392 fn the_bytes_come_back_out_of_the_section_they_went_into() {
393 let text = calling("puts");
394 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
395 let file = object::File::parse(&bytes[..]).expect("a readable object");
396 let section = file.section_by_name(".text").expect("a text section");
397 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
398 }
399
400 #[test]
401 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
402 let mut text = calling("puts");
403 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
404 text.bytes.resize(17, 0x90);
405 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
406 let file = object::File::parse(&bytes[..]).expect("a readable object");
407 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
408 assert_eq!(g.address(), 16);
409 assert_eq!(g.size(), 1);
410 assert_eq!(g.kind(), SymbolKind::Text);
411 assert!(g.is_global(), "nothing said otherwise about this one");
412 }
413
414 #[test]
415 fn a_function_no_other_file_can_see_is_a_local_symbol() {
416 let mut text = calling("puts");
417 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
418 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
419 text.bytes.resize(33, 0x90);
420 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
421 let file = object::File::parse(&bytes[..]).expect("a readable object");
422 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
423 assert!(hidden.is_local(), "a static function must not be offered to the linker");
426 assert!(!hidden.is_weak());
427 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
428 assert!(shared.is_weak(), "a weak function has to be able to lose");
429 assert!(shared.is_global());
430 }
431
432 #[test]
443 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
444 let mut text = calling("puts");
445 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
446 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
447 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
448 text.bytes.resize(49, 0x90);
449 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
450 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
451 let visibility = |name: &str| {
452 file.symbols()
453 .find(|s| s.name() == Ok(name))
454 .expect("the function")
455 .elf_symbol()
456 .st_visibility()
457 };
458 assert_eq!(visibility("g"), elf::STV_DEFAULT);
460 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
461 assert_eq!(visibility("s"), elf::STV_DEFAULT);
464 }
465
466 #[test]
476 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
477 let mut text = calling("puts");
478 for (index, (name, seen)) in
479 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
480 {
481 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
482 func.visibility = seen;
483 text.funcs.push(func);
484 }
485 text.bytes.resize(49, 0x90);
486 let mut data = Data::default();
487 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
488 let mut object = variable(name, Place::Written);
489 object.visibility = seen;
490 data.objects.push(object);
491 }
492 let bytes = write(&text, &data, &[], &target()).expect("an object");
493 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
494 let visibility = |name: &str| {
495 file.symbols()
496 .find(|s| s.name() == Ok(name))
497 .expect("the symbol")
498 .elf_symbol()
499 .st_visibility()
500 };
501 assert_eq!(visibility("h"), elf::STV_HIDDEN);
502 assert_eq!(visibility("p"), elf::STV_PROTECTED);
503 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
504 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
505 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
508 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
509 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
510 }
511
512 #[test]
513 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
514 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
515 let file = object::File::parse(&bytes[..]).expect("a readable object");
516 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
517 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
518 }
519
520 #[test]
521 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
522 for (reference, wanted) in [
523 (Reference::Call, elf::R_X86_64_PLT32),
524 (Reference::Data, elf::R_X86_64_PC32),
525 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
526 ] {
527 let mut text = calling("puts");
528 text.relocs[0].kind = reference;
529 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
530 let file = object::File::parse(&bytes[..]).expect("a readable object");
531 let section = file.section_by_name(".text").expect("a text section");
532 let (offset, reloc) = section.relocations().next().expect("one relocation");
533 assert_eq!(offset, 1);
534 assert_eq!(reloc.addend(), -4);
535 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
536 }
537 }
538
539 #[test]
540 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
541 let mut text = calling("puts");
542 text.relocs.push(Reloc {
543 at: 1,
544 symbol: "puts".to_owned(),
545 kind: Reference::Call,
546 addend: -4,
547 });
548 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
549 let file = object::File::parse(&bytes[..]).expect("a readable object");
550 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
551 }
552
553 #[test]
554 fn a_function_that_is_also_called_is_not_a_second_symbol() {
555 let text = calling("f");
556 let bytes = write(&text, &Data::default(), &[], &target()).expect("an object");
557 let file = object::File::parse(&bytes[..]).expect("a readable object");
558 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
559 let f = found.next().expect("the function");
560 assert!(!f.is_undefined(), "the file defines it");
561 assert!(found.next().is_none(), "and defines it once");
562 }
563
564 #[test]
565 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
566 let bytes = write(&calling("puts"), &Data::default(), &[], &target()).expect("an object");
567 let file = object::File::parse(&bytes[..]).expect("a readable object");
568 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
569 assert!(note.data().expect("no bytes").is_empty());
570 }
571
572 fn variable(name: &str, place: Place) -> Object {
574 Object {
575 name: name.to_owned(),
576 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
577 size: 4,
578 align: 4,
579 place,
580 binding: Binding::Global,
581 visibility: Visibility::Default,
582 relocs: Vec::new(),
583 }
584 }
585
586 fn holding(object: Object) -> Vec<u8> {
588 let data = Data { objects: vec![object] };
589 write(&Text::default(), &data, &[], &target()).expect("an object")
590 }
591
592 #[test]
593 fn what_a_variable_is_decides_which_section_it_goes_in() {
594 for (place, wanted) in [
595 (Place::Written, ".data"),
596 (Place::ReadOnly, ".rodata"),
597 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
598 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
599 (Place::Zero, ".bss"),
600 (Place::Named(".init_array".to_owned()), ".init_array"),
601 ] {
602 let bytes = holding(variable("x", place.clone()));
603 let file = object::File::parse(&bytes[..]).expect("a readable object");
604 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
605 assert_eq!(section.size(), 4, "{place:?}");
606 let carried = section.data().expect("the bytes").len();
609 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
610 }
611 }
612
613 #[test]
621 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
622 let place = Place::RelocReadOnly { local: true };
623 let data =
624 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
625 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
626 let file = object::File::parse(&bytes[..]).expect("a readable object");
627 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
628 assert_eq!(named, 1, "one section holding both, not one each");
629 }
630
631 #[test]
632 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
633 let mut data = Data { objects: vec![variable("first", Place::Written)] };
634 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
635 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
636 let file = object::File::parse(&bytes[..]).expect("a readable object");
637 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
638 assert_eq!(second.kind(), SymbolKind::Data);
639 assert_eq!(second.size(), 4);
640 assert_eq!(second.address(), 16);
644 }
645
646 #[test]
647 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
648 for (binding, global, weak) in [
649 (Binding::Global, true, false),
650 (Binding::Local, false, false),
651 (Binding::Weak, true, true),
652 ] {
653 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
654 let file = object::File::parse(&bytes[..]).expect("a readable object");
655 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
656 assert_eq!(x.is_global(), global, "{binding:?}");
657 assert_eq!(x.is_weak(), weak, "{binding:?}");
658 }
659 }
660
661 #[test]
662 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
663 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
664 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
665 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
666 assert!(x.is_common(), "the linker merges every definition of this name into one");
667 assert_eq!(x.size(), 4);
668 assert_eq!(x.address(), 0);
672 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
673 }
674
675 #[test]
676 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
677 let object = Object {
678 bytes: vec![0; 8],
679 size: 8,
680 align: 8,
681 relocs: vec![Reloc {
682 at: 0,
683 symbol: "y".to_owned(),
684 kind: Reference::Address { bytes: 8 },
685 addend: 16,
686 }],
687 ..variable("p", Place::Written)
688 };
689 let bytes = holding(object);
690 let file = object::File::parse(&bytes[..]).expect("a readable object");
691 let section = file.section_by_name(".data").expect("a data section");
692 let (offset, reloc) = section.relocations().next().expect("one relocation");
693 assert_eq!(offset, 0);
694 assert_eq!(reloc.addend(), 16);
695 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
696 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
697 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
698 }
699
700 #[test]
702 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
703 let mut data = Data { objects: vec![variable("first", Place::Written)] };
704 data.objects.push(Object {
705 bytes: vec![0; 16],
706 size: 16,
707 align: 8,
708 relocs: vec![Reloc {
709 at: 8,
710 symbol: "y".to_owned(),
711 kind: Reference::Address { bytes: 8 },
712 addend: 0,
713 }],
714 ..variable("second", Place::Written)
715 });
716 let bytes = write(&Text::default(), &data, &[], &target()).expect("an object");
717 let file = object::File::parse(&bytes[..]).expect("a readable object");
718 let section = file.section_by_name(".data").expect("a data section");
719 let (offset, _) = section.relocations().next().expect("one relocation");
720 assert_eq!(offset, 16);
723 }
724
725 #[test]
726 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
727 let data = Data {
728 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
729 };
730 let aliases = [Alias {
731 name: "b".to_owned(),
732 target: "a".to_owned(),
733 binding: Binding::Global,
734 visibility: Visibility::Default,
735 }];
736 let bytes = write(&Text::default(), &data, &aliases, &target()).expect("an object");
737 let file = object::File::parse(&bytes[..]).expect("a readable object");
738 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
739 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
740 assert_eq!(b.address(), a.address(), "the same place");
741 assert_eq!(b.size(), a.size());
742 assert_eq!(b.section_index(), a.section_index());
743 assert!(a.is_local(), "the target was written `static`");
746 assert!(b.is_global(), "and the name given to it was not");
747 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
749 }
750
751 #[test]
752 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
753 let text = calling("puts");
754 let aliases = [Alias {
755 name: "g".to_owned(),
756 target: "f".to_owned(),
757 binding: Binding::Weak,
758 visibility: Visibility::Default,
759 }];
760 let bytes = write(&text, &Data::default(), &aliases, &target()).expect("an object");
761 let file = object::File::parse(&bytes[..]).expect("a readable object");
762 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
763 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
764 assert_eq!(g.address(), f.address());
765 assert_eq!(g.size(), f.size());
766 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
767 assert!(g.is_weak(), "so that a program may define the name itself instead");
768 }
769
770 #[test]
773 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
774 let aliases = [Alias {
775 name: "b".to_owned(),
776 target: "a".to_owned(),
777 binding: Binding::Global,
778 visibility: Visibility::Default,
779 }];
780 let error = write(&Text::default(), &Data::default(), &aliases, &target())
781 .expect_err("nothing to point at");
782 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
783 }
784
785 #[test]
786 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
787 let text = calling("puts");
788 for triple in [
789 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
790 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
791 ] {
792 let error = write(&text, &Data::default(), &[], &TargetInfo::new(triple))
793 .expect_err("no writer");
794 assert!(matches!(error, Error::Format { .. }), "{error:?}");
795 }
796 }
797}