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, Info, Object, Output, Place, Property, Reference, Reloc, Sections,
47 Text, 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(
266 text: &Text,
267 data: &Data,
268 aliases: &[Alias],
269 target: &TargetInfo,
270 output: Output,
271 info: &Info,
272) -> Result<Vec<u8>, Error> {
273 let Output { sections, property } = output;
274 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
275 let Some(flavour) = flavour else {
276 return Err(Error::Format { triple: target.tuple.to_string() });
277 };
278 if flavour == Flavour::Coff {
279 beyond(text, data, info)?;
280 }
281 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
282 let whole = obj.section_id(StandardSection::Text);
286 if !sections.functions {
287 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
288 }
289
290 let mut symbols = std::collections::BTreeMap::new();
294 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
299 let mut ordered: Vec<String> = Vec::new();
302 for func in &text.funcs {
303 let ahead = func.patch.map_or(0, |patch| patch.before);
313 let (section, at) = if sections.functions {
314 let name = format!(".text.{}", func.name).into_bytes();
315 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
316 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
317 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
318 (id, ahead as u64)
319 } else {
320 (whole, func.start as u64)
321 };
322 if let Some(patch) = func.patch {
337 let base = if sections.functions { func.start - ahead } else { 0 };
338 let name = elf::PATCHABLE.as_bytes().to_vec();
339 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
340 obj.section_mut(id).flags = elf::ordered();
341 obj.append_section_data(id, &[0; 8], 8);
342 let symbol = obj.section_symbol(section);
343 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
344 Error::Refused { why: "no relocation holds an address here".to_owned() }
345 })?;
346 obj.add_relocation(
347 id,
348 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
349 )
350 .map_err(|why| Error::Refused { why: why.to_string() })?;
351 ordered.push(if sections.functions {
352 format!(".text.{}", func.name)
353 } else {
354 ".text".to_owned()
355 });
356 }
357 let id = obj.add_symbol(Symbol {
358 name: func.name.clone().into_bytes(),
359 value: at,
360 size: func.len as u64,
361 kind: SymbolKind::Text,
362 scope: scope_of(func.binding),
363 weak: func.binding == Binding::Weak,
364 section: SymbolSection::Section(section),
365 flags: SymbolFlags::None,
366 });
367 flavour.see(&mut obj, id, func.binding, func.visibility);
368 symbols.insert(func.name.clone(), id);
369 split.push((section, at));
370 }
371
372 for label in &text.labels {
376 let after = text.funcs.partition_point(|func| func.start <= label.at);
377 let Some(index) = after.checked_sub(1) else {
378 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
379 return Err(Error::Refused { why });
380 };
381 let func = &text.funcs[index];
382 let (section, at) = if sections.functions {
383 let base = func.start - func.patch.map_or(0, |patch| patch.before);
386 (split[index].0, (label.at - base) as u64)
387 } else {
388 (whole, label.at as u64)
389 };
390 let id = obj.add_symbol(Symbol {
391 name: label.name.clone().into_bytes(),
392 value: at,
393 size: 0,
396 kind: SymbolKind::Label,
397 scope: SymbolScope::Compilation,
401 weak: false,
402 section: SymbolSection::Section(section),
403 flags: SymbolFlags::None,
404 });
405 symbols.insert(label.name.clone(), id);
406 }
407
408 let mut placed = Vec::with_capacity(data.objects.len());
413 let mut named = HashMap::new();
417 for object in &data.objects {
418 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
419 let id = obj.add_symbol(Symbol {
420 name: object.name.clone().into_bytes(),
421 value: if object.place == Place::Merged { object.align } else { offset },
424 size: object.size,
425 kind: match object.place {
430 Place::Thread { .. } => SymbolKind::Tls,
431 _ => SymbolKind::Data,
432 },
433 scope: scope_of(object.binding),
434 weak: object.binding == Binding::Weak,
435 section,
436 flags: SymbolFlags::None,
437 });
438 flavour.see(&mut obj, id, object.binding, object.visibility);
439 symbols.insert(object.name.clone(), id);
440 placed.push((section.id(), offset));
441 }
442
443 for alias in aliases {
449 let Some(&id) = symbols.get(&alias.target) else {
450 let why =
451 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
452 return Err(Error::Refused { why });
453 };
454 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
455 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
456 let id = obj.add_symbol(Symbol {
457 name: alias.name.clone().into_bytes(),
458 value,
459 size,
460 kind,
461 scope: scope_of(alias.binding),
462 weak: alias.binding == Binding::Weak,
463 section,
464 flags: SymbolFlags::None,
465 });
466 flavour.see(&mut obj, id, alias.binding, alias.visibility);
467 symbols.insert(alias.name.clone(), id);
468 }
469
470 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
477 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
478 let thread: HashSet<&str> = relocs()
483 .filter(|reloc| reloc.kind == Reference::Thread)
484 .map(|reloc| reloc.symbol.as_str())
485 .collect();
486 let wanted: Vec<&String> =
487 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
488 for name in wanted {
489 if symbols.contains_key(name) {
490 continue;
491 }
492 let id = obj.add_symbol(Symbol {
493 name: name.clone().into_bytes(),
494 value: 0,
495 size: 0,
496 kind: if thread.contains(name.as_str()) {
506 SymbolKind::Tls
507 } else {
508 SymbolKind::Unknown
509 },
510 scope: SymbolScope::Dynamic,
511 weak: weak.contains(name.as_str()),
512 section: SymbolSection::Undefined,
513 flags: SymbolFlags::None,
514 });
515 symbols.insert(name.clone(), id);
516 }
517
518 for reloc in &text.relocs {
519 let (section, at) = if sections.functions {
524 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
525 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
526 let why = format!("a relocation at {} is in front of every function", reloc.at);
527 return Err(Error::Refused { why });
528 };
529 let base = func.start - func.patch.map_or(0, |patch| patch.before);
532 (split[after - 1].0, (reloc.at - base) as u64)
533 } else {
534 (whole, reloc.at as u64)
535 };
536 add(&mut obj, section, at, reloc, &symbols, flavour)?;
537 }
538
539 if !text.unwind.bytes.is_empty() {
543 let ((name, align), second) = flavour.tables();
544 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
545 obj.append_section_data(frames, &text.unwind.bytes, align);
546 let mut described = HashMap::new();
551 if !text.unwind.info.is_empty() {
552 let Some((name, align)) = second else {
553 let why = "an unwind table here is one section and it was given two".to_owned();
554 return Err(Error::Refused { why });
555 };
556 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
557 obj.append_section_data(codes, &text.unwind.info, align);
558 for label in &text.unwind.labels {
559 let id = obj.add_symbol(Symbol {
560 name: label.name.clone().into_bytes(),
561 value: label.at as u64,
562 size: 0,
563 kind: SymbolKind::Label,
564 scope: SymbolScope::Compilation,
565 weak: false,
566 section: SymbolSection::Section(codes),
567 flags: SymbolFlags::None,
568 });
569 described.insert(label.name.clone(), id);
570 }
571 }
572 for reloc in &text.unwind.relocs {
573 let (symbol, addend) = match described.get(&reloc.symbol) {
574 Some(&id) => (id, reloc.addend),
578 None => {
592 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
593 let Some((section, at)) = found.map(|i| split[i]) else {
594 let why = format!(
595 "'{}' has an unwind record and is not a function here",
596 reloc.symbol
597 );
598 return Err(Error::Refused { why });
599 };
600 (obj.section_symbol(section), reloc.addend + at as i64)
604 }
605 };
606 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
607 why: format!("no relocation is {:?}", reloc.kind),
608 })?;
609 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
610 obj.add_relocation(frames, record)
611 .map_err(|why| Error::Refused { why: why.to_string() })?;
612 }
613 }
614 let mut named = HashMap::new();
623 for chunk in &info.chunks {
624 let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
625 obj.append_section_data(id, &chunk.bytes, 1);
626 named.insert(chunk.name.as_str(), id);
627 }
628 for chunk in &info.chunks {
629 let section = named[chunk.name.as_str()];
630 for reloc in &chunk.relocs {
631 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
632 Some(&id) => (obj.section_symbol(id), reloc.addend),
635 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
640 Some(which) => {
641 let (section, at) = split[which];
642 (obj.section_symbol(section), reloc.addend + at as i64)
643 }
644 None => {
650 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
651 let Some(which) = found else {
652 let why = format!(
653 "'{}' is named by the debug information and is not defined here",
654 reloc.symbol
655 );
656 return Err(Error::Refused { why });
657 };
658 match placed[which] {
659 (Some(section), at) => {
660 (obj.section_symbol(section), reloc.addend + at as i64)
661 }
662 (None, _) => (symbols[&reloc.symbol], reloc.addend),
663 }
664 }
665 },
666 };
667 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
668 why: format!("no relocation is {:?}", reloc.kind),
669 })?;
670 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
671 obj.add_relocation(section, record)
672 .map_err(|why| Error::Refused { why: why.to_string() })?;
673 }
674 }
675 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
676 let Some(section) = section else { continue };
677 for reloc in &object.relocs {
678 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
679 }
680 }
681
682 flavour.property(&mut obj, property);
686
687 flavour.marker(&mut obj);
690
691 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
692 flavour.finish(&mut bytes, &ordered);
693 Ok(bytes)
694}
695
696fn beyond(text: &Text, data: &Data, info: &Info) -> Result<(), Error> {
709 let why = |why: String| Err(Error::Refused { why });
710 if !info.chunks.is_empty() {
711 return why("debug information here goes in sections this writer does not name".to_owned());
712 }
713 if text.funcs.iter().any(|func| func.patch.is_some()) {
714 return why("a record of where a patcher's room is has no section flags here".to_owned());
715 }
716 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
717 if matches!(reloc.kind, Reference::Got | Reference::Thread) {
718 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
719 }
720 }
721 for object in &data.objects {
722 if matches!(object.place, Place::Thread { .. }) {
723 return why(format!("'{}' is thread-local and this format is not", object.name));
724 }
725 let Place::Named(name) = &object.place else { continue };
726 if Array::of(name).is_some() {
727 return why(format!("'{name}' is not a list the startup code here gathers"));
728 }
729 }
730 Ok(())
731}
732
733pub fn defines(
757 text: &Text,
758 data: &Data,
759 aliases: &[Alias],
760 target: &TargetInfo,
761) -> Result<Vec<String>, Error> {
762 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
763 return Err(Error::Format { triple: target.tuple.to_string() });
764 }
765 let names = text
766 .funcs
767 .iter()
768 .filter(|func| func.binding != Binding::Local)
769 .map(|func| func.name.clone())
770 .chain(
771 data.objects
772 .iter()
773 .filter(|object| object.binding != Binding::Local)
774 .map(|object| object.name.clone()),
775 )
776 .chain(
777 aliases
778 .iter()
779 .filter(|alias| alias.binding != Binding::Local)
780 .map(|alias| alias.name.clone()),
781 )
782 .collect();
783 Ok(names)
784}
785
786fn put(
793 obj: &mut Writer<'_>,
794 object: &Object,
795 named: &mut HashMap<String, object::write::SectionId>,
796 sections: Sections,
797 flavour: Flavour,
798) -> (SymbolSection, u64) {
799 if sections.data {
805 if let Some(name) = object.place.split(&object.name) {
806 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
807 let offset = if carries_no_bytes(&object.place) {
808 obj.append_section_bss(section, object.size, object.align)
809 } else {
810 obj.append_section_data(section, &object.bytes, object.align)
811 };
812 return (SymbolSection::Section(section), offset);
813 }
814 }
815 let section = match &object.place {
816 Place::Written => obj.section_id(StandardSection::Data),
817 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
818 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
824 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
825 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
826 },
827 Place::Zero => obj.section_id(StandardSection::UninitializedData),
828 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
829 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
830 Place::Merged => return (SymbolSection::Common, 0),
831 Place::Named(name) => {
837 let section = made(obj, named, name, SectionKind::Data);
838 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
839 obj.section_mut(section).flags = flags;
840 }
841 section
842 }
843 };
844 let offset = if carries_no_bytes(&object.place) {
845 obj.append_section_bss(section, object.size, object.align)
846 } else {
847 obj.append_section_data(section, &object.bytes, object.align)
848 };
849 (SymbolSection::Section(section), offset)
850}
851
852fn carries_no_bytes(place: &Place) -> bool {
858 matches!(place, Place::Zero | Place::Thread { zero: true })
859}
860
861fn made(
869 obj: &mut Writer<'_>,
870 named: &mut HashMap<String, object::write::SectionId>,
871 name: &str,
872 kind: SectionKind,
873) -> object::write::SectionId {
874 if let Some(section) = named.get(name) {
875 return *section;
876 }
877 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
878 named.insert(name.to_owned(), section);
879 section
880}
881
882fn kind_of(place: &Place) -> SectionKind {
891 match place {
892 Place::ReadOnly => SectionKind::ReadOnlyData,
893 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
894 Place::Zero => SectionKind::UninitializedData,
895 Place::Thread { zero: false } => SectionKind::Tls,
896 Place::Thread { zero: true } => SectionKind::UninitializedTls,
897 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
898 }
899}
900
901fn add(
908 obj: &mut Writer<'_>,
909 section: object::write::SectionId,
910 at: u64,
911 reloc: &Reloc,
912 symbols: &std::collections::BTreeMap<String, SymbolId>,
913 flavour: Flavour,
914) -> Result<(), Error> {
915 let flags = flavour
916 .reloc(reloc.kind, reloc.after)
917 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
918 obj.add_relocation(
919 section,
920 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
921 )
922 .map_err(|why| Error::Refused { why: why.to_string() })
923}
924
925pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
938 match binding {
939 Binding::Local => SymbolScope::Compilation,
940 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
941 }
942}
943
944#[cfg(test)]
945mod tests {
946 use super::*;
947
948 use object::read::elf::Sym as _;
949 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
950 use object::{elf, pe};
951 use rucc_target::{Arch, Env, Os, Triple};
952
953 use crate::elf::PATCHABLE;
954 use crate::section::{Extent, Patch, Reloc};
955
956 fn target() -> TargetInfo {
958 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
959 }
960
961 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
966 Extent {
967 name,
968 start,
969 len,
970 align: crate::FUNC_ALIGN,
971 binding,
972 visibility: Visibility::Default,
973 patch: None,
974 }
975 }
976
977 fn calling(name: &str) -> Text {
979 Text {
980 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
981 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
982 relocs: vec![Reloc {
983 at: 1,
984 symbol: name.to_owned(),
985 kind: Reference::Call,
986 addend: -4,
987 after: 0,
988 }],
989 ..Text::default()
990 }
991 }
992
993 #[test]
994 fn the_bytes_come_back_out_of_the_section_they_went_into() {
995 let text = calling("puts");
996 let bytes =
997 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
998 .expect("an object");
999 let file = object::File::parse(&bytes[..]).expect("a readable object");
1000 let section = file.section_by_name(".text").expect("a text section");
1001 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1002 }
1003
1004 #[test]
1005 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1006 let mut text = calling("puts");
1007 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1008 text.bytes.resize(17, 0x90);
1009 let bytes =
1010 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1011 .expect("an object");
1012 let file = object::File::parse(&bytes[..]).expect("a readable object");
1013 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1014 assert_eq!(g.address(), 16);
1015 assert_eq!(g.size(), 1);
1016 assert_eq!(g.kind(), SymbolKind::Text);
1017 assert!(g.is_global(), "nothing said otherwise about this one");
1018 }
1019
1020 #[test]
1021 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1022 let mut text = calling("puts");
1023 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1024 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1025 text.bytes.resize(33, 0x90);
1026 let bytes =
1027 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1028 .expect("an object");
1029 let file = object::File::parse(&bytes[..]).expect("a readable object");
1030 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1031 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1034 assert!(!hidden.is_weak());
1035 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1036 assert!(shared.is_weak(), "a weak function has to be able to lose");
1037 assert!(shared.is_global());
1038 }
1039
1040 #[test]
1057 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1058 let mut text = calling("puts");
1059 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1060 text.funcs[0].start = 3;
1061 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1062 text.relocs[0].at = 4;
1063 let bytes =
1064 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1065 .expect("an object");
1066 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1067 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1068 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1069 assert_eq!(section.align(), 8);
1070 let header = section.elf_section_header();
1071 assert_eq!(
1072 header.sh_flags.get(Endianness::Little),
1073 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1074 );
1075 let index = file.section_by_name(".text").expect("a text section").index().0;
1078 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1079 assert_ne!(index, 0);
1080
1081 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1083 panic!("one address in the record")
1084 };
1085 assert_eq!(*at, 0);
1086 assert_eq!(reloc.addend(), 0);
1087 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1088 }
1089
1090 #[test]
1092 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1093 let text = calling("puts");
1094 let bytes =
1095 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1096 .expect("an object");
1097 let file = object::File::parse(&bytes[..]).expect("a readable object");
1098 assert!(file.section_by_name(PATCHABLE).is_none());
1099 }
1100
1101 #[test]
1107 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1108 let mut text = calling("puts");
1109 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1110 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1111 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1112 text.bytes.resize(17, 0x90);
1113 let output =
1114 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1115 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1116 .expect("an object");
1117 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1118 let links: Vec<usize> = file
1119 .sections()
1120 .filter(|section| section.name() == Ok(PATCHABLE))
1121 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1122 .collect();
1123 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1124 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1125 }
1126
1127 #[test]
1128 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1129 let mut text = calling("puts");
1130 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1131 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1132 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1133 text.bytes.resize(49, 0x90);
1134 let bytes =
1135 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1136 .expect("an object");
1137 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1138 let visibility = |name: &str| {
1139 file.symbols()
1140 .find(|s| s.name() == Ok(name))
1141 .expect("the function")
1142 .elf_symbol()
1143 .st_visibility()
1144 };
1145 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1147 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1148 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1151 }
1152
1153 #[test]
1163 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1164 let mut text = calling("puts");
1165 for (index, (name, seen)) in
1166 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1167 {
1168 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1169 func.visibility = seen;
1170 text.funcs.push(func);
1171 }
1172 text.bytes.resize(49, 0x90);
1173 let mut data = Data::default();
1174 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1175 let mut object = variable(name, Place::Written);
1176 object.visibility = seen;
1177 data.objects.push(object);
1178 }
1179 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1180 .expect("an object");
1181 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1182 let visibility = |name: &str| {
1183 file.symbols()
1184 .find(|s| s.name() == Ok(name))
1185 .expect("the symbol")
1186 .elf_symbol()
1187 .st_visibility()
1188 };
1189 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1190 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1191 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1192 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1193 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1196 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1197 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1198 }
1199
1200 #[test]
1201 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1202 let bytes = write(
1203 &calling("puts"),
1204 &Data::default(),
1205 &[],
1206 &target(),
1207 Output::default(),
1208 &Info::default(),
1209 )
1210 .expect("an object");
1211 let file = object::File::parse(&bytes[..]).expect("a readable object");
1212 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1213 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1214 }
1215
1216 #[test]
1217 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1218 for (reference, wanted) in [
1219 (Reference::Call, elf::R_X86_64_PLT32),
1220 (Reference::Data, elf::R_X86_64_PC32),
1221 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1222 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1223 ] {
1224 let mut text = calling("puts");
1225 text.relocs[0].kind = reference;
1226 let bytes =
1227 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1228 .expect("an object");
1229 let file = object::File::parse(&bytes[..]).expect("a readable object");
1230 let section = file.section_by_name(".text").expect("a text section");
1231 let (offset, reloc) = section.relocations().next().expect("one relocation");
1232 assert_eq!(offset, 1);
1233 assert_eq!(reloc.addend(), -4);
1234 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1235 }
1236 }
1237
1238 #[test]
1239 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1240 let mut text = calling("puts");
1241 text.relocs.push(Reloc {
1242 at: 1,
1243 symbol: "puts".to_owned(),
1244 kind: Reference::Call,
1245 addend: -4,
1246 after: 0,
1247 });
1248 let bytes =
1249 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1250 .expect("an object");
1251 let file = object::File::parse(&bytes[..]).expect("a readable object");
1252 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1253 }
1254
1255 #[test]
1256 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1257 let text = calling("f");
1258 let bytes =
1259 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1260 .expect("an object");
1261 let file = object::File::parse(&bytes[..]).expect("a readable object");
1262 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1263 let f = found.next().expect("the function");
1264 assert!(!f.is_undefined(), "the file defines it");
1265 assert!(found.next().is_none(), "and defines it once");
1266 }
1267
1268 #[test]
1269 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1270 let bytes = write(
1271 &calling("puts"),
1272 &Data::default(),
1273 &[],
1274 &target(),
1275 Output::default(),
1276 &Info::default(),
1277 )
1278 .expect("an object");
1279 let file = object::File::parse(&bytes[..]).expect("a readable object");
1280 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1281 assert!(note.data().expect("no bytes").is_empty());
1282 }
1283
1284 #[test]
1291 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1292 let property = Property { features: Property::IBT | Property::SHSTK };
1293 let output = Output { property, ..Output::default() };
1294 let bytes =
1295 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1296 .expect("an object");
1297 let file = object::File::parse(&bytes[..]).expect("a readable object");
1298 let note = file.section_by_name(".note.gnu.property").expect("the note");
1299 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1300 let want: Vec<u8> = [
1301 4u32,
1302 16,
1303 5,
1304 u32::from_le_bytes(*b"GNU\0"),
1305 Property::X86_FEATURES,
1306 4,
1307 Property::IBT | Property::SHSTK,
1308 0,
1309 ]
1310 .iter()
1311 .flat_map(|word| word.to_le_bytes())
1312 .collect();
1313 assert_eq!(note.data().expect("the bytes"), &want[..]);
1314 }
1315
1316 #[test]
1322 fn a_file_built_to_have_nothing_checked_says_nothing() {
1323 let bytes = write(
1324 &calling("puts"),
1325 &Data::default(),
1326 &[],
1327 &target(),
1328 Output::default(),
1329 &Info::default(),
1330 )
1331 .expect("an object");
1332 let file = object::File::parse(&bytes[..]).expect("a readable object");
1333 assert!(file.section_by_name(".note.gnu.property").is_none());
1334 }
1335
1336 #[test]
1345 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1346 let mut text = calling("puts");
1347 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1348 text.bytes.resize(17, 0x90);
1349 text.unwind.bytes = vec![0; 64];
1352 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1353 text.unwind.relocs.push(Reloc {
1354 at,
1355 symbol: name.to_owned(),
1356 kind: Reference::Address { bytes: 8 },
1357 addend: 0,
1358 after: 0,
1359 });
1360 }
1361 let bytes =
1362 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1363 .expect("an object");
1364 let file = object::File::parse(&bytes[..]).expect("a readable object");
1365 let mut found = points_at(&file);
1366 found.sort_unstable();
1367 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1368 }
1369
1370 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1373 let frames = file.section_by_name(".eh_frame").expect("the table");
1374 frames
1375 .relocations()
1376 .map(|(offset, reloc)| {
1377 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1378 panic!("a record points at something that is not a symbol");
1379 };
1380 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1381 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1382 let section = symbol.section_index().expect("a section symbol is in one");
1383 let name = file.section_by_index(section).expect("a readable section");
1384 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1385 })
1386 .collect()
1387 }
1388
1389 #[test]
1402 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1403 let mut text = two();
1404 text.unwind.bytes = vec![0; 64];
1405 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1406 text.unwind.relocs.push(Reloc {
1407 at,
1408 symbol: name.to_owned(),
1409 kind: Reference::Data,
1410 addend: 0,
1411 after: 0,
1412 });
1413 }
1414 let bytes =
1415 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1416 .expect("an object");
1417 let file = object::File::parse(&bytes[..]).expect("a readable object");
1418 let mut whole = points_at(&file);
1419 whole.sort_unstable();
1420 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1421
1422 let sections =
1423 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1424 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1425 .expect("an object");
1426 let file = object::File::parse(&bytes[..]).expect("a readable object");
1427 let mut split = points_at(&file);
1428 split.sort_unstable();
1429 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1430 }
1431
1432 #[test]
1439 fn a_record_about_something_this_file_does_not_define_is_refused() {
1440 let mut text = calling("puts");
1441 text.unwind.bytes = vec![0; 64];
1442 text.unwind.relocs.push(Reloc {
1443 at: 32,
1444 symbol: "puts".to_owned(),
1445 kind: Reference::Data,
1446 addend: 0,
1447 after: 0,
1448 });
1449 let why =
1450 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1451 .expect_err("a record about a name from somewhere else");
1452 assert!(why.to_string().contains("puts"), "{why}");
1453 }
1454
1455 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1457 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1458 let index = symbol.section_index().expect("a section to be defined in");
1459 let section = file.section_by_index(index).expect("a readable section");
1460 section.name().expect("a named section").to_owned()
1461 }
1462
1463 fn two() -> Text {
1465 let mut text = calling("puts");
1466 text.bytes.resize(16, 0x90);
1469 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1470 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1471 text.relocs.push(Reloc {
1472 at: 17,
1473 symbol: "puts".to_owned(),
1474 kind: Reference::Call,
1475 addend: -4,
1476 after: 0,
1477 });
1478 text
1479 }
1480
1481 #[test]
1488 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1489 let sections =
1490 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1491 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1492 .expect("an object");
1493 let file = object::File::parse(&bytes[..]).expect("a readable object");
1494 assert_eq!(lives_in(&file, "f"), ".text.f");
1495 assert_eq!(lives_in(&file, "g"), ".text.g");
1496 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1497 for name in ["f", "g"] {
1500 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1501 assert_eq!(symbol.address(), 0, "{name}");
1502 assert_eq!(symbol.size(), 6, "{name}");
1503 }
1504 let section = file.section_by_name(".text.g").expect("the second function");
1505 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1506 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1509 }
1510
1511 #[test]
1517 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1518 let sections =
1519 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1520 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1521 .expect("an object");
1522 let file = object::File::parse(&bytes[..]).expect("a readable object");
1523 for name in [".text.f", ".text.g"] {
1524 let section = file.section_by_name(name).expect("a function");
1525 let (offset, _) = section.relocations().next().expect("the call in it");
1526 assert_eq!(offset, 1, "{name}");
1529 assert_eq!(section.relocations().count(), 1, "{name}");
1530 }
1531 }
1532
1533 fn variable(name: &str, place: Place) -> Object {
1535 Object {
1536 name: name.to_owned(),
1537 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1538 size: 4,
1539 align: 4,
1540 place,
1541 binding: Binding::Global,
1542 visibility: Visibility::Default,
1543 relocs: Vec::new(),
1544 }
1545 }
1546
1547 fn holding(object: Object) -> Vec<u8> {
1549 let data = Data { weak: Vec::new(), objects: vec![object] };
1550 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1551 .expect("an object")
1552 }
1553
1554 #[test]
1555 fn what_a_variable_is_decides_which_section_it_goes_in() {
1556 for (place, wanted) in [
1557 (Place::Written, ".data"),
1558 (Place::ReadOnly, ".rodata"),
1559 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1560 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1561 (Place::Zero, ".bss"),
1562 (Place::Thread { zero: false }, ".tdata"),
1563 (Place::Thread { zero: true }, ".tbss"),
1564 (Place::Named(".init_array".to_owned()), ".init_array"),
1565 ] {
1566 let bytes = holding(variable("x", place.clone()));
1567 let file = object::File::parse(&bytes[..]).expect("a readable object");
1568 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1569 assert_eq!(section.size(), 4, "{place:?}");
1570 let carried = section.data().expect("the bytes").len();
1573 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1574 }
1575 }
1576
1577 #[test]
1583 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1584 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1585 let bytes = holding(variable("counter", place.clone()));
1586 let file = object::File::parse(&bytes[..]).expect("a readable object");
1587 let symbol = file
1588 .symbols()
1589 .find(|symbol| symbol.name() == Ok("counter"))
1590 .unwrap_or_else(|| panic!("{place:?}"));
1591 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1592 }
1593 }
1594
1595 #[test]
1601 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1602 for (name, wanted) in [
1603 (".init_array", elf::SHT_INIT_ARRAY),
1604 (".init_array.00101", elf::SHT_INIT_ARRAY),
1605 (".fini_array", elf::SHT_FINI_ARRAY),
1606 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1607 (".init_arrays", elf::SHT_PROGBITS),
1608 ] {
1609 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1610 let file = object::File::parse(&bytes[..]).expect("a readable object");
1611 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1612 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1613 panic!("{name} is not an elf section");
1614 };
1615 assert_eq!(sh_type, wanted, "{name}");
1616 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1617 }
1618 }
1619
1620 #[test]
1626 fn two_variables_in_one_named_section_share_it() {
1627 let objects = vec![
1628 variable("x", Place::Named(".init_array".to_owned())),
1629 variable("y", Place::Named(".init_array".to_owned())),
1630 ];
1631 let data = Data { weak: Vec::new(), objects };
1632 let bytes =
1633 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1634 .expect("an object");
1635 let file = object::File::parse(&bytes[..]).expect("a readable object");
1636 let named: Vec<_> =
1637 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1638 assert_eq!(named.len(), 1);
1639 assert_eq!(named[0].size(), 8);
1640 }
1641
1642 #[test]
1646 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1647 let sections =
1648 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1649 for (place, wanted) in [
1650 (Place::Written, ".data.x"),
1651 (Place::ReadOnly, ".rodata.x"),
1652 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1653 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1654 (Place::Zero, ".bss.x"),
1655 (Place::Thread { zero: false }, ".tdata.x"),
1656 (Place::Thread { zero: true }, ".tbss.x"),
1657 ] {
1658 let data = Data { weak: Vec::new(), objects: vec![variable("x", place.clone())] };
1659 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1660 .expect("object");
1661 let file = object::File::parse(&bytes[..]).expect("a readable object");
1662 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1663 let section = file.section_by_name(wanted).expect("the section it named");
1664 assert_eq!(section.size(), 4, "{place:?}");
1665 let carried = section.data().expect("the bytes").len();
1668 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1669 }
1670 }
1671
1672 #[test]
1676 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1677 let sections =
1678 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1679 let named = Place::Named(".init_array".to_owned());
1680 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1681 let bytes = write(
1682 &Text::default(),
1683 &Data { weak: Vec::new(), objects },
1684 &[],
1685 &target(),
1686 sections,
1687 &Info::default(),
1688 )
1689 .expect("object");
1690 let file = object::File::parse(&bytes[..]).expect("a readable object");
1691 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1692 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1693 assert_eq!(lives_in(&file, "n"), ".init_array");
1694 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1695 }
1696
1697 #[test]
1701 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1702 let sections =
1703 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1704 let pointer = Object {
1705 bytes: vec![0; 8],
1706 size: 8,
1707 align: 8,
1708 relocs: vec![Reloc {
1709 at: 0,
1710 symbol: "y".to_owned(),
1711 kind: Reference::Address { bytes: 8 },
1712 addend: 0,
1713 after: 0,
1714 }],
1715 ..variable("p", Place::Written)
1716 };
1717 let objects = vec![variable("first", Place::Written), pointer];
1718 let bytes = write(
1719 &Text::default(),
1720 &Data { weak: Vec::new(), objects },
1721 &[],
1722 &target(),
1723 sections,
1724 &Info::default(),
1725 )
1726 .expect("object");
1727 let file = object::File::parse(&bytes[..]).expect("a readable object");
1728 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1729 let (offset, reloc) = section.relocations().next().expect("one relocation");
1730 assert_eq!(offset, 0);
1733 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1734 }
1735
1736 #[test]
1744 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1745 let place = Place::RelocReadOnly { local: true };
1746 let data = Data {
1747 weak: Vec::new(),
1748 objects: vec![variable("first", place.clone()), variable("second", place)],
1749 };
1750 let bytes =
1751 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1752 .expect("an object");
1753 let file = object::File::parse(&bytes[..]).expect("a readable object");
1754 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1755 assert_eq!(named, 1, "one section holding both, not one each");
1756 }
1757
1758 #[test]
1759 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1760 let mut data = Data { weak: Vec::new(), objects: vec![variable("first", Place::Written)] };
1761 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1762 let bytes =
1763 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1764 .expect("an object");
1765 let file = object::File::parse(&bytes[..]).expect("a readable object");
1766 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1767 assert_eq!(second.kind(), SymbolKind::Data);
1768 assert_eq!(second.size(), 4);
1769 assert_eq!(second.address(), 16);
1773 }
1774
1775 #[test]
1776 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1777 for (binding, global, weak) in [
1778 (Binding::Global, true, false),
1779 (Binding::Local, false, false),
1780 (Binding::Weak, true, true),
1781 ] {
1782 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1783 let file = object::File::parse(&bytes[..]).expect("a readable object");
1784 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1785 assert_eq!(x.is_global(), global, "{binding:?}");
1786 assert_eq!(x.is_weak(), weak, "{binding:?}");
1787 }
1788 }
1789
1790 #[test]
1791 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1792 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1793 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1794 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1795 assert!(x.is_common(), "the linker merges every definition of this name into one");
1796 assert_eq!(x.size(), 4);
1797 assert_eq!(x.address(), 0);
1801 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1802 }
1803
1804 #[test]
1805 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1806 let object = Object {
1807 bytes: vec![0; 8],
1808 size: 8,
1809 align: 8,
1810 relocs: vec![Reloc {
1811 at: 0,
1812 symbol: "y".to_owned(),
1813 kind: Reference::Address { bytes: 8 },
1814 addend: 16,
1815 after: 0,
1816 }],
1817 ..variable("p", Place::Written)
1818 };
1819 let bytes = holding(object);
1820 let file = object::File::parse(&bytes[..]).expect("a readable object");
1821 let section = file.section_by_name(".data").expect("a data section");
1822 let (offset, reloc) = section.relocations().next().expect("one relocation");
1823 assert_eq!(offset, 0);
1824 assert_eq!(reloc.addend(), 16);
1825 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1826 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1827 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1828 }
1829
1830 #[test]
1837 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
1838 let mut text = Text::default();
1839 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
1840 text.bytes.resize(8, 0x90);
1841 text.relocs.push(Reloc {
1842 at: 1,
1843 symbol: "hook".to_owned(),
1844 kind: Reference::Call,
1845 addend: -4,
1846 after: 0,
1847 });
1848 let data =
1849 Data { weak: vec!["hook".to_owned(), "never_called".to_owned()], objects: vec![] };
1850 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1851 .expect("an object");
1852 let file = object::File::parse(&bytes[..]).expect("a readable object");
1853
1854 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
1855 assert!(hook.is_undefined(), "nothing here defines it");
1856 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
1857
1858 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
1862 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
1863 }
1864
1865 #[test]
1880 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
1881 let mut text = Text::default();
1882 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
1883 text.bytes.resize(16, 0x90);
1884 text.relocs.push(Reloc {
1885 at: 3,
1886 symbol: "flags".to_owned(),
1887 kind: Reference::Thread,
1888 addend: -4,
1889 after: 0,
1890 });
1891 text.relocs.push(Reloc {
1894 at: 10,
1895 symbol: "shared".to_owned(),
1896 kind: Reference::Got,
1897 addend: -4,
1898 after: 0,
1899 });
1900 let data = Data { weak: Vec::new(), objects: vec![] };
1901 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1902 .expect("an object");
1903 let file = object::File::parse(&bytes[..]).expect("a readable object");
1904
1905 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
1906 assert!(flags.is_undefined(), "nothing here defines it");
1907 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
1908
1909 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
1910 assert!(shared.is_undefined(), "nothing here defines this one either");
1911 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
1912 }
1913
1914 #[test]
1916 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1917 let mut data = Data { weak: Vec::new(), objects: vec![variable("first", Place::Written)] };
1918 data.objects.push(Object {
1919 bytes: vec![0; 16],
1920 size: 16,
1921 align: 8,
1922 relocs: vec![Reloc {
1923 at: 8,
1924 symbol: "y".to_owned(),
1925 kind: Reference::Address { bytes: 8 },
1926 addend: 0,
1927 after: 0,
1928 }],
1929 ..variable("second", Place::Written)
1930 });
1931 let bytes =
1932 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1933 .expect("an object");
1934 let file = object::File::parse(&bytes[..]).expect("a readable object");
1935 let section = file.section_by_name(".data").expect("a data section");
1936 let (offset, _) = section.relocations().next().expect("one relocation");
1937 assert_eq!(offset, 16);
1940 }
1941
1942 #[test]
1943 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1944 let data = Data {
1945 weak: Vec::new(),
1946 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1947 };
1948 let aliases = [Alias {
1949 name: "b".to_owned(),
1950 target: "a".to_owned(),
1951 binding: Binding::Global,
1952 visibility: Visibility::Default,
1953 }];
1954 let bytes = write(
1955 &Text::default(),
1956 &data,
1957 &aliases,
1958 &target(),
1959 Output::default(),
1960 &Info::default(),
1961 )
1962 .expect("an object");
1963 let file = object::File::parse(&bytes[..]).expect("a readable object");
1964 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1965 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1966 assert_eq!(b.address(), a.address(), "the same place");
1967 assert_eq!(b.size(), a.size());
1968 assert_eq!(b.section_index(), a.section_index());
1969 assert!(a.is_local(), "the target was written `static`");
1972 assert!(b.is_global(), "and the name given to it was not");
1973 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1975 }
1976
1977 #[test]
1978 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1979 let text = calling("puts");
1980 let aliases = [Alias {
1981 name: "g".to_owned(),
1982 target: "f".to_owned(),
1983 binding: Binding::Weak,
1984 visibility: Visibility::Default,
1985 }];
1986 let bytes = write(
1987 &text,
1988 &Data::default(),
1989 &aliases,
1990 &target(),
1991 Output::default(),
1992 &Info::default(),
1993 )
1994 .expect("an object");
1995 let file = object::File::parse(&bytes[..]).expect("a readable object");
1996 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1997 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1998 assert_eq!(g.address(), f.address());
1999 assert_eq!(g.size(), f.size());
2000 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2001 assert!(g.is_weak(), "so that a program may define the name itself instead");
2002 }
2003
2004 #[test]
2007 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2008 let aliases = [Alias {
2009 name: "b".to_owned(),
2010 target: "a".to_owned(),
2011 binding: Binding::Global,
2012 visibility: Visibility::Default,
2013 }];
2014 let error = write(
2015 &Text::default(),
2016 &Data::default(),
2017 &aliases,
2018 &target(),
2019 Output::default(),
2020 &Info::default(),
2021 )
2022 .expect_err("nothing to point at");
2023 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2024 }
2025
2026 #[test]
2027 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2028 let text = calling("puts");
2029 for triple in [
2030 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2031 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2032 ] {
2033 let error = write(
2034 &text,
2035 &Data::default(),
2036 &[],
2037 &TargetInfo::new(triple),
2038 Output::default(),
2039 &Info::default(),
2040 )
2041 .expect_err("no writer");
2042 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2043 }
2044 }
2045
2046 #[test]
2052 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2053 let mut text = calling("puts");
2054 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2055 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2056 text.bytes.resize(33, 0x90);
2057 let data = Data {
2058 weak: Vec::new(),
2059 objects: vec![variable("seen", Place::Written), {
2060 let mut quiet = variable("quiet", Place::Zero);
2061 quiet.binding = Binding::Local;
2062 quiet
2063 }],
2064 };
2065 let aliases = [Alias {
2066 name: "second".to_owned(),
2067 target: "f".to_owned(),
2068 binding: Binding::Global,
2069 visibility: Visibility::Default,
2070 }];
2071
2072 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2073 assert_eq!(names, ["f", "shared", "seen", "second"]);
2074
2075 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2076 .expect("an object");
2077 let file = object::File::parse(&bytes[..]).expect("a readable object");
2078 let found: Vec<String> = file
2079 .symbols()
2080 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2081 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2082 .collect();
2083 let mut sorted = names.clone();
2084 sorted.sort();
2085 let mut theirs = found;
2086 theirs.sort();
2087 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2088 }
2089
2090 fn windows() -> TargetInfo {
2092 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2093 }
2094
2095 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2097 let file = object::File::parse(bytes).expect("a readable object");
2098 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2099 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2100 }
2101
2102 #[test]
2103 fn a_windows_target_is_written_rather_than_refused() {
2104 let text = calling("puts");
2105 let bytes =
2106 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2107 .expect("an object");
2108 let file = object::File::parse(&bytes[..]).expect("a readable object");
2109 assert_eq!(file.format(), BinaryFormat::Coff);
2110 let section = file.section_by_name(".text").expect("a text section");
2111 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2112 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2113 assert!(names.contains(&"f"), "{names:?}");
2114 assert!(names.contains(&"puts"), "{names:?}");
2115 }
2116
2117 #[test]
2125 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2126 for (after, typ) in [
2127 (0, pe::IMAGE_REL_AMD64_REL32),
2128 (1, pe::IMAGE_REL_AMD64_REL32_1),
2129 (4, pe::IMAGE_REL_AMD64_REL32_4),
2130 (5, pe::IMAGE_REL_AMD64_REL32_5),
2131 ] {
2132 let mut text = calling("puts");
2133 text.relocs[0].addend = -4 - i64::from(after);
2136 text.relocs[0].after = after;
2137 text.bytes.resize(6 + after as usize, 0x90);
2138 text.funcs[0].len = text.bytes.len();
2139 let bytes = write(
2140 &text,
2141 &Data::default(),
2142 &[],
2143 &windows(),
2144 Output::default(),
2145 &Info::default(),
2146 )
2147 .expect("an object");
2148 let file = object::File::parse(&bytes[..]).expect("a readable object");
2149 let section = file.section_by_name(".text").expect("a text section");
2150 let (_, reloc) = section.relocations().next().expect("the relocation");
2151 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2152 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2155 }
2156 }
2157
2158 #[test]
2161 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2162 let mut text = calling("puts");
2163 text.relocs[0].addend = 12;
2164 let bytes =
2165 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2166 .expect("an object");
2167 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2168 }
2169
2170 #[test]
2171 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2172 let object = Object {
2173 bytes: vec![0; 8],
2174 size: 8,
2175 align: 8,
2176 relocs: vec![Reloc {
2177 at: 0,
2178 symbol: "y".to_owned(),
2179 kind: Reference::Address { bytes: 8 },
2180 addend: 0,
2181 after: 0,
2182 }],
2183 ..variable("p", Place::Written)
2184 };
2185 let data = Data { weak: Vec::new(), objects: vec![object] };
2186 let bytes =
2187 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2188 .expect("an object");
2189 let file = object::File::parse(&bytes[..]).expect("a readable object");
2190 let section = file.section_by_name(".data").expect("a data section");
2191 let (_, reloc) = section.relocations().next().expect("the relocation");
2192 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2193 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2194 }
2195
2196 #[test]
2199 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2200 for local in [false, true] {
2201 let data = Data {
2202 weak: Vec::new(),
2203 objects: vec![variable("p", Place::RelocReadOnly { local })],
2204 };
2205 let bytes = write(
2206 &Text::default(),
2207 &data,
2208 &[],
2209 &windows(),
2210 Output::default(),
2211 &Info::default(),
2212 )
2213 .expect("an object");
2214 let file = object::File::parse(&bytes[..]).expect("a readable object");
2215 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2216 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2217 }
2218 }
2219
2220 #[test]
2223 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2224 let text = calling("puts");
2225 let output = Output { property: Property { features: 3 }, ..Output::default() };
2226 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2227 .expect("an object");
2228 let file = object::File::parse(&bytes[..]).expect("a readable object");
2229 assert!(file.section_by_name(".note.GNU-stack").is_none());
2230 assert!(file.section_by_name(".note.gnu.property").is_none());
2231 }
2232
2233 #[test]
2238 fn what_this_format_cannot_say_is_refused_by_name() {
2239 let ordinary = Text::default();
2240 let empty = Data::default();
2241
2242 let mut thread = Data::default();
2243 thread.objects.push(variable("t", Place::Thread { zero: false }));
2244
2245 let mut gathered = Data::default();
2246 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2247
2248 let mut table = calling("puts");
2249 table.relocs[0].kind = Reference::Got;
2250
2251 let mut room = calling("puts");
2252 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2253
2254 let cases: [(&str, &Text, &Data); 4] = [
2255 ("thread-local", &ordinary, &thread),
2256 ("startup", &ordinary, &gathered),
2257 ("table", &table, &empty),
2258 ("patcher", &room, &empty),
2259 ];
2260 for (what, text, data) in cases {
2261 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2262 .expect_err("something this format cannot write");
2263 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2264 }
2265 }
2266
2267 #[test]
2271 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2272 let mut text = calling("puts");
2273 text.funcs[0].visibility = Visibility::Hidden;
2274 let bytes =
2275 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2276 .expect("an object");
2277 let file = object::File::parse(&bytes[..]).expect("a readable object");
2278 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2279 assert!(symbol.is_global(), "a name others may use either way");
2280 }
2281
2282 #[test]
2283 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2284 let text = calling("puts");
2285 let data = Data { weak: Vec::new(), objects: vec![variable("shared", Place::Written)] };
2286 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2287 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2288 }
2289
2290 #[test]
2294 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2295 let text = calling("puts");
2296 for triple in [
2297 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2298 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2299 ] {
2300 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2301 .expect_err("no writer");
2302 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2303 }
2304 }
2305}