1use std::collections::{HashMap, HashSet};
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 pub(crate) 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 pub(crate) fn binary(self) -> BinaryFormat {
77 match self {
78 Flavour::Elf => BinaryFormat::Elf,
79 Flavour::Coff => BinaryFormat::Coff,
80 }
81 }
82
83 pub(crate) 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 pub(crate) fn see(
100 self,
101 obj: &mut Writer<'_>,
102 id: SymbolId,
103 binding: Binding,
104 visibility: Visibility,
105 ) {
106 match self {
107 Flavour::Elf => elf::see(obj, id, binding, visibility),
108 Flavour::Coff => {}
109 }
110 }
111
112 fn rel_ro_local(self) -> Option<&'static str> {
116 match self {
117 Flavour::Elf => elf::REL_RO_LOCAL,
118 Flavour::Coff => coff::REL_RO_LOCAL,
119 }
120 }
121
122 fn gathered(self, array: Array) -> Option<SectionFlags> {
128 match self {
129 Flavour::Elf => Some(elf::gathered(array)),
130 Flavour::Coff => None,
131 }
132 }
133
134 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
143 match self {
144 Flavour::Elf => {
145 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
146 }
147 Flavour::Coff => None,
148 }
149 }
150
151 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
163 match sort {
164 crate::source::Sort::Func => SymbolKind::Text,
165 crate::source::Sort::Object => SymbolKind::Data,
166 crate::source::Sort::Thread => SymbolKind::Tls,
167 crate::source::Sort::File => SymbolKind::File,
168 crate::source::Sort::Untyped => match (self, binding) {
169 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
170 _ => SymbolKind::Label,
171 },
172 }
173 }
174
175 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
177 match self {
178 Flavour::Elf => elf::marker(obj),
179 Flavour::Coff => coff::marker(obj),
180 }
181 }
182
183 fn property(self, obj: &mut Writer<'_>, property: Property) {
189 if !property.any() {
190 return;
191 }
192 match self {
193 Flavour::Elf => {
194 let note = obj.section_id(StandardSection::GnuProperty);
195 obj.append_section_data(note, &elf::record(property), 8);
196 }
197 Flavour::Coff => {}
198 }
199 }
200
201 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
205 match self {
206 Flavour::Elf => (elf::FRAMES, None),
207 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
208 }
209 }
210
211 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
213 match self {
214 Flavour::Elf => elf::link(bytes, ordered),
215 Flavour::Coff => debug_assert!(ordered.is_empty(), "a record this format cannot write"),
216 }
217 }
218}
219
220#[derive(Debug, Clone, PartialEq, Eq)]
222pub enum Error {
223 Format {
225 triple: String,
227 },
228 Refused {
230 why: String,
232 },
233}
234
235impl std::fmt::Display for Error {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 Error::Format { triple } => {
239 write!(f, "there is no object writer for {triple} in this compiler yet")
240 }
241 Error::Refused { why } => {
242 write!(f, "the object writer refused what it was given: {why}")
243 }
244 }
245 }
246}
247
248impl std::error::Error for Error {}
249
250pub fn write(
262 text: &Text,
263 data: &Data,
264 aliases: &[Alias],
265 target: &TargetInfo,
266 output: Output,
267) -> Result<Vec<u8>, Error> {
268 let Output { sections, property } = output;
269 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
270 let Some(flavour) = flavour else {
271 return Err(Error::Format { triple: target.tuple.to_string() });
272 };
273 if flavour == Flavour::Coff {
274 beyond(text, data)?;
275 }
276 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
277 let whole = obj.section_id(StandardSection::Text);
281 if !sections.functions {
282 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
283 }
284
285 let mut symbols = std::collections::BTreeMap::new();
289 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
294 let mut ordered: Vec<String> = Vec::new();
297 for func in &text.funcs {
298 let ahead = func.patch.map_or(0, |patch| patch.before);
308 let (section, at) = if sections.functions {
309 let name = format!(".text.{}", func.name).into_bytes();
310 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
311 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
312 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
313 (id, ahead as u64)
314 } else {
315 (whole, func.start as u64)
316 };
317 if let Some(patch) = func.patch {
332 let base = if sections.functions { func.start - ahead } else { 0 };
333 let name = elf::PATCHABLE.as_bytes().to_vec();
334 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
335 obj.section_mut(id).flags = elf::ordered();
336 obj.append_section_data(id, &[0; 8], 8);
337 let symbol = obj.section_symbol(section);
338 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
339 Error::Refused { why: "no relocation holds an address here".to_owned() }
340 })?;
341 obj.add_relocation(
342 id,
343 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
344 )
345 .map_err(|why| Error::Refused { why: why.to_string() })?;
346 ordered.push(if sections.functions {
347 format!(".text.{}", func.name)
348 } else {
349 ".text".to_owned()
350 });
351 }
352 let id = obj.add_symbol(Symbol {
353 name: func.name.clone().into_bytes(),
354 value: at,
355 size: func.len as u64,
356 kind: SymbolKind::Text,
357 scope: scope_of(func.binding),
358 weak: func.binding == Binding::Weak,
359 section: SymbolSection::Section(section),
360 flags: SymbolFlags::None,
361 });
362 flavour.see(&mut obj, id, func.binding, func.visibility);
363 symbols.insert(func.name.clone(), id);
364 split.push((section, at));
365 }
366
367 for label in &text.labels {
371 let after = text.funcs.partition_point(|func| func.start <= label.at);
372 let Some(index) = after.checked_sub(1) else {
373 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
374 return Err(Error::Refused { why });
375 };
376 let func = &text.funcs[index];
377 let (section, at) = if sections.functions {
378 let base = func.start - func.patch.map_or(0, |patch| patch.before);
381 (split[index].0, (label.at - base) as u64)
382 } else {
383 (whole, label.at as u64)
384 };
385 let id = obj.add_symbol(Symbol {
386 name: label.name.clone().into_bytes(),
387 value: at,
388 size: 0,
391 kind: SymbolKind::Label,
392 scope: SymbolScope::Compilation,
396 weak: false,
397 section: SymbolSection::Section(section),
398 flags: SymbolFlags::None,
399 });
400 symbols.insert(label.name.clone(), id);
401 }
402
403 let mut placed = Vec::with_capacity(data.objects.len());
408 let mut named = HashMap::new();
412 for object in &data.objects {
413 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
414 let id = obj.add_symbol(Symbol {
415 name: object.name.clone().into_bytes(),
416 value: if object.place == Place::Merged { object.align } else { offset },
419 size: object.size,
420 kind: match object.place {
425 Place::Thread { .. } => SymbolKind::Tls,
426 _ => SymbolKind::Data,
427 },
428 scope: scope_of(object.binding),
429 weak: object.binding == Binding::Weak,
430 section,
431 flags: SymbolFlags::None,
432 });
433 flavour.see(&mut obj, id, object.binding, object.visibility);
434 symbols.insert(object.name.clone(), id);
435 placed.push((section.id(), offset));
436 }
437
438 for alias in aliases {
444 let Some(&id) = symbols.get(&alias.target) else {
445 let why =
446 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
447 return Err(Error::Refused { why });
448 };
449 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
450 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
451 let id = obj.add_symbol(Symbol {
452 name: alias.name.clone().into_bytes(),
453 value,
454 size,
455 kind,
456 scope: scope_of(alias.binding),
457 weak: alias.binding == Binding::Weak,
458 section,
459 flags: SymbolFlags::None,
460 });
461 flavour.see(&mut obj, id, alias.binding, alias.visibility);
462 symbols.insert(alias.name.clone(), id);
463 }
464
465 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
472 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
473 let thread: HashSet<&str> = relocs()
478 .filter(|reloc| reloc.kind == Reference::Thread)
479 .map(|reloc| reloc.symbol.as_str())
480 .collect();
481 let wanted: Vec<&String> =
482 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
483 for name in wanted {
484 if symbols.contains_key(name) {
485 continue;
486 }
487 let id = obj.add_symbol(Symbol {
488 name: name.clone().into_bytes(),
489 value: 0,
490 size: 0,
491 kind: if thread.contains(name.as_str()) {
501 SymbolKind::Tls
502 } else {
503 SymbolKind::Unknown
504 },
505 scope: SymbolScope::Dynamic,
506 weak: weak.contains(name.as_str()),
507 section: SymbolSection::Undefined,
508 flags: SymbolFlags::None,
509 });
510 symbols.insert(name.clone(), id);
511 }
512
513 for reloc in &text.relocs {
514 let (section, at) = if sections.functions {
519 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
520 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
521 let why = format!("a relocation at {} is in front of every function", reloc.at);
522 return Err(Error::Refused { why });
523 };
524 let base = func.start - func.patch.map_or(0, |patch| patch.before);
527 (split[after - 1].0, (reloc.at - base) as u64)
528 } else {
529 (whole, reloc.at as u64)
530 };
531 add(&mut obj, section, at, reloc, &symbols, flavour)?;
532 }
533
534 if !text.unwind.bytes.is_empty() {
538 let ((name, align), second) = flavour.tables();
539 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
540 obj.append_section_data(frames, &text.unwind.bytes, align);
541 let mut described = HashMap::new();
546 if !text.unwind.info.is_empty() {
547 let Some((name, align)) = second else {
548 let why = "an unwind table here is one section and it was given two".to_owned();
549 return Err(Error::Refused { why });
550 };
551 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
552 obj.append_section_data(codes, &text.unwind.info, align);
553 for label in &text.unwind.labels {
554 let id = obj.add_symbol(Symbol {
555 name: label.name.clone().into_bytes(),
556 value: label.at as u64,
557 size: 0,
558 kind: SymbolKind::Label,
559 scope: SymbolScope::Compilation,
560 weak: false,
561 section: SymbolSection::Section(codes),
562 flags: SymbolFlags::None,
563 });
564 described.insert(label.name.clone(), id);
565 }
566 }
567 for reloc in &text.unwind.relocs {
568 let (symbol, addend) = match described.get(&reloc.symbol) {
569 Some(&id) => (id, reloc.addend),
573 None => {
587 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
588 let Some((section, at)) = found.map(|i| split[i]) else {
589 let why = format!(
590 "'{}' has an unwind record and is not a function here",
591 reloc.symbol
592 );
593 return Err(Error::Refused { why });
594 };
595 (obj.section_symbol(section), reloc.addend + at as i64)
599 }
600 };
601 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
602 why: format!("no relocation is {:?}", reloc.kind),
603 })?;
604 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
605 obj.add_relocation(frames, record)
606 .map_err(|why| Error::Refused { why: why.to_string() })?;
607 }
608 }
609 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
610 let Some(section) = section else { continue };
611 for reloc in &object.relocs {
612 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
613 }
614 }
615
616 flavour.property(&mut obj, property);
620
621 flavour.marker(&mut obj);
624
625 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
626 flavour.finish(&mut bytes, &ordered);
627 Ok(bytes)
628}
629
630fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
643 let why = |why: String| Err(Error::Refused { why });
644 if text.funcs.iter().any(|func| func.patch.is_some()) {
645 return why("a record of where a patcher's room is has no section flags here".to_owned());
646 }
647 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
648 if matches!(reloc.kind, Reference::Got | Reference::Thread) {
649 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
650 }
651 }
652 for object in &data.objects {
653 if matches!(object.place, Place::Thread { .. }) {
654 return why(format!("'{}' is thread-local and this format is not", object.name));
655 }
656 let Place::Named(name) = &object.place else { continue };
657 if Array::of(name).is_some() {
658 return why(format!("'{name}' is not a list the startup code here gathers"));
659 }
660 }
661 Ok(())
662}
663
664pub fn defines(
688 text: &Text,
689 data: &Data,
690 aliases: &[Alias],
691 target: &TargetInfo,
692) -> Result<Vec<String>, Error> {
693 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
694 return Err(Error::Format { triple: target.tuple.to_string() });
695 }
696 let names = text
697 .funcs
698 .iter()
699 .filter(|func| func.binding != Binding::Local)
700 .map(|func| func.name.clone())
701 .chain(
702 data.objects
703 .iter()
704 .filter(|object| object.binding != Binding::Local)
705 .map(|object| object.name.clone()),
706 )
707 .chain(
708 aliases
709 .iter()
710 .filter(|alias| alias.binding != Binding::Local)
711 .map(|alias| alias.name.clone()),
712 )
713 .collect();
714 Ok(names)
715}
716
717fn put(
724 obj: &mut Writer<'_>,
725 object: &Object,
726 named: &mut HashMap<String, object::write::SectionId>,
727 sections: Sections,
728 flavour: Flavour,
729) -> (SymbolSection, u64) {
730 if sections.data {
736 if let Some(name) = object.place.split(&object.name) {
737 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
738 let offset = if carries_no_bytes(&object.place) {
739 obj.append_section_bss(section, object.size, object.align)
740 } else {
741 obj.append_section_data(section, &object.bytes, object.align)
742 };
743 return (SymbolSection::Section(section), offset);
744 }
745 }
746 let section = match &object.place {
747 Place::Written => obj.section_id(StandardSection::Data),
748 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
749 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
755 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
756 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
757 },
758 Place::Zero => obj.section_id(StandardSection::UninitializedData),
759 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
760 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
761 Place::Merged => return (SymbolSection::Common, 0),
762 Place::Named(name) => {
768 let section = made(obj, named, name, SectionKind::Data);
769 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
770 obj.section_mut(section).flags = flags;
771 }
772 section
773 }
774 };
775 let offset = if carries_no_bytes(&object.place) {
776 obj.append_section_bss(section, object.size, object.align)
777 } else {
778 obj.append_section_data(section, &object.bytes, object.align)
779 };
780 (SymbolSection::Section(section), offset)
781}
782
783fn carries_no_bytes(place: &Place) -> bool {
789 matches!(place, Place::Zero | Place::Thread { zero: true })
790}
791
792fn made(
800 obj: &mut Writer<'_>,
801 named: &mut HashMap<String, object::write::SectionId>,
802 name: &str,
803 kind: SectionKind,
804) -> object::write::SectionId {
805 if let Some(section) = named.get(name) {
806 return *section;
807 }
808 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
809 named.insert(name.to_owned(), section);
810 section
811}
812
813fn kind_of(place: &Place) -> SectionKind {
822 match place {
823 Place::ReadOnly => SectionKind::ReadOnlyData,
824 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
825 Place::Zero => SectionKind::UninitializedData,
826 Place::Thread { zero: false } => SectionKind::Tls,
827 Place::Thread { zero: true } => SectionKind::UninitializedTls,
828 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
829 }
830}
831
832fn add(
839 obj: &mut Writer<'_>,
840 section: object::write::SectionId,
841 at: u64,
842 reloc: &Reloc,
843 symbols: &std::collections::BTreeMap<String, SymbolId>,
844 flavour: Flavour,
845) -> Result<(), Error> {
846 let flags = flavour
847 .reloc(reloc.kind, reloc.after)
848 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
849 obj.add_relocation(
850 section,
851 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
852 )
853 .map_err(|why| Error::Refused { why: why.to_string() })
854}
855
856pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
869 match binding {
870 Binding::Local => SymbolScope::Compilation,
871 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
872 }
873}
874
875#[cfg(test)]
876mod tests {
877 use super::*;
878
879 use object::read::elf::Sym as _;
880 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
881 use object::{elf, pe};
882 use rucc_target::{Arch, Env, Os, Triple};
883
884 use crate::elf::PATCHABLE;
885 use crate::section::{Extent, Patch, Reloc};
886
887 fn target() -> TargetInfo {
889 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
890 }
891
892 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
897 Extent {
898 name,
899 start,
900 len,
901 align: crate::FUNC_ALIGN,
902 binding,
903 visibility: Visibility::Default,
904 patch: None,
905 }
906 }
907
908 fn calling(name: &str) -> Text {
910 Text {
911 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
912 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
913 relocs: vec![Reloc {
914 at: 1,
915 symbol: name.to_owned(),
916 kind: Reference::Call,
917 addend: -4,
918 after: 0,
919 }],
920 ..Text::default()
921 }
922 }
923
924 #[test]
925 fn the_bytes_come_back_out_of_the_section_they_went_into() {
926 let text = calling("puts");
927 let bytes =
928 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
929 let file = object::File::parse(&bytes[..]).expect("a readable object");
930 let section = file.section_by_name(".text").expect("a text section");
931 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
932 }
933
934 #[test]
935 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
936 let mut text = calling("puts");
937 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
938 text.bytes.resize(17, 0x90);
939 let bytes =
940 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
941 let file = object::File::parse(&bytes[..]).expect("a readable object");
942 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
943 assert_eq!(g.address(), 16);
944 assert_eq!(g.size(), 1);
945 assert_eq!(g.kind(), SymbolKind::Text);
946 assert!(g.is_global(), "nothing said otherwise about this one");
947 }
948
949 #[test]
950 fn a_function_no_other_file_can_see_is_a_local_symbol() {
951 let mut text = calling("puts");
952 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
953 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
954 text.bytes.resize(33, 0x90);
955 let bytes =
956 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
957 let file = object::File::parse(&bytes[..]).expect("a readable object");
958 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
959 assert!(hidden.is_local(), "a static function must not be offered to the linker");
962 assert!(!hidden.is_weak());
963 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
964 assert!(shared.is_weak(), "a weak function has to be able to lose");
965 assert!(shared.is_global());
966 }
967
968 #[test]
985 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
986 let mut text = calling("puts");
987 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
988 text.funcs[0].start = 3;
989 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
990 text.relocs[0].at = 4;
991 let bytes =
992 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
993 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
994 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
995 assert_eq!(section.size(), 8, "one address, and this file defines one function");
996 assert_eq!(section.align(), 8);
997 let header = section.elf_section_header();
998 assert_eq!(
999 header.sh_flags.get(Endianness::Little),
1000 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1001 );
1002 let index = file.section_by_name(".text").expect("a text section").index().0;
1005 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1006 assert_ne!(index, 0);
1007
1008 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1010 panic!("one address in the record")
1011 };
1012 assert_eq!(*at, 0);
1013 assert_eq!(reloc.addend(), 0);
1014 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1015 }
1016
1017 #[test]
1019 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1020 let text = calling("puts");
1021 let bytes =
1022 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1023 let file = object::File::parse(&bytes[..]).expect("a readable object");
1024 assert!(file.section_by_name(PATCHABLE).is_none());
1025 }
1026
1027 #[test]
1033 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1034 let mut text = calling("puts");
1035 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1036 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1037 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1038 text.bytes.resize(17, 0x90);
1039 let output =
1040 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1041 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
1042 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1043 let links: Vec<usize> = file
1044 .sections()
1045 .filter(|section| section.name() == Ok(PATCHABLE))
1046 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1047 .collect();
1048 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1049 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1050 }
1051
1052 #[test]
1053 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1054 let mut text = calling("puts");
1055 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1056 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1057 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1058 text.bytes.resize(49, 0x90);
1059 let bytes =
1060 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1061 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1062 let visibility = |name: &str| {
1063 file.symbols()
1064 .find(|s| s.name() == Ok(name))
1065 .expect("the function")
1066 .elf_symbol()
1067 .st_visibility()
1068 };
1069 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1071 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1072 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1075 }
1076
1077 #[test]
1087 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1088 let mut text = calling("puts");
1089 for (index, (name, seen)) in
1090 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1091 {
1092 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1093 func.visibility = seen;
1094 text.funcs.push(func);
1095 }
1096 text.bytes.resize(49, 0x90);
1097 let mut data = Data::default();
1098 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1099 let mut object = variable(name, Place::Written);
1100 object.visibility = seen;
1101 data.objects.push(object);
1102 }
1103 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
1104 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1105 let visibility = |name: &str| {
1106 file.symbols()
1107 .find(|s| s.name() == Ok(name))
1108 .expect("the symbol")
1109 .elf_symbol()
1110 .st_visibility()
1111 };
1112 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1113 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1114 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1115 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1116 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1119 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1120 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1121 }
1122
1123 #[test]
1124 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1125 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1126 .expect("an object");
1127 let file = object::File::parse(&bytes[..]).expect("a readable object");
1128 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1129 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1130 }
1131
1132 #[test]
1133 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1134 for (reference, wanted) in [
1135 (Reference::Call, elf::R_X86_64_PLT32),
1136 (Reference::Data, elf::R_X86_64_PC32),
1137 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1138 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1139 ] {
1140 let mut text = calling("puts");
1141 text.relocs[0].kind = reference;
1142 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
1143 .expect("an object");
1144 let file = object::File::parse(&bytes[..]).expect("a readable object");
1145 let section = file.section_by_name(".text").expect("a text section");
1146 let (offset, reloc) = section.relocations().next().expect("one relocation");
1147 assert_eq!(offset, 1);
1148 assert_eq!(reloc.addend(), -4);
1149 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1150 }
1151 }
1152
1153 #[test]
1154 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1155 let mut text = calling("puts");
1156 text.relocs.push(Reloc {
1157 at: 1,
1158 symbol: "puts".to_owned(),
1159 kind: Reference::Call,
1160 addend: -4,
1161 after: 0,
1162 });
1163 let bytes =
1164 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1165 let file = object::File::parse(&bytes[..]).expect("a readable object");
1166 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1167 }
1168
1169 #[test]
1170 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1171 let text = calling("f");
1172 let bytes =
1173 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1174 let file = object::File::parse(&bytes[..]).expect("a readable object");
1175 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1176 let f = found.next().expect("the function");
1177 assert!(!f.is_undefined(), "the file defines it");
1178 assert!(found.next().is_none(), "and defines it once");
1179 }
1180
1181 #[test]
1182 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1183 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1184 .expect("an object");
1185 let file = object::File::parse(&bytes[..]).expect("a readable object");
1186 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1187 assert!(note.data().expect("no bytes").is_empty());
1188 }
1189
1190 #[test]
1197 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1198 let property = Property { features: Property::IBT | Property::SHSTK };
1199 let output = Output { property, ..Output::default() };
1200 let bytes =
1201 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
1202 let file = object::File::parse(&bytes[..]).expect("a readable object");
1203 let note = file.section_by_name(".note.gnu.property").expect("the note");
1204 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1205 let want: Vec<u8> = [
1206 4u32,
1207 16,
1208 5,
1209 u32::from_le_bytes(*b"GNU\0"),
1210 Property::X86_FEATURES,
1211 4,
1212 Property::IBT | Property::SHSTK,
1213 0,
1214 ]
1215 .iter()
1216 .flat_map(|word| word.to_le_bytes())
1217 .collect();
1218 assert_eq!(note.data().expect("the bytes"), &want[..]);
1219 }
1220
1221 #[test]
1227 fn a_file_built_to_have_nothing_checked_says_nothing() {
1228 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1229 .expect("an object");
1230 let file = object::File::parse(&bytes[..]).expect("a readable object");
1231 assert!(file.section_by_name(".note.gnu.property").is_none());
1232 }
1233
1234 #[test]
1243 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1244 let mut text = calling("puts");
1245 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1246 text.bytes.resize(17, 0x90);
1247 text.unwind.bytes = vec![0; 64];
1250 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1251 text.unwind.relocs.push(Reloc {
1252 at,
1253 symbol: name.to_owned(),
1254 kind: Reference::Address { bytes: 8 },
1255 addend: 0,
1256 after: 0,
1257 });
1258 }
1259 let bytes =
1260 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1261 let file = object::File::parse(&bytes[..]).expect("a readable object");
1262 let mut found = points_at(&file);
1263 found.sort_unstable();
1264 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1265 }
1266
1267 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1270 let frames = file.section_by_name(".eh_frame").expect("the table");
1271 frames
1272 .relocations()
1273 .map(|(offset, reloc)| {
1274 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1275 panic!("a record points at something that is not a symbol");
1276 };
1277 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1278 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1279 let section = symbol.section_index().expect("a section symbol is in one");
1280 let name = file.section_by_index(section).expect("a readable section");
1281 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1282 })
1283 .collect()
1284 }
1285
1286 #[test]
1299 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1300 let mut text = two();
1301 text.unwind.bytes = vec![0; 64];
1302 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1303 text.unwind.relocs.push(Reloc {
1304 at,
1305 symbol: name.to_owned(),
1306 kind: Reference::Data,
1307 addend: 0,
1308 after: 0,
1309 });
1310 }
1311 let bytes =
1312 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1313 let file = object::File::parse(&bytes[..]).expect("a readable object");
1314 let mut whole = points_at(&file);
1315 whole.sort_unstable();
1316 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1317
1318 let sections =
1319 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1320 let bytes = write(&text, &Data::default(), &[], &target(), sections).expect("an object");
1321 let file = object::File::parse(&bytes[..]).expect("a readable object");
1322 let mut split = points_at(&file);
1323 split.sort_unstable();
1324 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1325 }
1326
1327 #[test]
1334 fn a_record_about_something_this_file_does_not_define_is_refused() {
1335 let mut text = calling("puts");
1336 text.unwind.bytes = vec![0; 64];
1337 text.unwind.relocs.push(Reloc {
1338 at: 32,
1339 symbol: "puts".to_owned(),
1340 kind: Reference::Data,
1341 addend: 0,
1342 after: 0,
1343 });
1344 let why = write(&text, &Data::default(), &[], &target(), Output::default())
1345 .expect_err("a record about a name from somewhere else");
1346 assert!(why.to_string().contains("puts"), "{why}");
1347 }
1348
1349 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1351 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1352 let index = symbol.section_index().expect("a section to be defined in");
1353 let section = file.section_by_index(index).expect("a readable section");
1354 section.name().expect("a named section").to_owned()
1355 }
1356
1357 fn two() -> Text {
1359 let mut text = calling("puts");
1360 text.bytes.resize(16, 0x90);
1363 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1364 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1365 text.relocs.push(Reloc {
1366 at: 17,
1367 symbol: "puts".to_owned(),
1368 kind: Reference::Call,
1369 addend: -4,
1370 after: 0,
1371 });
1372 text
1373 }
1374
1375 #[test]
1382 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1383 let sections =
1384 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1385 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1386 let file = object::File::parse(&bytes[..]).expect("a readable object");
1387 assert_eq!(lives_in(&file, "f"), ".text.f");
1388 assert_eq!(lives_in(&file, "g"), ".text.g");
1389 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1390 for name in ["f", "g"] {
1393 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1394 assert_eq!(symbol.address(), 0, "{name}");
1395 assert_eq!(symbol.size(), 6, "{name}");
1396 }
1397 let section = file.section_by_name(".text.g").expect("the second function");
1398 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1399 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1402 }
1403
1404 #[test]
1410 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1411 let sections =
1412 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1413 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1414 let file = object::File::parse(&bytes[..]).expect("a readable object");
1415 for name in [".text.f", ".text.g"] {
1416 let section = file.section_by_name(name).expect("a function");
1417 let (offset, _) = section.relocations().next().expect("the call in it");
1418 assert_eq!(offset, 1, "{name}");
1421 assert_eq!(section.relocations().count(), 1, "{name}");
1422 }
1423 }
1424
1425 fn variable(name: &str, place: Place) -> Object {
1427 Object {
1428 name: name.to_owned(),
1429 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1430 size: 4,
1431 align: 4,
1432 place,
1433 binding: Binding::Global,
1434 visibility: Visibility::Default,
1435 relocs: Vec::new(),
1436 }
1437 }
1438
1439 fn holding(object: Object) -> Vec<u8> {
1441 let data = Data { weak: Vec::new(), objects: vec![object] };
1442 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1443 }
1444
1445 #[test]
1446 fn what_a_variable_is_decides_which_section_it_goes_in() {
1447 for (place, wanted) in [
1448 (Place::Written, ".data"),
1449 (Place::ReadOnly, ".rodata"),
1450 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1451 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1452 (Place::Zero, ".bss"),
1453 (Place::Thread { zero: false }, ".tdata"),
1454 (Place::Thread { zero: true }, ".tbss"),
1455 (Place::Named(".init_array".to_owned()), ".init_array"),
1456 ] {
1457 let bytes = holding(variable("x", place.clone()));
1458 let file = object::File::parse(&bytes[..]).expect("a readable object");
1459 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1460 assert_eq!(section.size(), 4, "{place:?}");
1461 let carried = section.data().expect("the bytes").len();
1464 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1465 }
1466 }
1467
1468 #[test]
1474 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1475 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1476 let bytes = holding(variable("counter", place.clone()));
1477 let file = object::File::parse(&bytes[..]).expect("a readable object");
1478 let symbol = file
1479 .symbols()
1480 .find(|symbol| symbol.name() == Ok("counter"))
1481 .unwrap_or_else(|| panic!("{place:?}"));
1482 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1483 }
1484 }
1485
1486 #[test]
1492 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1493 for (name, wanted) in [
1494 (".init_array", elf::SHT_INIT_ARRAY),
1495 (".init_array.00101", elf::SHT_INIT_ARRAY),
1496 (".fini_array", elf::SHT_FINI_ARRAY),
1497 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1498 (".init_arrays", elf::SHT_PROGBITS),
1499 ] {
1500 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1501 let file = object::File::parse(&bytes[..]).expect("a readable object");
1502 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1503 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1504 panic!("{name} is not an elf section");
1505 };
1506 assert_eq!(sh_type, wanted, "{name}");
1507 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1508 }
1509 }
1510
1511 #[test]
1517 fn two_variables_in_one_named_section_share_it() {
1518 let objects = vec![
1519 variable("x", Place::Named(".init_array".to_owned())),
1520 variable("y", Place::Named(".init_array".to_owned())),
1521 ];
1522 let data = Data { weak: Vec::new(), objects };
1523 let bytes =
1524 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1525 let file = object::File::parse(&bytes[..]).expect("a readable object");
1526 let named: Vec<_> =
1527 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1528 assert_eq!(named.len(), 1);
1529 assert_eq!(named[0].size(), 8);
1530 }
1531
1532 #[test]
1536 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1537 let sections =
1538 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1539 for (place, wanted) in [
1540 (Place::Written, ".data.x"),
1541 (Place::ReadOnly, ".rodata.x"),
1542 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1543 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1544 (Place::Zero, ".bss.x"),
1545 (Place::Thread { zero: false }, ".tdata.x"),
1546 (Place::Thread { zero: true }, ".tbss.x"),
1547 ] {
1548 let data = Data { weak: Vec::new(), objects: vec![variable("x", place.clone())] };
1549 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1550 let file = object::File::parse(&bytes[..]).expect("a readable object");
1551 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1552 let section = file.section_by_name(wanted).expect("the section it named");
1553 assert_eq!(section.size(), 4, "{place:?}");
1554 let carried = section.data().expect("the bytes").len();
1557 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1558 }
1559 }
1560
1561 #[test]
1565 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1566 let sections =
1567 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1568 let named = Place::Named(".init_array".to_owned());
1569 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1570 let bytes =
1571 write(&Text::default(), &Data { weak: Vec::new(), objects }, &[], &target(), sections)
1572 .expect("object");
1573 let file = object::File::parse(&bytes[..]).expect("a readable object");
1574 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1575 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1576 assert_eq!(lives_in(&file, "n"), ".init_array");
1577 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1578 }
1579
1580 #[test]
1584 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1585 let sections =
1586 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1587 let pointer = Object {
1588 bytes: vec![0; 8],
1589 size: 8,
1590 align: 8,
1591 relocs: vec![Reloc {
1592 at: 0,
1593 symbol: "y".to_owned(),
1594 kind: Reference::Address { bytes: 8 },
1595 addend: 0,
1596 after: 0,
1597 }],
1598 ..variable("p", Place::Written)
1599 };
1600 let objects = vec![variable("first", Place::Written), pointer];
1601 let bytes =
1602 write(&Text::default(), &Data { weak: Vec::new(), objects }, &[], &target(), sections)
1603 .expect("object");
1604 let file = object::File::parse(&bytes[..]).expect("a readable object");
1605 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1606 let (offset, reloc) = section.relocations().next().expect("one relocation");
1607 assert_eq!(offset, 0);
1610 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1611 }
1612
1613 #[test]
1621 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1622 let place = Place::RelocReadOnly { local: true };
1623 let data = Data {
1624 weak: Vec::new(),
1625 objects: vec![variable("first", place.clone()), variable("second", place)],
1626 };
1627 let bytes =
1628 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1629 let file = object::File::parse(&bytes[..]).expect("a readable object");
1630 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1631 assert_eq!(named, 1, "one section holding both, not one each");
1632 }
1633
1634 #[test]
1635 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1636 let mut data = Data { weak: Vec::new(), objects: vec![variable("first", Place::Written)] };
1637 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1638 let bytes =
1639 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1640 let file = object::File::parse(&bytes[..]).expect("a readable object");
1641 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1642 assert_eq!(second.kind(), SymbolKind::Data);
1643 assert_eq!(second.size(), 4);
1644 assert_eq!(second.address(), 16);
1648 }
1649
1650 #[test]
1651 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1652 for (binding, global, weak) in [
1653 (Binding::Global, true, false),
1654 (Binding::Local, false, false),
1655 (Binding::Weak, true, true),
1656 ] {
1657 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1658 let file = object::File::parse(&bytes[..]).expect("a readable object");
1659 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1660 assert_eq!(x.is_global(), global, "{binding:?}");
1661 assert_eq!(x.is_weak(), weak, "{binding:?}");
1662 }
1663 }
1664
1665 #[test]
1666 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1667 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1668 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1669 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1670 assert!(x.is_common(), "the linker merges every definition of this name into one");
1671 assert_eq!(x.size(), 4);
1672 assert_eq!(x.address(), 0);
1676 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1677 }
1678
1679 #[test]
1680 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1681 let object = Object {
1682 bytes: vec![0; 8],
1683 size: 8,
1684 align: 8,
1685 relocs: vec![Reloc {
1686 at: 0,
1687 symbol: "y".to_owned(),
1688 kind: Reference::Address { bytes: 8 },
1689 addend: 16,
1690 after: 0,
1691 }],
1692 ..variable("p", Place::Written)
1693 };
1694 let bytes = holding(object);
1695 let file = object::File::parse(&bytes[..]).expect("a readable object");
1696 let section = file.section_by_name(".data").expect("a data section");
1697 let (offset, reloc) = section.relocations().next().expect("one relocation");
1698 assert_eq!(offset, 0);
1699 assert_eq!(reloc.addend(), 16);
1700 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1701 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1702 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1703 }
1704
1705 #[test]
1712 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
1713 let mut text = Text::default();
1714 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
1715 text.bytes.resize(8, 0x90);
1716 text.relocs.push(Reloc {
1717 at: 1,
1718 symbol: "hook".to_owned(),
1719 kind: Reference::Call,
1720 addend: -4,
1721 after: 0,
1722 });
1723 let data =
1724 Data { weak: vec!["hook".to_owned(), "never_called".to_owned()], objects: vec![] };
1725 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
1726 let file = object::File::parse(&bytes[..]).expect("a readable object");
1727
1728 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
1729 assert!(hook.is_undefined(), "nothing here defines it");
1730 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
1731
1732 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
1736 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
1737 }
1738
1739 #[test]
1754 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
1755 let mut text = Text::default();
1756 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
1757 text.bytes.resize(16, 0x90);
1758 text.relocs.push(Reloc {
1759 at: 3,
1760 symbol: "flags".to_owned(),
1761 kind: Reference::Thread,
1762 addend: -4,
1763 after: 0,
1764 });
1765 text.relocs.push(Reloc {
1768 at: 10,
1769 symbol: "shared".to_owned(),
1770 kind: Reference::Got,
1771 addend: -4,
1772 after: 0,
1773 });
1774 let data = Data { weak: Vec::new(), objects: vec![] };
1775 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
1776 let file = object::File::parse(&bytes[..]).expect("a readable object");
1777
1778 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
1779 assert!(flags.is_undefined(), "nothing here defines it");
1780 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
1781
1782 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
1783 assert!(shared.is_undefined(), "nothing here defines this one either");
1784 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
1785 }
1786
1787 #[test]
1789 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1790 let mut data = Data { weak: Vec::new(), objects: vec![variable("first", Place::Written)] };
1791 data.objects.push(Object {
1792 bytes: vec![0; 16],
1793 size: 16,
1794 align: 8,
1795 relocs: vec![Reloc {
1796 at: 8,
1797 symbol: "y".to_owned(),
1798 kind: Reference::Address { bytes: 8 },
1799 addend: 0,
1800 after: 0,
1801 }],
1802 ..variable("second", Place::Written)
1803 });
1804 let bytes =
1805 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1806 let file = object::File::parse(&bytes[..]).expect("a readable object");
1807 let section = file.section_by_name(".data").expect("a data section");
1808 let (offset, _) = section.relocations().next().expect("one relocation");
1809 assert_eq!(offset, 16);
1812 }
1813
1814 #[test]
1815 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1816 let data = Data {
1817 weak: Vec::new(),
1818 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1819 };
1820 let aliases = [Alias {
1821 name: "b".to_owned(),
1822 target: "a".to_owned(),
1823 binding: Binding::Global,
1824 visibility: Visibility::Default,
1825 }];
1826 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1827 .expect("an object");
1828 let file = object::File::parse(&bytes[..]).expect("a readable object");
1829 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1830 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1831 assert_eq!(b.address(), a.address(), "the same place");
1832 assert_eq!(b.size(), a.size());
1833 assert_eq!(b.section_index(), a.section_index());
1834 assert!(a.is_local(), "the target was written `static`");
1837 assert!(b.is_global(), "and the name given to it was not");
1838 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1840 }
1841
1842 #[test]
1843 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1844 let text = calling("puts");
1845 let aliases = [Alias {
1846 name: "g".to_owned(),
1847 target: "f".to_owned(),
1848 binding: Binding::Weak,
1849 visibility: Visibility::Default,
1850 }];
1851 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1852 .expect("an object");
1853 let file = object::File::parse(&bytes[..]).expect("a readable object");
1854 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1855 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1856 assert_eq!(g.address(), f.address());
1857 assert_eq!(g.size(), f.size());
1858 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1859 assert!(g.is_weak(), "so that a program may define the name itself instead");
1860 }
1861
1862 #[test]
1865 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1866 let aliases = [Alias {
1867 name: "b".to_owned(),
1868 target: "a".to_owned(),
1869 binding: Binding::Global,
1870 visibility: Visibility::Default,
1871 }];
1872 let error =
1873 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1874 .expect_err("nothing to point at");
1875 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1876 }
1877
1878 #[test]
1879 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1880 let text = calling("puts");
1881 for triple in [
1882 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1883 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1884 ] {
1885 let error =
1886 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1887 .expect_err("no writer");
1888 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1889 }
1890 }
1891
1892 #[test]
1898 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
1899 let mut text = calling("puts");
1900 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1901 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1902 text.bytes.resize(33, 0x90);
1903 let data = Data {
1904 weak: Vec::new(),
1905 objects: vec![variable("seen", Place::Written), {
1906 let mut quiet = variable("quiet", Place::Zero);
1907 quiet.binding = Binding::Local;
1908 quiet
1909 }],
1910 };
1911 let aliases = [Alias {
1912 name: "second".to_owned(),
1913 target: "f".to_owned(),
1914 binding: Binding::Global,
1915 visibility: Visibility::Default,
1916 }];
1917
1918 let names = defines(&text, &data, &aliases, &target()).expect("a list");
1919 assert_eq!(names, ["f", "shared", "seen", "second"]);
1920
1921 let bytes = write(&text, &data, &aliases, &target(), Output::default()).expect("an object");
1922 let file = object::File::parse(&bytes[..]).expect("a readable object");
1923 let found: Vec<String> = file
1924 .symbols()
1925 .filter(|symbol| symbol.is_global() && symbol.is_definition())
1926 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
1927 .collect();
1928 let mut sorted = names.clone();
1929 sorted.sort();
1930 let mut theirs = found;
1931 theirs.sort();
1932 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
1933 }
1934
1935 fn windows() -> TargetInfo {
1937 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
1938 }
1939
1940 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
1942 let file = object::File::parse(bytes).expect("a readable object");
1943 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
1944 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
1945 }
1946
1947 #[test]
1948 fn a_windows_target_is_written_rather_than_refused() {
1949 let text = calling("puts");
1950 let bytes =
1951 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
1952 let file = object::File::parse(&bytes[..]).expect("a readable object");
1953 assert_eq!(file.format(), BinaryFormat::Coff);
1954 let section = file.section_by_name(".text").expect("a text section");
1955 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1956 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
1957 assert!(names.contains(&"f"), "{names:?}");
1958 assert!(names.contains(&"puts"), "{names:?}");
1959 }
1960
1961 #[test]
1969 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
1970 for (after, typ) in [
1971 (0, pe::IMAGE_REL_AMD64_REL32),
1972 (1, pe::IMAGE_REL_AMD64_REL32_1),
1973 (4, pe::IMAGE_REL_AMD64_REL32_4),
1974 (5, pe::IMAGE_REL_AMD64_REL32_5),
1975 ] {
1976 let mut text = calling("puts");
1977 text.relocs[0].addend = -4 - i64::from(after);
1980 text.relocs[0].after = after;
1981 text.bytes.resize(6 + after as usize, 0x90);
1982 text.funcs[0].len = text.bytes.len();
1983 let bytes = write(&text, &Data::default(), &[], &windows(), Output::default())
1984 .expect("an object");
1985 let file = object::File::parse(&bytes[..]).expect("a readable object");
1986 let section = file.section_by_name(".text").expect("a text section");
1987 let (_, reloc) = section.relocations().next().expect("the relocation");
1988 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
1989 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
1992 }
1993 }
1994
1995 #[test]
1998 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
1999 let mut text = calling("puts");
2000 text.relocs[0].addend = 12;
2001 let bytes =
2002 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
2003 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2004 }
2005
2006 #[test]
2007 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2008 let object = Object {
2009 bytes: vec![0; 8],
2010 size: 8,
2011 align: 8,
2012 relocs: vec![Reloc {
2013 at: 0,
2014 symbol: "y".to_owned(),
2015 kind: Reference::Address { bytes: 8 },
2016 addend: 0,
2017 after: 0,
2018 }],
2019 ..variable("p", Place::Written)
2020 };
2021 let data = Data { weak: Vec::new(), objects: vec![object] };
2022 let bytes =
2023 write(&Text::default(), &data, &[], &windows(), Output::default()).expect("an object");
2024 let file = object::File::parse(&bytes[..]).expect("a readable object");
2025 let section = file.section_by_name(".data").expect("a data section");
2026 let (_, reloc) = section.relocations().next().expect("the relocation");
2027 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2028 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2029 }
2030
2031 #[test]
2034 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2035 for local in [false, true] {
2036 let data = Data {
2037 weak: Vec::new(),
2038 objects: vec![variable("p", Place::RelocReadOnly { local })],
2039 };
2040 let bytes = write(&Text::default(), &data, &[], &windows(), Output::default())
2041 .expect("an object");
2042 let file = object::File::parse(&bytes[..]).expect("a readable object");
2043 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2044 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2045 }
2046 }
2047
2048 #[test]
2051 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2052 let text = calling("puts");
2053 let output = Output { property: Property { features: 3 }, ..Output::default() };
2054 let bytes = write(&text, &Data::default(), &[], &windows(), output).expect("an object");
2055 let file = object::File::parse(&bytes[..]).expect("a readable object");
2056 assert!(file.section_by_name(".note.GNU-stack").is_none());
2057 assert!(file.section_by_name(".note.gnu.property").is_none());
2058 }
2059
2060 #[test]
2065 fn what_this_format_cannot_say_is_refused_by_name() {
2066 let ordinary = Text::default();
2067 let empty = Data::default();
2068
2069 let mut thread = Data::default();
2070 thread.objects.push(variable("t", Place::Thread { zero: false }));
2071
2072 let mut gathered = Data::default();
2073 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2074
2075 let mut table = calling("puts");
2076 table.relocs[0].kind = Reference::Got;
2077
2078 let mut room = calling("puts");
2079 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2080
2081 let cases: [(&str, &Text, &Data); 4] = [
2082 ("thread-local", &ordinary, &thread),
2083 ("startup", &ordinary, &gathered),
2084 ("table", &table, &empty),
2085 ("patcher", &room, &empty),
2086 ];
2087 for (what, text, data) in cases {
2088 let error = write(text, data, &[], &windows(), Output::default())
2089 .expect_err("something this format cannot write");
2090 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2091 }
2092 }
2093
2094 #[test]
2098 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2099 let mut text = calling("puts");
2100 text.funcs[0].visibility = Visibility::Hidden;
2101 let bytes =
2102 write(&text, &Data::default(), &[], &windows(), Output::default()).expect("an object");
2103 let file = object::File::parse(&bytes[..]).expect("a readable object");
2104 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2105 assert!(symbol.is_global(), "a name others may use either way");
2106 }
2107
2108 #[test]
2109 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2110 let text = calling("puts");
2111 let data = Data { weak: Vec::new(), objects: vec![variable("shared", Place::Written)] };
2112 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2113 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2114 }
2115
2116 #[test]
2120 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2121 let text = calling("puts");
2122 for triple in [
2123 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2124 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2125 ] {
2126 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2127 .expect_err("no writer");
2128 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2129 }
2130 }
2131}