1use std::collections::HashMap;
34
35use object::write::{
36 Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
37};
38use object::{
39 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionFlags, SectionKind,
40 SymbolFlags, SymbolKind, SymbolScope,
41};
42use rucc_target::{ObjectFormat, TargetInfo};
43use rucc_tuple::Arch;
44
45use crate::section::{
46 Alias, Array, Binding, Data, Object, Output, Place, Property, Reference, Reloc, Sections, Text,
47 Visibility,
48};
49use crate::{coff, elf};
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub(crate) enum Flavour {
59 Elf,
61 Coff,
63}
64
65impl Flavour {
66 fn of(target: &TargetInfo) -> Option<Flavour> {
68 match target.object_format {
69 ObjectFormat::Elf => Some(Flavour::Elf),
70 ObjectFormat::Coff => Some(Flavour::Coff),
71 ObjectFormat::MachO | ObjectFormat::Wasm => None,
72 }
73 }
74
75 fn binary(self) -> BinaryFormat {
77 match self {
78 Flavour::Elf => BinaryFormat::Elf,
79 Flavour::Coff => BinaryFormat::Coff,
80 }
81 }
82
83 fn reloc(self, reference: Reference, after: u8) -> Option<RelocationFlags> {
88 match self {
89 Flavour::Elf => elf::r_type(reference).map(|r_type| RelocationFlags::Elf { r_type }),
90 Flavour::Coff => coff::reloc(reference, after),
91 }
92 }
93
94 fn see(self, obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
100 match self {
101 Flavour::Elf => elf::see(obj, id, binding, visibility),
102 Flavour::Coff => {}
103 }
104 }
105
106 fn rel_ro_local(self) -> Option<&'static str> {
110 match self {
111 Flavour::Elf => elf::REL_RO_LOCAL,
112 Flavour::Coff => coff::REL_RO_LOCAL,
113 }
114 }
115
116 fn gathered(self, array: Array) -> Option<SectionFlags> {
122 match self {
123 Flavour::Elf => Some(elf::gathered(array)),
124 Flavour::Coff => None,
125 }
126 }
127
128 fn marker(self, obj: &mut Writer<'_>) {
130 match self {
131 Flavour::Elf => elf::marker(obj),
132 Flavour::Coff => coff::marker(obj),
133 }
134 }
135
136 fn property(self, obj: &mut Writer<'_>, property: Property) {
142 if !property.any() {
143 return;
144 }
145 match self {
146 Flavour::Elf => {
147 let note = obj.section_id(StandardSection::GnuProperty);
148 obj.append_section_data(note, &elf::record(property), 8);
149 }
150 Flavour::Coff => {}
151 }
152 }
153
154 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
158 match self {
159 Flavour::Elf => (elf::FRAMES, None),
160 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
161 }
162 }
163
164 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
166 match self {
167 Flavour::Elf => elf::link(bytes, ordered),
168 Flavour::Coff => debug_assert!(ordered.is_empty(), "a record this format cannot write"),
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
175pub enum Error {
176 Format {
178 triple: String,
180 },
181 Refused {
183 why: String,
185 },
186}
187
188impl std::fmt::Display for Error {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 match self {
191 Error::Format { triple } => {
192 write!(f, "there is no object writer for {triple} in this compiler yet")
193 }
194 Error::Refused { why } => {
195 write!(f, "the object writer refused what it was given: {why}")
196 }
197 }
198 }
199}
200
201impl std::error::Error for Error {}
202
203pub fn write(
215 text: &Text,
216 data: &Data,
217 aliases: &[Alias],
218 target: &TargetInfo,
219 output: Output,
220) -> Result<Vec<u8>, Error> {
221 let Output { sections, property } = output;
222 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
223 let Some(flavour) = flavour else {
224 return Err(Error::Format { triple: target.tuple.to_string() });
225 };
226 if flavour == Flavour::Coff {
227 beyond(text, data)?;
228 }
229 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
230 let whole = obj.section_id(StandardSection::Text);
234 if !sections.functions {
235 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
236 }
237
238 let mut symbols = std::collections::BTreeMap::new();
242 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
247 let mut ordered: Vec<String> = Vec::new();
250 for func in &text.funcs {
251 let ahead = func.patch.map_or(0, |patch| patch.before);
261 let (section, at) = if sections.functions {
262 let name = format!(".text.{}", func.name).into_bytes();
263 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
264 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
265 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
266 (id, ahead as u64)
267 } else {
268 (whole, func.start as u64)
269 };
270 if let Some(patch) = func.patch {
285 let base = if sections.functions { func.start - ahead } else { 0 };
286 let name = elf::PATCHABLE.as_bytes().to_vec();
287 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
288 obj.section_mut(id).flags = elf::ordered();
289 obj.append_section_data(id, &[0; 8], 8);
290 let symbol = obj.section_symbol(section);
291 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
292 Error::Refused { why: "no relocation holds an address here".to_owned() }
293 })?;
294 obj.add_relocation(
295 id,
296 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
297 )
298 .map_err(|why| Error::Refused { why: why.to_string() })?;
299 ordered.push(if sections.functions {
300 format!(".text.{}", func.name)
301 } else {
302 ".text".to_owned()
303 });
304 }
305 let id = obj.add_symbol(Symbol {
306 name: func.name.clone().into_bytes(),
307 value: at,
308 size: func.len as u64,
309 kind: SymbolKind::Text,
310 scope: scope_of(func.binding),
311 weak: func.binding == Binding::Weak,
312 section: SymbolSection::Section(section),
313 flags: SymbolFlags::None,
314 });
315 flavour.see(&mut obj, id, func.binding, func.visibility);
316 symbols.insert(func.name.clone(), id);
317 split.push((section, at));
318 }
319
320 for label in &text.labels {
324 let after = text.funcs.partition_point(|func| func.start <= label.at);
325 let Some(index) = after.checked_sub(1) else {
326 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
327 return Err(Error::Refused { why });
328 };
329 let func = &text.funcs[index];
330 let (section, at) = if sections.functions {
331 let base = func.start - func.patch.map_or(0, |patch| patch.before);
334 (split[index].0, (label.at - base) as u64)
335 } else {
336 (whole, label.at as u64)
337 };
338 let id = obj.add_symbol(Symbol {
339 name: label.name.clone().into_bytes(),
340 value: at,
341 size: 0,
344 kind: SymbolKind::Label,
345 scope: SymbolScope::Compilation,
349 weak: false,
350 section: SymbolSection::Section(section),
351 flags: SymbolFlags::None,
352 });
353 symbols.insert(label.name.clone(), id);
354 }
355
356 let mut placed = Vec::with_capacity(data.objects.len());
361 let mut named = HashMap::new();
365 for object in &data.objects {
366 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
367 let id = obj.add_symbol(Symbol {
368 name: object.name.clone().into_bytes(),
369 value: if object.place == Place::Merged { object.align } else { offset },
372 size: object.size,
373 kind: match object.place {
378 Place::Thread { .. } => SymbolKind::Tls,
379 _ => SymbolKind::Data,
380 },
381 scope: scope_of(object.binding),
382 weak: object.binding == Binding::Weak,
383 section,
384 flags: SymbolFlags::None,
385 });
386 flavour.see(&mut obj, id, object.binding, object.visibility);
387 symbols.insert(object.name.clone(), id);
388 placed.push((section.id(), offset));
389 }
390
391 for alias in aliases {
397 let Some(&id) = symbols.get(&alias.target) else {
398 let why =
399 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
400 return Err(Error::Refused { why });
401 };
402 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
403 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
404 let id = obj.add_symbol(Symbol {
405 name: alias.name.clone().into_bytes(),
406 value,
407 size,
408 kind,
409 scope: scope_of(alias.binding),
410 weak: alias.binding == Binding::Weak,
411 section,
412 flags: SymbolFlags::None,
413 });
414 flavour.see(&mut obj, id, alias.binding, alias.visibility);
415 symbols.insert(alias.name.clone(), id);
416 }
417
418 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
422 for reloc in wanted {
423 if symbols.contains_key(&reloc.symbol) {
424 continue;
425 }
426 let id = obj.add_symbol(Symbol {
427 name: reloc.symbol.clone().into_bytes(),
428 value: 0,
429 size: 0,
430 kind: SymbolKind::Unknown,
434 scope: SymbolScope::Dynamic,
435 weak: false,
436 section: SymbolSection::Undefined,
437 flags: SymbolFlags::None,
438 });
439 symbols.insert(reloc.symbol.clone(), id);
440 }
441
442 for reloc in &text.relocs {
443 let (section, at) = if sections.functions {
448 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
449 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
450 let why = format!("a relocation at {} is in front of every function", reloc.at);
451 return Err(Error::Refused { why });
452 };
453 let base = func.start - func.patch.map_or(0, |patch| patch.before);
456 (split[after - 1].0, (reloc.at - base) as u64)
457 } else {
458 (whole, reloc.at as u64)
459 };
460 add(&mut obj, section, at, reloc, &symbols, flavour)?;
461 }
462
463 if !text.unwind.bytes.is_empty() {
467 let ((name, align), second) = flavour.tables();
468 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
469 obj.append_section_data(frames, &text.unwind.bytes, align);
470 let mut described = HashMap::new();
475 if !text.unwind.info.is_empty() {
476 let Some((name, align)) = second else {
477 let why = "an unwind table here is one section and it was given two".to_owned();
478 return Err(Error::Refused { why });
479 };
480 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
481 obj.append_section_data(codes, &text.unwind.info, align);
482 for label in &text.unwind.labels {
483 let id = obj.add_symbol(Symbol {
484 name: label.name.clone().into_bytes(),
485 value: label.at as u64,
486 size: 0,
487 kind: SymbolKind::Label,
488 scope: SymbolScope::Compilation,
489 weak: false,
490 section: SymbolSection::Section(codes),
491 flags: SymbolFlags::None,
492 });
493 described.insert(label.name.clone(), id);
494 }
495 }
496 for reloc in &text.unwind.relocs {
497 let (symbol, addend) = match described.get(&reloc.symbol) {
498 Some(&id) => (id, reloc.addend),
502 None => {
516 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
517 let Some((section, at)) = found.map(|i| split[i]) else {
518 let why = format!(
519 "'{}' has an unwind record and is not a function here",
520 reloc.symbol
521 );
522 return Err(Error::Refused { why });
523 };
524 (obj.section_symbol(section), reloc.addend + at as i64)
528 }
529 };
530 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
531 why: format!("no relocation is {:?}", reloc.kind),
532 })?;
533 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
534 obj.add_relocation(frames, record)
535 .map_err(|why| Error::Refused { why: why.to_string() })?;
536 }
537 }
538 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
539 let Some(section) = section else { continue };
540 for reloc in &object.relocs {
541 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
542 }
543 }
544
545 flavour.property(&mut obj, property);
549
550 flavour.marker(&mut obj);
553
554 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
555 flavour.finish(&mut bytes, &ordered);
556 Ok(bytes)
557}
558
559fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
572 let why = |why: String| Err(Error::Refused { why });
573 if text.funcs.iter().any(|func| func.patch.is_some()) {
574 return why("a record of where a patcher's room is has no section flags here".to_owned());
575 }
576 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
577 if matches!(reloc.kind, Reference::Got | Reference::Thread) {
578 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
579 }
580 }
581 for object in &data.objects {
582 if matches!(object.place, Place::Thread { .. }) {
583 return why(format!("'{}' is thread-local and this format is not", object.name));
584 }
585 let Place::Named(name) = &object.place else { continue };
586 if Array::of(name).is_some() {
587 return why(format!("'{name}' is not a list the startup code here gathers"));
588 }
589 }
590 Ok(())
591}
592
593pub fn defines(
617 text: &Text,
618 data: &Data,
619 aliases: &[Alias],
620 target: &TargetInfo,
621) -> Result<Vec<String>, Error> {
622 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
623 return Err(Error::Format { triple: target.tuple.to_string() });
624 }
625 let names = text
626 .funcs
627 .iter()
628 .filter(|func| func.binding != Binding::Local)
629 .map(|func| func.name.clone())
630 .chain(
631 data.objects
632 .iter()
633 .filter(|object| object.binding != Binding::Local)
634 .map(|object| object.name.clone()),
635 )
636 .chain(
637 aliases
638 .iter()
639 .filter(|alias| alias.binding != Binding::Local)
640 .map(|alias| alias.name.clone()),
641 )
642 .collect();
643 Ok(names)
644}
645
646fn put(
653 obj: &mut Writer<'_>,
654 object: &Object,
655 named: &mut HashMap<String, object::write::SectionId>,
656 sections: Sections,
657 flavour: Flavour,
658) -> (SymbolSection, u64) {
659 if sections.data {
665 if let Some(name) = object.place.split(&object.name) {
666 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
667 let offset = if carries_no_bytes(&object.place) {
668 obj.append_section_bss(section, object.size, object.align)
669 } else {
670 obj.append_section_data(section, &object.bytes, object.align)
671 };
672 return (SymbolSection::Section(section), offset);
673 }
674 }
675 let section = match &object.place {
676 Place::Written => obj.section_id(StandardSection::Data),
677 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
678 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
684 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
685 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
686 },
687 Place::Zero => obj.section_id(StandardSection::UninitializedData),
688 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
689 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
690 Place::Merged => return (SymbolSection::Common, 0),
691 Place::Named(name) => {
697 let section = made(obj, named, name, SectionKind::Data);
698 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
699 obj.section_mut(section).flags = flags;
700 }
701 section
702 }
703 };
704 let offset = if carries_no_bytes(&object.place) {
705 obj.append_section_bss(section, object.size, object.align)
706 } else {
707 obj.append_section_data(section, &object.bytes, object.align)
708 };
709 (SymbolSection::Section(section), offset)
710}
711
712fn carries_no_bytes(place: &Place) -> bool {
718 matches!(place, Place::Zero | Place::Thread { zero: true })
719}
720
721fn made(
729 obj: &mut Writer<'_>,
730 named: &mut HashMap<String, object::write::SectionId>,
731 name: &str,
732 kind: SectionKind,
733) -> object::write::SectionId {
734 if let Some(section) = named.get(name) {
735 return *section;
736 }
737 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
738 named.insert(name.to_owned(), section);
739 section
740}
741
742fn kind_of(place: &Place) -> SectionKind {
751 match place {
752 Place::ReadOnly => SectionKind::ReadOnlyData,
753 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
754 Place::Zero => SectionKind::UninitializedData,
755 Place::Thread { zero: false } => SectionKind::Tls,
756 Place::Thread { zero: true } => SectionKind::UninitializedTls,
757 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
758 }
759}
760
761fn add(
768 obj: &mut Writer<'_>,
769 section: object::write::SectionId,
770 at: u64,
771 reloc: &Reloc,
772 symbols: &std::collections::BTreeMap<String, SymbolId>,
773 flavour: Flavour,
774) -> Result<(), Error> {
775 let flags = flavour
776 .reloc(reloc.kind, reloc.after)
777 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
778 obj.add_relocation(
779 section,
780 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
781 )
782 .map_err(|why| Error::Refused { why: why.to_string() })
783}
784
785pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
798 match binding {
799 Binding::Local => SymbolScope::Compilation,
800 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807
808 use object::read::elf::Sym as _;
809 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
810 use object::{elf, pe};
811 use rucc_target::{Arch, Env, Os, Triple};
812
813 use crate::elf::PATCHABLE;
814 use crate::section::{Extent, Patch, Reloc};
815
816 fn target() -> TargetInfo {
818 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
819 }
820
821 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
826 Extent {
827 name,
828 start,
829 len,
830 align: crate::FUNC_ALIGN,
831 binding,
832 visibility: Visibility::Default,
833 patch: None,
834 }
835 }
836
837 fn calling(name: &str) -> Text {
839 Text {
840 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
841 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
842 relocs: vec![Reloc {
843 at: 1,
844 symbol: name.to_owned(),
845 kind: Reference::Call,
846 addend: -4,
847 after: 0,
848 }],
849 ..Text::default()
850 }
851 }
852
853 #[test]
854 fn the_bytes_come_back_out_of_the_section_they_went_into() {
855 let text = calling("puts");
856 let bytes =
857 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
858 let file = object::File::parse(&bytes[..]).expect("a readable object");
859 let section = file.section_by_name(".text").expect("a text section");
860 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
861 }
862
863 #[test]
864 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
865 let mut text = calling("puts");
866 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
867 text.bytes.resize(17, 0x90);
868 let bytes =
869 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
870 let file = object::File::parse(&bytes[..]).expect("a readable object");
871 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
872 assert_eq!(g.address(), 16);
873 assert_eq!(g.size(), 1);
874 assert_eq!(g.kind(), SymbolKind::Text);
875 assert!(g.is_global(), "nothing said otherwise about this one");
876 }
877
878 #[test]
879 fn a_function_no_other_file_can_see_is_a_local_symbol() {
880 let mut text = calling("puts");
881 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
882 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
883 text.bytes.resize(33, 0x90);
884 let bytes =
885 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
886 let file = object::File::parse(&bytes[..]).expect("a readable object");
887 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
888 assert!(hidden.is_local(), "a static function must not be offered to the linker");
891 assert!(!hidden.is_weak());
892 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
893 assert!(shared.is_weak(), "a weak function has to be able to lose");
894 assert!(shared.is_global());
895 }
896
897 #[test]
914 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
915 let mut text = calling("puts");
916 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
917 text.funcs[0].start = 3;
918 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
919 text.relocs[0].at = 4;
920 let bytes =
921 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
922 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
923 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
924 assert_eq!(section.size(), 8, "one address, and this file defines one function");
925 assert_eq!(section.align(), 8);
926 let header = section.elf_section_header();
927 assert_eq!(
928 header.sh_flags.get(Endianness::Little),
929 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
930 );
931 let index = file.section_by_name(".text").expect("a text section").index().0;
934 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
935 assert_ne!(index, 0);
936
937 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
939 panic!("one address in the record")
940 };
941 assert_eq!(*at, 0);
942 assert_eq!(reloc.addend(), 0);
943 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
944 }
945
946 #[test]
948 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
949 let text = calling("puts");
950 let bytes =
951 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
952 let file = object::File::parse(&bytes[..]).expect("a readable object");
953 assert!(file.section_by_name(PATCHABLE).is_none());
954 }
955
956 #[test]
962 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
963 let mut text = calling("puts");
964 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
965 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
966 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
967 text.bytes.resize(17, 0x90);
968 let output =
969 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
970 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
971 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
972 let links: Vec<usize> = file
973 .sections()
974 .filter(|section| section.name() == Ok(PATCHABLE))
975 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
976 .collect();
977 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
978 assert_eq!(links, [index(".text.f"), index(".text.g")]);
979 }
980
981 #[test]
982 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
983 let mut text = calling("puts");
984 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
985 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
986 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
987 text.bytes.resize(49, 0x90);
988 let bytes =
989 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
990 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
991 let visibility = |name: &str| {
992 file.symbols()
993 .find(|s| s.name() == Ok(name))
994 .expect("the function")
995 .elf_symbol()
996 .st_visibility()
997 };
998 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1000 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1001 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1004 }
1005
1006 #[test]
1016 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1017 let mut text = calling("puts");
1018 for (index, (name, seen)) in
1019 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1020 {
1021 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1022 func.visibility = seen;
1023 text.funcs.push(func);
1024 }
1025 text.bytes.resize(49, 0x90);
1026 let mut data = Data::default();
1027 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1028 let mut object = variable(name, Place::Written);
1029 object.visibility = seen;
1030 data.objects.push(object);
1031 }
1032 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
1033 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1034 let visibility = |name: &str| {
1035 file.symbols()
1036 .find(|s| s.name() == Ok(name))
1037 .expect("the symbol")
1038 .elf_symbol()
1039 .st_visibility()
1040 };
1041 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1042 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1043 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1044 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1045 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1048 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1049 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1050 }
1051
1052 #[test]
1053 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1054 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1055 .expect("an object");
1056 let file = object::File::parse(&bytes[..]).expect("a readable object");
1057 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1058 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1059 }
1060
1061 #[test]
1062 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1063 for (reference, wanted) in [
1064 (Reference::Call, elf::R_X86_64_PLT32),
1065 (Reference::Data, elf::R_X86_64_PC32),
1066 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1067 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1068 ] {
1069 let mut text = calling("puts");
1070 text.relocs[0].kind = reference;
1071 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
1072 .expect("an object");
1073 let file = object::File::parse(&bytes[..]).expect("a readable object");
1074 let section = file.section_by_name(".text").expect("a text section");
1075 let (offset, reloc) = section.relocations().next().expect("one relocation");
1076 assert_eq!(offset, 1);
1077 assert_eq!(reloc.addend(), -4);
1078 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1079 }
1080 }
1081
1082 #[test]
1083 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1084 let mut text = calling("puts");
1085 text.relocs.push(Reloc {
1086 at: 1,
1087 symbol: "puts".to_owned(),
1088 kind: Reference::Call,
1089 addend: -4,
1090 after: 0,
1091 });
1092 let bytes =
1093 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1094 let file = object::File::parse(&bytes[..]).expect("a readable object");
1095 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1096 }
1097
1098 #[test]
1099 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1100 let text = calling("f");
1101 let bytes =
1102 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1103 let file = object::File::parse(&bytes[..]).expect("a readable object");
1104 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1105 let f = found.next().expect("the function");
1106 assert!(!f.is_undefined(), "the file defines it");
1107 assert!(found.next().is_none(), "and defines it once");
1108 }
1109
1110 #[test]
1111 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1112 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1113 .expect("an object");
1114 let file = object::File::parse(&bytes[..]).expect("a readable object");
1115 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1116 assert!(note.data().expect("no bytes").is_empty());
1117 }
1118
1119 #[test]
1126 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1127 let property = Property { features: Property::IBT | Property::SHSTK };
1128 let output = Output { property, ..Output::default() };
1129 let bytes =
1130 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
1131 let file = object::File::parse(&bytes[..]).expect("a readable object");
1132 let note = file.section_by_name(".note.gnu.property").expect("the note");
1133 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1134 let want: Vec<u8> = [
1135 4u32,
1136 16,
1137 5,
1138 u32::from_le_bytes(*b"GNU\0"),
1139 Property::X86_FEATURES,
1140 4,
1141 Property::IBT | Property::SHSTK,
1142 0,
1143 ]
1144 .iter()
1145 .flat_map(|word| word.to_le_bytes())
1146 .collect();
1147 assert_eq!(note.data().expect("the bytes"), &want[..]);
1148 }
1149
1150 #[test]
1156 fn a_file_built_to_have_nothing_checked_says_nothing() {
1157 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1158 .expect("an object");
1159 let file = object::File::parse(&bytes[..]).expect("a readable object");
1160 assert!(file.section_by_name(".note.gnu.property").is_none());
1161 }
1162
1163 #[test]
1172 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1173 let mut text = calling("puts");
1174 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1175 text.bytes.resize(17, 0x90);
1176 text.unwind.bytes = vec![0; 64];
1179 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1180 text.unwind.relocs.push(Reloc {
1181 at,
1182 symbol: name.to_owned(),
1183 kind: Reference::Address { bytes: 8 },
1184 addend: 0,
1185 after: 0,
1186 });
1187 }
1188 let bytes =
1189 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1190 let file = object::File::parse(&bytes[..]).expect("a readable object");
1191 let mut found = points_at(&file);
1192 found.sort_unstable();
1193 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1194 }
1195
1196 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1199 let frames = file.section_by_name(".eh_frame").expect("the table");
1200 frames
1201 .relocations()
1202 .map(|(offset, reloc)| {
1203 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1204 panic!("a record points at something that is not a symbol");
1205 };
1206 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1207 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1208 let section = symbol.section_index().expect("a section symbol is in one");
1209 let name = file.section_by_index(section).expect("a readable section");
1210 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1211 })
1212 .collect()
1213 }
1214
1215 #[test]
1228 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1229 let mut text = two();
1230 text.unwind.bytes = vec![0; 64];
1231 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1232 text.unwind.relocs.push(Reloc {
1233 at,
1234 symbol: name.to_owned(),
1235 kind: Reference::Data,
1236 addend: 0,
1237 after: 0,
1238 });
1239 }
1240 let bytes =
1241 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1242 let file = object::File::parse(&bytes[..]).expect("a readable object");
1243 let mut whole = points_at(&file);
1244 whole.sort_unstable();
1245 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1246
1247 let sections =
1248 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1249 let bytes = write(&text, &Data::default(), &[], &target(), sections).expect("an object");
1250 let file = object::File::parse(&bytes[..]).expect("a readable object");
1251 let mut split = points_at(&file);
1252 split.sort_unstable();
1253 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1254 }
1255
1256 #[test]
1263 fn a_record_about_something_this_file_does_not_define_is_refused() {
1264 let mut text = calling("puts");
1265 text.unwind.bytes = vec![0; 64];
1266 text.unwind.relocs.push(Reloc {
1267 at: 32,
1268 symbol: "puts".to_owned(),
1269 kind: Reference::Data,
1270 addend: 0,
1271 after: 0,
1272 });
1273 let why = write(&text, &Data::default(), &[], &target(), Output::default())
1274 .expect_err("a record about a name from somewhere else");
1275 assert!(why.to_string().contains("puts"), "{why}");
1276 }
1277
1278 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1280 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1281 let index = symbol.section_index().expect("a section to be defined in");
1282 let section = file.section_by_index(index).expect("a readable section");
1283 section.name().expect("a named section").to_owned()
1284 }
1285
1286 fn two() -> Text {
1288 let mut text = calling("puts");
1289 text.bytes.resize(16, 0x90);
1292 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1293 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1294 text.relocs.push(Reloc {
1295 at: 17,
1296 symbol: "puts".to_owned(),
1297 kind: Reference::Call,
1298 addend: -4,
1299 after: 0,
1300 });
1301 text
1302 }
1303
1304 #[test]
1311 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1312 let sections =
1313 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1314 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1315 let file = object::File::parse(&bytes[..]).expect("a readable object");
1316 assert_eq!(lives_in(&file, "f"), ".text.f");
1317 assert_eq!(lives_in(&file, "g"), ".text.g");
1318 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1319 for name in ["f", "g"] {
1322 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1323 assert_eq!(symbol.address(), 0, "{name}");
1324 assert_eq!(symbol.size(), 6, "{name}");
1325 }
1326 let section = file.section_by_name(".text.g").expect("the second function");
1327 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1328 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1331 }
1332
1333 #[test]
1339 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1340 let sections =
1341 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1342 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1343 let file = object::File::parse(&bytes[..]).expect("a readable object");
1344 for name in [".text.f", ".text.g"] {
1345 let section = file.section_by_name(name).expect("a function");
1346 let (offset, _) = section.relocations().next().expect("the call in it");
1347 assert_eq!(offset, 1, "{name}");
1350 assert_eq!(section.relocations().count(), 1, "{name}");
1351 }
1352 }
1353
1354 fn variable(name: &str, place: Place) -> Object {
1356 Object {
1357 name: name.to_owned(),
1358 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1359 size: 4,
1360 align: 4,
1361 place,
1362 binding: Binding::Global,
1363 visibility: Visibility::Default,
1364 relocs: Vec::new(),
1365 }
1366 }
1367
1368 fn holding(object: Object) -> Vec<u8> {
1370 let data = Data { objects: vec![object] };
1371 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1372 }
1373
1374 #[test]
1375 fn what_a_variable_is_decides_which_section_it_goes_in() {
1376 for (place, wanted) in [
1377 (Place::Written, ".data"),
1378 (Place::ReadOnly, ".rodata"),
1379 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1380 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1381 (Place::Zero, ".bss"),
1382 (Place::Thread { zero: false }, ".tdata"),
1383 (Place::Thread { zero: true }, ".tbss"),
1384 (Place::Named(".init_array".to_owned()), ".init_array"),
1385 ] {
1386 let bytes = holding(variable("x", place.clone()));
1387 let file = object::File::parse(&bytes[..]).expect("a readable object");
1388 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1389 assert_eq!(section.size(), 4, "{place:?}");
1390 let carried = section.data().expect("the bytes").len();
1393 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1394 }
1395 }
1396
1397 #[test]
1403 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1404 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1405 let bytes = holding(variable("counter", place.clone()));
1406 let file = object::File::parse(&bytes[..]).expect("a readable object");
1407 let symbol = file
1408 .symbols()
1409 .find(|symbol| symbol.name() == Ok("counter"))
1410 .unwrap_or_else(|| panic!("{place:?}"));
1411 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1412 }
1413 }
1414
1415 #[test]
1421 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1422 for (name, wanted) in [
1423 (".init_array", elf::SHT_INIT_ARRAY),
1424 (".init_array.00101", elf::SHT_INIT_ARRAY),
1425 (".fini_array", elf::SHT_FINI_ARRAY),
1426 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1427 (".init_arrays", elf::SHT_PROGBITS),
1428 ] {
1429 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1430 let file = object::File::parse(&bytes[..]).expect("a readable object");
1431 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1432 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1433 panic!("{name} is not an elf section");
1434 };
1435 assert_eq!(sh_type, wanted, "{name}");
1436 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1437 }
1438 }
1439
1440 #[test]
1446 fn two_variables_in_one_named_section_share_it() {
1447 let objects = vec![
1448 variable("x", Place::Named(".init_array".to_owned())),
1449 variable("y", Place::Named(".init_array".to_owned())),
1450 ];
1451 let data = Data { objects };
1452 let bytes =
1453 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1454 let file = object::File::parse(&bytes[..]).expect("a readable object");
1455 let named: Vec<_> =
1456 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1457 assert_eq!(named.len(), 1);
1458 assert_eq!(named[0].size(), 8);
1459 }
1460
1461 #[test]
1465 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1466 let sections =
1467 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1468 for (place, wanted) in [
1469 (Place::Written, ".data.x"),
1470 (Place::ReadOnly, ".rodata.x"),
1471 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1472 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1473 (Place::Zero, ".bss.x"),
1474 (Place::Thread { zero: false }, ".tdata.x"),
1475 (Place::Thread { zero: true }, ".tbss.x"),
1476 ] {
1477 let data = Data { objects: vec![variable("x", place.clone())] };
1478 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1479 let file = object::File::parse(&bytes[..]).expect("a readable object");
1480 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1481 let section = file.section_by_name(wanted).expect("the section it named");
1482 assert_eq!(section.size(), 4, "{place:?}");
1483 let carried = section.data().expect("the bytes").len();
1486 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1487 }
1488 }
1489
1490 #[test]
1494 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1495 let sections =
1496 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1497 let named = Place::Named(".init_array".to_owned());
1498 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1499 let bytes =
1500 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1501 let file = object::File::parse(&bytes[..]).expect("a readable object");
1502 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1503 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1504 assert_eq!(lives_in(&file, "n"), ".init_array");
1505 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1506 }
1507
1508 #[test]
1512 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1513 let sections =
1514 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1515 let pointer = Object {
1516 bytes: vec![0; 8],
1517 size: 8,
1518 align: 8,
1519 relocs: vec![Reloc {
1520 at: 0,
1521 symbol: "y".to_owned(),
1522 kind: Reference::Address { bytes: 8 },
1523 addend: 0,
1524 after: 0,
1525 }],
1526 ..variable("p", Place::Written)
1527 };
1528 let objects = vec![variable("first", Place::Written), pointer];
1529 let bytes =
1530 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1531 let file = object::File::parse(&bytes[..]).expect("a readable object");
1532 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1533 let (offset, reloc) = section.relocations().next().expect("one relocation");
1534 assert_eq!(offset, 0);
1537 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1538 }
1539
1540 #[test]
1548 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1549 let place = Place::RelocReadOnly { local: true };
1550 let data =
1551 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
1552 let bytes =
1553 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1554 let file = object::File::parse(&bytes[..]).expect("a readable object");
1555 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1556 assert_eq!(named, 1, "one section holding both, not one each");
1557 }
1558
1559 #[test]
1560 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1561 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1562 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1563 let bytes =
1564 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1565 let file = object::File::parse(&bytes[..]).expect("a readable object");
1566 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1567 assert_eq!(second.kind(), SymbolKind::Data);
1568 assert_eq!(second.size(), 4);
1569 assert_eq!(second.address(), 16);
1573 }
1574
1575 #[test]
1576 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1577 for (binding, global, weak) in [
1578 (Binding::Global, true, false),
1579 (Binding::Local, false, false),
1580 (Binding::Weak, true, true),
1581 ] {
1582 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1583 let file = object::File::parse(&bytes[..]).expect("a readable object");
1584 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1585 assert_eq!(x.is_global(), global, "{binding:?}");
1586 assert_eq!(x.is_weak(), weak, "{binding:?}");
1587 }
1588 }
1589
1590 #[test]
1591 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1592 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1593 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1594 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1595 assert!(x.is_common(), "the linker merges every definition of this name into one");
1596 assert_eq!(x.size(), 4);
1597 assert_eq!(x.address(), 0);
1601 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1602 }
1603
1604 #[test]
1605 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1606 let object = Object {
1607 bytes: vec![0; 8],
1608 size: 8,
1609 align: 8,
1610 relocs: vec![Reloc {
1611 at: 0,
1612 symbol: "y".to_owned(),
1613 kind: Reference::Address { bytes: 8 },
1614 addend: 16,
1615 after: 0,
1616 }],
1617 ..variable("p", Place::Written)
1618 };
1619 let bytes = holding(object);
1620 let file = object::File::parse(&bytes[..]).expect("a readable object");
1621 let section = file.section_by_name(".data").expect("a data section");
1622 let (offset, reloc) = section.relocations().next().expect("one relocation");
1623 assert_eq!(offset, 0);
1624 assert_eq!(reloc.addend(), 16);
1625 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1626 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1627 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1628 }
1629
1630 #[test]
1632 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1633 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1634 data.objects.push(Object {
1635 bytes: vec![0; 16],
1636 size: 16,
1637 align: 8,
1638 relocs: vec![Reloc {
1639 at: 8,
1640 symbol: "y".to_owned(),
1641 kind: Reference::Address { bytes: 8 },
1642 addend: 0,
1643 after: 0,
1644 }],
1645 ..variable("second", Place::Written)
1646 });
1647 let bytes =
1648 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1649 let file = object::File::parse(&bytes[..]).expect("a readable object");
1650 let section = file.section_by_name(".data").expect("a data section");
1651 let (offset, _) = section.relocations().next().expect("one relocation");
1652 assert_eq!(offset, 16);
1655 }
1656
1657 #[test]
1658 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1659 let data = Data {
1660 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1661 };
1662 let aliases = [Alias {
1663 name: "b".to_owned(),
1664 target: "a".to_owned(),
1665 binding: Binding::Global,
1666 visibility: Visibility::Default,
1667 }];
1668 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1669 .expect("an object");
1670 let file = object::File::parse(&bytes[..]).expect("a readable object");
1671 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1672 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1673 assert_eq!(b.address(), a.address(), "the same place");
1674 assert_eq!(b.size(), a.size());
1675 assert_eq!(b.section_index(), a.section_index());
1676 assert!(a.is_local(), "the target was written `static`");
1679 assert!(b.is_global(), "and the name given to it was not");
1680 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1682 }
1683
1684 #[test]
1685 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1686 let text = calling("puts");
1687 let aliases = [Alias {
1688 name: "g".to_owned(),
1689 target: "f".to_owned(),
1690 binding: Binding::Weak,
1691 visibility: Visibility::Default,
1692 }];
1693 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1694 .expect("an object");
1695 let file = object::File::parse(&bytes[..]).expect("a readable object");
1696 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1697 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1698 assert_eq!(g.address(), f.address());
1699 assert_eq!(g.size(), f.size());
1700 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1701 assert!(g.is_weak(), "so that a program may define the name itself instead");
1702 }
1703
1704 #[test]
1707 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1708 let aliases = [Alias {
1709 name: "b".to_owned(),
1710 target: "a".to_owned(),
1711 binding: Binding::Global,
1712 visibility: Visibility::Default,
1713 }];
1714 let error =
1715 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1716 .expect_err("nothing to point at");
1717 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1718 }
1719
1720 #[test]
1721 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1722 let text = calling("puts");
1723 for triple in [
1724 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1725 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1726 ] {
1727 let error =
1728 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1729 .expect_err("no writer");
1730 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1731 }
1732 }
1733
1734 #[test]
1740 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
1741 let mut text = calling("puts");
1742 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1743 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1744 text.bytes.resize(33, 0x90);
1745 let data = Data {
1746 objects: vec![variable("seen", Place::Written), {
1747 let mut quiet = variable("quiet", Place::Zero);
1748 quiet.binding = Binding::Local;
1749 quiet
1750 }],
1751 };
1752 let aliases = [Alias {
1753 name: "second".to_owned(),
1754 target: "f".to_owned(),
1755 binding: Binding::Global,
1756 visibility: Visibility::Default,
1757 }];
1758
1759 let names = defines(&text, &data, &aliases, &target()).expect("a list");
1760 assert_eq!(names, ["f", "shared", "seen", "second"]);
1761
1762 let bytes = write(&text, &data, &aliases, &target(), Output::default()).expect("an object");
1763 let file = object::File::parse(&bytes[..]).expect("a readable object");
1764 let found: Vec<String> = file
1765 .symbols()
1766 .filter(|symbol| symbol.is_global() && symbol.is_definition())
1767 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
1768 .collect();
1769 let mut sorted = names.clone();
1770 sorted.sort();
1771 let mut theirs = found;
1772 theirs.sort();
1773 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
1774 }
1775
1776 fn windows() -> TargetInfo {
1778 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
1779 }
1780
1781 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
1783 let file = object::File::parse(bytes).expect("a readable object");
1784 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
1785 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
1786 }
1787
1788 #[test]
1789 fn a_windows_target_is_written_rather_than_refused() {
1790 let text = calling("puts");
1791 let bytes =
1792 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
1793 let file = object::File::parse(&bytes[..]).expect("a readable object");
1794 assert_eq!(file.format(), BinaryFormat::Coff);
1795 let section = file.section_by_name(".text").expect("a text section");
1796 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1797 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
1798 assert!(names.contains(&"f"), "{names:?}");
1799 assert!(names.contains(&"puts"), "{names:?}");
1800 }
1801
1802 #[test]
1810 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
1811 for (after, typ) in [
1812 (0, pe::IMAGE_REL_AMD64_REL32),
1813 (1, pe::IMAGE_REL_AMD64_REL32_1),
1814 (4, pe::IMAGE_REL_AMD64_REL32_4),
1815 (5, pe::IMAGE_REL_AMD64_REL32_5),
1816 ] {
1817 let mut text = calling("puts");
1818 text.relocs[0].addend = -4 - i64::from(after);
1821 text.relocs[0].after = after;
1822 text.bytes.resize(6 + after as usize, 0x90);
1823 text.funcs[0].len = text.bytes.len();
1824 let bytes = write(&text, &Data::default(), &[], &windows(), Output::default())
1825 .expect("an object");
1826 let file = object::File::parse(&bytes[..]).expect("a readable object");
1827 let section = file.section_by_name(".text").expect("a text section");
1828 let (_, reloc) = section.relocations().next().expect("the relocation");
1829 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
1830 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
1833 }
1834 }
1835
1836 #[test]
1839 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
1840 let mut text = calling("puts");
1841 text.relocs[0].addend = 12;
1842 let bytes =
1843 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
1844 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
1845 }
1846
1847 #[test]
1848 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
1849 let object = Object {
1850 bytes: vec![0; 8],
1851 size: 8,
1852 align: 8,
1853 relocs: vec![Reloc {
1854 at: 0,
1855 symbol: "y".to_owned(),
1856 kind: Reference::Address { bytes: 8 },
1857 addend: 0,
1858 after: 0,
1859 }],
1860 ..variable("p", Place::Written)
1861 };
1862 let data = Data { objects: vec![object] };
1863 let bytes =
1864 write(&Text::default(), &data, &[], &windows(), Output::default()).expect("an object");
1865 let file = object::File::parse(&bytes[..]).expect("a readable object");
1866 let section = file.section_by_name(".data").expect("a data section");
1867 let (_, reloc) = section.relocations().next().expect("the relocation");
1868 let typ = pe::IMAGE_REL_AMD64_ADDR64;
1869 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
1870 }
1871
1872 #[test]
1875 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
1876 for local in [false, true] {
1877 let data = Data { objects: vec![variable("p", Place::RelocReadOnly { local })] };
1878 let bytes = write(&Text::default(), &data, &[], &windows(), Output::default())
1879 .expect("an object");
1880 let file = object::File::parse(&bytes[..]).expect("a readable object");
1881 assert!(file.section_by_name(".rdata").is_some(), "{local}");
1882 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
1883 }
1884 }
1885
1886 #[test]
1889 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
1890 let text = calling("puts");
1891 let output = Output { property: Property { features: 3 }, ..Output::default() };
1892 let bytes = write(&text, &Data::default(), &[], &windows(), output).expect("an object");
1893 let file = object::File::parse(&bytes[..]).expect("a readable object");
1894 assert!(file.section_by_name(".note.GNU-stack").is_none());
1895 assert!(file.section_by_name(".note.gnu.property").is_none());
1896 }
1897
1898 #[test]
1903 fn what_this_format_cannot_say_is_refused_by_name() {
1904 let ordinary = Text::default();
1905 let empty = Data::default();
1906
1907 let mut thread = Data::default();
1908 thread.objects.push(variable("t", Place::Thread { zero: false }));
1909
1910 let mut gathered = Data::default();
1911 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
1912
1913 let mut table = calling("puts");
1914 table.relocs[0].kind = Reference::Got;
1915
1916 let mut room = calling("puts");
1917 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1918
1919 let cases: [(&str, &Text, &Data); 4] = [
1920 ("thread-local", &ordinary, &thread),
1921 ("startup", &ordinary, &gathered),
1922 ("table", &table, &empty),
1923 ("patcher", &room, &empty),
1924 ];
1925 for (what, text, data) in cases {
1926 let error = write(text, data, &[], &windows(), Output::default())
1927 .expect_err("something this format cannot write");
1928 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
1929 }
1930 }
1931
1932 #[test]
1936 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
1937 let mut text = calling("puts");
1938 text.funcs[0].visibility = Visibility::Hidden;
1939 let bytes =
1940 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
1941 let file = object::File::parse(&bytes[..]).expect("a readable object");
1942 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
1943 assert!(symbol.is_global(), "a name others may use either way");
1944 }
1945
1946 #[test]
1947 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
1948 let text = calling("puts");
1949 let data = Data { objects: vec![variable("shared", Place::Written)] };
1950 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
1951 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
1952 }
1953
1954 #[test]
1958 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
1959 let text = calling("puts");
1960 for triple in [
1961 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1962 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1963 ] {
1964 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
1965 .expect_err("no writer");
1966 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1967 }
1968 }
1969}