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::{
39 Alias, Binding, Data, Object, Output, Place, Property, Reference, Reloc, Sections, Text,
40 Visibility,
41};
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum Error {
46 Format {
48 triple: String,
50 },
51 Refused {
53 why: String,
55 },
56}
57
58impl std::fmt::Display for Error {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 Error::Format { triple } => {
62 write!(f, "there is no object writer for {triple} in this compiler yet")
63 }
64 Error::Refused { why } => {
65 write!(f, "the object writer refused what it was given: {why}")
66 }
67 }
68 }
69}
70
71impl std::error::Error for Error {}
72
73pub fn write(
82 text: &Text,
83 data: &Data,
84 aliases: &[Alias],
85 target: &TargetInfo,
86 output: Output,
87) -> Result<Vec<u8>, Error> {
88 let Output { sections, property } = output;
89 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
90 return Err(Error::Format { triple: target.tuple.to_string() });
91 }
92 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
93 let whole = obj.section_id(StandardSection::Text);
97 if !sections.functions {
98 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
99 }
100
101 let mut symbols = std::collections::BTreeMap::new();
105 let mut split = Vec::with_capacity(text.funcs.len());
109 for func in &text.funcs {
110 let (section, at) = if sections.functions {
115 let name = format!(".text.{}", func.name).into_bytes();
116 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
117 let bytes = &text.bytes[func.start..func.start + func.len];
118 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
119 (id, 0)
120 } else {
121 (whole, func.start as u64)
122 };
123 let id = obj.add_symbol(Symbol {
124 name: func.name.clone().into_bytes(),
125 value: at,
126 size: func.len as u64,
127 kind: SymbolKind::Text,
128 scope: scope_of(func.binding),
129 weak: func.binding == Binding::Weak,
130 section: SymbolSection::Section(section),
131 flags: SymbolFlags::None,
132 });
133 see(&mut obj, id, func.binding, func.visibility);
134 symbols.insert(func.name.clone(), id);
135 split.push(section);
136 }
137
138 let mut placed = Vec::with_capacity(data.objects.len());
143 let mut local = None;
147 for object in &data.objects {
148 let (section, offset) = put(&mut obj, object, &mut local, sections);
149 let id = obj.add_symbol(Symbol {
150 name: object.name.clone().into_bytes(),
151 value: if object.place == Place::Merged { object.align } else { offset },
154 size: object.size,
155 kind: SymbolKind::Data,
156 scope: scope_of(object.binding),
157 weak: object.binding == Binding::Weak,
158 section,
159 flags: SymbolFlags::None,
160 });
161 see(&mut obj, id, object.binding, object.visibility);
162 symbols.insert(object.name.clone(), id);
163 placed.push((section.id(), offset));
164 }
165
166 for alias in aliases {
172 let Some(&id) = symbols.get(&alias.target) else {
173 let why =
174 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
175 return Err(Error::Refused { why });
176 };
177 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
178 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
179 let id = obj.add_symbol(Symbol {
180 name: alias.name.clone().into_bytes(),
181 value,
182 size,
183 kind,
184 scope: scope_of(alias.binding),
185 weak: alias.binding == Binding::Weak,
186 section,
187 flags: SymbolFlags::None,
188 });
189 see(&mut obj, id, alias.binding, alias.visibility);
190 symbols.insert(alias.name.clone(), id);
191 }
192
193 let wanted = text
194 .relocs
195 .iter()
196 .chain(text.unwind.relocs.iter())
197 .chain(data.objects.iter().flat_map(|object| &object.relocs));
198 for reloc in wanted {
199 if symbols.contains_key(&reloc.symbol) {
200 continue;
201 }
202 let id = obj.add_symbol(Symbol {
203 name: reloc.symbol.clone().into_bytes(),
204 value: 0,
205 size: 0,
206 kind: SymbolKind::Unknown,
210 scope: SymbolScope::Dynamic,
211 weak: false,
212 section: SymbolSection::Undefined,
213 flags: SymbolFlags::None,
214 });
215 symbols.insert(reloc.symbol.clone(), id);
216 }
217
218 for reloc in &text.relocs {
219 let (section, at) = if sections.functions {
224 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
225 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
226 let why = format!("a relocation at {} is in front of every function", reloc.at);
227 return Err(Error::Refused { why });
228 };
229 (split[after - 1], (reloc.at - func.start) as u64)
230 } else {
231 (whole, reloc.at as u64)
232 };
233 add(&mut obj, section, at, reloc, &symbols)?;
234 }
235
236 if !text.unwind.bytes.is_empty() {
242 let frames = obj.add_section(Vec::new(), b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
243 obj.append_section_data(frames, &text.unwind.bytes, 8);
244 for reloc in &text.unwind.relocs {
245 add(&mut obj, frames, reloc.at as u64, reloc, &symbols)?;
246 }
247 }
248 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
249 let Some(section) = section else { continue };
250 for reloc in &object.relocs {
251 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols)?;
252 }
253 }
254
255 if property.any() {
259 let note = obj.section_id(StandardSection::GnuProperty);
260 obj.append_section_data(note, &record(property), 8);
261 }
262
263 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
266
267 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
268}
269
270fn record(property: Property) -> Vec<u8> {
281 let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
284 let desc = [Property::X86_FEATURES, 4, property.features, 0];
285 let mut out = Vec::with_capacity(32);
286 for word in head {
287 out.extend_from_slice(&word.to_le_bytes());
288 }
289 out.extend_from_slice(b"GNU\0");
292 for word in desc {
293 out.extend_from_slice(&word.to_le_bytes());
294 }
295 out
296}
297
298fn put(
305 obj: &mut Writer<'_>,
306 object: &Object,
307 local: &mut Option<object::write::SectionId>,
308 sections: Sections,
309) -> (SymbolSection, u64) {
310 if sections.data {
316 if let Some(name) = object.place.split(&object.name) {
317 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
318 let offset = if object.place == Place::Zero {
319 obj.append_section_bss(section, object.size, object.align)
320 } else {
321 obj.append_section_data(section, &object.bytes, object.align)
322 };
323 return (SymbolSection::Section(section), offset);
324 }
325 }
326 let section = match &object.place {
327 Place::Written => obj.section_id(StandardSection::Data),
328 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
329 Place::RelocReadOnly { local: false } => {
335 obj.section_id(StandardSection::ReadOnlyDataWithRel)
336 }
337 Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
338 obj.add_section(
339 Vec::new(),
340 b".data.rel.ro.local".to_vec(),
341 SectionKind::ReadOnlyDataWithRel,
342 )
343 }),
344 Place::Zero => obj.section_id(StandardSection::UninitializedData),
345 Place::Merged => return (SymbolSection::Common, 0),
346 Place::Named(name) => {
350 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
351 }
352 };
353 let offset = if object.place == Place::Zero {
354 obj.append_section_bss(section, object.size, object.align)
355 } else {
356 obj.append_section_data(section, &object.bytes, object.align)
357 };
358 (SymbolSection::Section(section), offset)
359}
360
361fn kind_of(place: &Place) -> SectionKind {
370 match place {
371 Place::ReadOnly => SectionKind::ReadOnlyData,
372 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
373 Place::Zero => SectionKind::UninitializedData,
374 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
375 }
376}
377
378fn add(
385 obj: &mut Writer<'_>,
386 section: object::write::SectionId,
387 at: u64,
388 reloc: &Reloc,
389 symbols: &std::collections::BTreeMap<String, SymbolId>,
390) -> Result<(), Error> {
391 let r_type = r_type(reloc.kind)
392 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
393 obj.add_relocation(
394 section,
395 Relocation {
396 offset: at,
397 symbol: symbols[&reloc.symbol],
398 addend: reloc.addend,
399 flags: RelocationFlags::Elf { r_type },
400 },
401 )
402 .map_err(|why| Error::Refused { why: why.to_string() })
403}
404
405fn scope_of(binding: Binding) -> SymbolScope {
418 match binding {
419 Binding::Local => SymbolScope::Compilation,
420 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
421 }
422}
423
424fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
436 if binding == Binding::Local {
437 return;
438 }
439 let wanted = match visibility {
440 Visibility::Default => elf::STV_DEFAULT,
441 Visibility::Hidden => elf::STV_HIDDEN,
442 Visibility::Protected => elf::STV_PROTECTED,
443 };
444 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
445 *st_other = st_other.with_visibility(wanted);
446 }
447}
448
449fn r_type(reference: Reference) -> Option<elf::RelocationType> {
460 Some(match reference {
461 Reference::Call => elf::R_X86_64_PLT32,
462 Reference::Data => elf::R_X86_64_PC32,
463 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
464 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
465 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
466 Reference::Address { .. } => return None,
467 })
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 use object::read::elf::Sym as _;
475 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
476 use rucc_target::{Arch, Env, Os, Triple};
477
478 use crate::section::{Extent, Reloc};
479
480 fn target() -> TargetInfo {
482 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
483 }
484
485 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
490 Extent {
491 name,
492 start,
493 len,
494 align: crate::FUNC_ALIGN,
495 binding,
496 visibility: Visibility::Default,
497 }
498 }
499
500 fn calling(name: &str) -> Text {
502 Text {
503 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
504 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
505 relocs: vec![Reloc {
506 at: 1,
507 symbol: name.to_owned(),
508 kind: Reference::Call,
509 addend: -4,
510 }],
511 ..Text::default()
512 }
513 }
514
515 #[test]
516 fn the_bytes_come_back_out_of_the_section_they_went_into() {
517 let text = calling("puts");
518 let bytes =
519 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
520 let file = object::File::parse(&bytes[..]).expect("a readable object");
521 let section = file.section_by_name(".text").expect("a text section");
522 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
523 }
524
525 #[test]
526 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
527 let mut text = calling("puts");
528 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
529 text.bytes.resize(17, 0x90);
530 let bytes =
531 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
532 let file = object::File::parse(&bytes[..]).expect("a readable object");
533 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
534 assert_eq!(g.address(), 16);
535 assert_eq!(g.size(), 1);
536 assert_eq!(g.kind(), SymbolKind::Text);
537 assert!(g.is_global(), "nothing said otherwise about this one");
538 }
539
540 #[test]
541 fn a_function_no_other_file_can_see_is_a_local_symbol() {
542 let mut text = calling("puts");
543 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
544 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
545 text.bytes.resize(33, 0x90);
546 let bytes =
547 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
548 let file = object::File::parse(&bytes[..]).expect("a readable object");
549 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
550 assert!(hidden.is_local(), "a static function must not be offered to the linker");
553 assert!(!hidden.is_weak());
554 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
555 assert!(shared.is_weak(), "a weak function has to be able to lose");
556 assert!(shared.is_global());
557 }
558
559 #[test]
570 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
571 let mut text = calling("puts");
572 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
573 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
574 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
575 text.bytes.resize(49, 0x90);
576 let bytes =
577 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
578 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
579 let visibility = |name: &str| {
580 file.symbols()
581 .find(|s| s.name() == Ok(name))
582 .expect("the function")
583 .elf_symbol()
584 .st_visibility()
585 };
586 assert_eq!(visibility("g"), elf::STV_DEFAULT);
588 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
589 assert_eq!(visibility("s"), elf::STV_DEFAULT);
592 }
593
594 #[test]
604 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
605 let mut text = calling("puts");
606 for (index, (name, seen)) in
607 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
608 {
609 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
610 func.visibility = seen;
611 text.funcs.push(func);
612 }
613 text.bytes.resize(49, 0x90);
614 let mut data = Data::default();
615 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
616 let mut object = variable(name, Place::Written);
617 object.visibility = seen;
618 data.objects.push(object);
619 }
620 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
621 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
622 let visibility = |name: &str| {
623 file.symbols()
624 .find(|s| s.name() == Ok(name))
625 .expect("the symbol")
626 .elf_symbol()
627 .st_visibility()
628 };
629 assert_eq!(visibility("h"), elf::STV_HIDDEN);
630 assert_eq!(visibility("p"), elf::STV_PROTECTED);
631 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
632 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
633 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
636 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
637 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
638 }
639
640 #[test]
641 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
642 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
643 .expect("an object");
644 let file = object::File::parse(&bytes[..]).expect("a readable object");
645 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
646 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
647 }
648
649 #[test]
650 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
651 for (reference, wanted) in [
652 (Reference::Call, elf::R_X86_64_PLT32),
653 (Reference::Data, elf::R_X86_64_PC32),
654 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
655 ] {
656 let mut text = calling("puts");
657 text.relocs[0].kind = reference;
658 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
659 .expect("an object");
660 let file = object::File::parse(&bytes[..]).expect("a readable object");
661 let section = file.section_by_name(".text").expect("a text section");
662 let (offset, reloc) = section.relocations().next().expect("one relocation");
663 assert_eq!(offset, 1);
664 assert_eq!(reloc.addend(), -4);
665 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
666 }
667 }
668
669 #[test]
670 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
671 let mut text = calling("puts");
672 text.relocs.push(Reloc {
673 at: 1,
674 symbol: "puts".to_owned(),
675 kind: Reference::Call,
676 addend: -4,
677 });
678 let bytes =
679 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
680 let file = object::File::parse(&bytes[..]).expect("a readable object");
681 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
682 }
683
684 #[test]
685 fn a_function_that_is_also_called_is_not_a_second_symbol() {
686 let text = calling("f");
687 let bytes =
688 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
689 let file = object::File::parse(&bytes[..]).expect("a readable object");
690 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
691 let f = found.next().expect("the function");
692 assert!(!f.is_undefined(), "the file defines it");
693 assert!(found.next().is_none(), "and defines it once");
694 }
695
696 #[test]
697 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
698 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
699 .expect("an object");
700 let file = object::File::parse(&bytes[..]).expect("a readable object");
701 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
702 assert!(note.data().expect("no bytes").is_empty());
703 }
704
705 #[test]
712 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
713 let property = Property { features: Property::IBT | Property::SHSTK };
714 let output = Output { property, ..Output::default() };
715 let bytes =
716 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
717 let file = object::File::parse(&bytes[..]).expect("a readable object");
718 let note = file.section_by_name(".note.gnu.property").expect("the note");
719 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
720 let want: Vec<u8> = [
721 4u32,
722 16,
723 5,
724 u32::from_le_bytes(*b"GNU\0"),
725 Property::X86_FEATURES,
726 4,
727 Property::IBT | Property::SHSTK,
728 0,
729 ]
730 .iter()
731 .flat_map(|word| word.to_le_bytes())
732 .collect();
733 assert_eq!(note.data().expect("the bytes"), &want[..]);
734 }
735
736 #[test]
742 fn a_file_built_to_have_nothing_checked_says_nothing() {
743 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
744 .expect("an object");
745 let file = object::File::parse(&bytes[..]).expect("a readable object");
746 assert!(file.section_by_name(".note.gnu.property").is_none());
747 }
748
749 #[test]
758 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
759 let mut text = calling("puts");
760 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
761 text.bytes.resize(17, 0x90);
762 text.unwind.bytes = vec![0; 64];
765 for (at, name) in [(32usize, "f"), (48usize, "g")] {
766 text.unwind.relocs.push(Reloc {
767 at,
768 symbol: name.to_owned(),
769 kind: Reference::Address { bytes: 8 },
770 addend: 0,
771 });
772 }
773 let bytes =
774 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
775 let file = object::File::parse(&bytes[..]).expect("a readable object");
776 let frames = file.section_by_name(".eh_frame").expect("the table");
777 let mut at = frames.relocations().map(|(offset, _)| offset).collect::<Vec<_>>();
778 at.sort_unstable();
779 assert_eq!(at, [32, 48]);
780 }
781
782 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
784 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
785 let index = symbol.section_index().expect("a section to be defined in");
786 let section = file.section_by_index(index).expect("a readable section");
787 section.name().expect("a named section").to_owned()
788 }
789
790 fn two() -> Text {
792 let mut text = calling("puts");
793 text.bytes.resize(16, 0x90);
796 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
797 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
798 text.relocs.push(Reloc {
799 at: 17,
800 symbol: "puts".to_owned(),
801 kind: Reference::Call,
802 addend: -4,
803 });
804 text
805 }
806
807 #[test]
814 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
815 let sections =
816 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
817 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
818 let file = object::File::parse(&bytes[..]).expect("a readable object");
819 assert_eq!(lives_in(&file, "f"), ".text.f");
820 assert_eq!(lives_in(&file, "g"), ".text.g");
821 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
822 for name in ["f", "g"] {
825 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
826 assert_eq!(symbol.address(), 0, "{name}");
827 assert_eq!(symbol.size(), 6, "{name}");
828 }
829 let section = file.section_by_name(".text.g").expect("the second function");
830 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
831 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
834 }
835
836 #[test]
842 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
843 let sections =
844 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
845 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
846 let file = object::File::parse(&bytes[..]).expect("a readable object");
847 for name in [".text.f", ".text.g"] {
848 let section = file.section_by_name(name).expect("a function");
849 let (offset, _) = section.relocations().next().expect("the call in it");
850 assert_eq!(offset, 1, "{name}");
853 assert_eq!(section.relocations().count(), 1, "{name}");
854 }
855 }
856
857 fn variable(name: &str, place: Place) -> Object {
859 Object {
860 name: name.to_owned(),
861 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
862 size: 4,
863 align: 4,
864 place,
865 binding: Binding::Global,
866 visibility: Visibility::Default,
867 relocs: Vec::new(),
868 }
869 }
870
871 fn holding(object: Object) -> Vec<u8> {
873 let data = Data { objects: vec![object] };
874 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
875 }
876
877 #[test]
878 fn what_a_variable_is_decides_which_section_it_goes_in() {
879 for (place, wanted) in [
880 (Place::Written, ".data"),
881 (Place::ReadOnly, ".rodata"),
882 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
883 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
884 (Place::Zero, ".bss"),
885 (Place::Named(".init_array".to_owned()), ".init_array"),
886 ] {
887 let bytes = holding(variable("x", place.clone()));
888 let file = object::File::parse(&bytes[..]).expect("a readable object");
889 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
890 assert_eq!(section.size(), 4, "{place:?}");
891 let carried = section.data().expect("the bytes").len();
894 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
895 }
896 }
897
898 #[test]
902 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
903 let sections =
904 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
905 for (place, wanted) in [
906 (Place::Written, ".data.x"),
907 (Place::ReadOnly, ".rodata.x"),
908 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
909 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
910 (Place::Zero, ".bss.x"),
911 ] {
912 let data = Data { objects: vec![variable("x", place.clone())] };
913 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
914 let file = object::File::parse(&bytes[..]).expect("a readable object");
915 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
916 let section = file.section_by_name(wanted).expect("the section it named");
917 assert_eq!(section.size(), 4, "{place:?}");
918 let carried = section.data().expect("the bytes").len();
921 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
922 }
923 }
924
925 #[test]
929 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
930 let sections =
931 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
932 let named = Place::Named(".init_array".to_owned());
933 let objects = vec![variable("m", Place::Merged), variable("n", named)];
934 let bytes =
935 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
936 let file = object::File::parse(&bytes[..]).expect("a readable object");
937 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
938 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
939 assert_eq!(lives_in(&file, "n"), ".init_array");
940 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
941 }
942
943 #[test]
947 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
948 let sections =
949 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
950 let pointer = Object {
951 bytes: vec![0; 8],
952 size: 8,
953 align: 8,
954 relocs: vec![Reloc {
955 at: 0,
956 symbol: "y".to_owned(),
957 kind: Reference::Address { bytes: 8 },
958 addend: 0,
959 }],
960 ..variable("p", Place::Written)
961 };
962 let objects = vec![variable("first", Place::Written), pointer];
963 let bytes =
964 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
965 let file = object::File::parse(&bytes[..]).expect("a readable object");
966 let section = file.section_by_name(".data.p").expect("the pointer's own section");
967 let (offset, reloc) = section.relocations().next().expect("one relocation");
968 assert_eq!(offset, 0);
971 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
972 }
973
974 #[test]
982 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
983 let place = Place::RelocReadOnly { local: true };
984 let data =
985 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
986 let bytes =
987 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
988 let file = object::File::parse(&bytes[..]).expect("a readable object");
989 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
990 assert_eq!(named, 1, "one section holding both, not one each");
991 }
992
993 #[test]
994 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
995 let mut data = Data { objects: vec![variable("first", Place::Written)] };
996 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
997 let bytes =
998 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
999 let file = object::File::parse(&bytes[..]).expect("a readable object");
1000 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1001 assert_eq!(second.kind(), SymbolKind::Data);
1002 assert_eq!(second.size(), 4);
1003 assert_eq!(second.address(), 16);
1007 }
1008
1009 #[test]
1010 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1011 for (binding, global, weak) in [
1012 (Binding::Global, true, false),
1013 (Binding::Local, false, false),
1014 (Binding::Weak, true, true),
1015 ] {
1016 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1017 let file = object::File::parse(&bytes[..]).expect("a readable object");
1018 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1019 assert_eq!(x.is_global(), global, "{binding:?}");
1020 assert_eq!(x.is_weak(), weak, "{binding:?}");
1021 }
1022 }
1023
1024 #[test]
1025 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1026 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1027 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1028 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1029 assert!(x.is_common(), "the linker merges every definition of this name into one");
1030 assert_eq!(x.size(), 4);
1031 assert_eq!(x.address(), 0);
1035 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1036 }
1037
1038 #[test]
1039 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1040 let object = Object {
1041 bytes: vec![0; 8],
1042 size: 8,
1043 align: 8,
1044 relocs: vec![Reloc {
1045 at: 0,
1046 symbol: "y".to_owned(),
1047 kind: Reference::Address { bytes: 8 },
1048 addend: 16,
1049 }],
1050 ..variable("p", Place::Written)
1051 };
1052 let bytes = holding(object);
1053 let file = object::File::parse(&bytes[..]).expect("a readable object");
1054 let section = file.section_by_name(".data").expect("a data section");
1055 let (offset, reloc) = section.relocations().next().expect("one relocation");
1056 assert_eq!(offset, 0);
1057 assert_eq!(reloc.addend(), 16);
1058 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1059 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1060 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1061 }
1062
1063 #[test]
1065 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1066 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1067 data.objects.push(Object {
1068 bytes: vec![0; 16],
1069 size: 16,
1070 align: 8,
1071 relocs: vec![Reloc {
1072 at: 8,
1073 symbol: "y".to_owned(),
1074 kind: Reference::Address { bytes: 8 },
1075 addend: 0,
1076 }],
1077 ..variable("second", Place::Written)
1078 });
1079 let bytes =
1080 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1081 let file = object::File::parse(&bytes[..]).expect("a readable object");
1082 let section = file.section_by_name(".data").expect("a data section");
1083 let (offset, _) = section.relocations().next().expect("one relocation");
1084 assert_eq!(offset, 16);
1087 }
1088
1089 #[test]
1090 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1091 let data = Data {
1092 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1093 };
1094 let aliases = [Alias {
1095 name: "b".to_owned(),
1096 target: "a".to_owned(),
1097 binding: Binding::Global,
1098 visibility: Visibility::Default,
1099 }];
1100 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1101 .expect("an object");
1102 let file = object::File::parse(&bytes[..]).expect("a readable object");
1103 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1104 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1105 assert_eq!(b.address(), a.address(), "the same place");
1106 assert_eq!(b.size(), a.size());
1107 assert_eq!(b.section_index(), a.section_index());
1108 assert!(a.is_local(), "the target was written `static`");
1111 assert!(b.is_global(), "and the name given to it was not");
1112 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1114 }
1115
1116 #[test]
1117 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1118 let text = calling("puts");
1119 let aliases = [Alias {
1120 name: "g".to_owned(),
1121 target: "f".to_owned(),
1122 binding: Binding::Weak,
1123 visibility: Visibility::Default,
1124 }];
1125 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1126 .expect("an object");
1127 let file = object::File::parse(&bytes[..]).expect("a readable object");
1128 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1129 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1130 assert_eq!(g.address(), f.address());
1131 assert_eq!(g.size(), f.size());
1132 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1133 assert!(g.is_weak(), "so that a program may define the name itself instead");
1134 }
1135
1136 #[test]
1139 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1140 let aliases = [Alias {
1141 name: "b".to_owned(),
1142 target: "a".to_owned(),
1143 binding: Binding::Global,
1144 visibility: Visibility::Default,
1145 }];
1146 let error =
1147 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1148 .expect_err("nothing to point at");
1149 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1150 }
1151
1152 #[test]
1153 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1154 let text = calling("puts");
1155 for triple in [
1156 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1157 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1158 ] {
1159 let error =
1160 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1161 .expect_err("no writer");
1162 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1163 }
1164 }
1165}