1use std::collections::{BTreeMap, 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, Apart, Array, Binding, Data, Info, Object, Output, Place, Property, Reference, Reloc,
47 Sections, 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 = 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 apart in &data.apart {
447 let (Some(section), offset) = placed[apart.object] else { continue };
448 let value = distance(&obj, &symbols, apart)?;
449 let bytes = usize::from(apart.bytes);
450 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
451 let at = at + apart.at;
452 let image = obj.section_mut(section).data_mut();
453 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
454 }
455
456 for alias in aliases {
462 let Some(&id) = symbols.get(&alias.target) else {
463 let why =
464 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
465 return Err(Error::Refused { why });
466 };
467 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
468 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
469 let id = obj.add_symbol(Symbol {
470 name: alias.name.clone().into_bytes(),
471 value,
472 size,
473 kind,
474 scope: scope_of(alias.binding),
475 weak: alias.binding == Binding::Weak,
476 section,
477 flags: SymbolFlags::None,
478 });
479 flavour.see(&mut obj, id, alias.binding, alias.visibility);
480 symbols.insert(alias.name.clone(), id);
481 }
482
483 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
490 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
491 let thread: HashSet<&str> = relocs()
496 .filter(|reloc| reloc.kind == Reference::Thread)
497 .map(|reloc| reloc.symbol.as_str())
498 .collect();
499 let wanted: Vec<&String> =
500 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
501 for name in wanted {
502 if symbols.contains_key(name) {
503 continue;
504 }
505 let id = obj.add_symbol(Symbol {
506 name: name.clone().into_bytes(),
507 value: 0,
508 size: 0,
509 kind: if thread.contains(name.as_str()) {
519 SymbolKind::Tls
520 } else {
521 SymbolKind::Unknown
522 },
523 scope: SymbolScope::Dynamic,
524 weak: weak.contains(name.as_str()),
525 section: SymbolSection::Undefined,
526 flags: SymbolFlags::None,
527 });
528 symbols.insert(name.clone(), id);
529 }
530
531 for reloc in &text.relocs {
532 let (section, at) = if sections.functions {
537 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
538 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
539 let why = format!("a relocation at {} is in front of every function", reloc.at);
540 return Err(Error::Refused { why });
541 };
542 let base = func.start - func.patch.map_or(0, |patch| patch.before);
545 (split[after - 1].0, (reloc.at - base) as u64)
546 } else {
547 (whole, reloc.at as u64)
548 };
549 add(&mut obj, section, at, reloc, &symbols, flavour)?;
550 }
551
552 if !text.unwind.bytes.is_empty() {
556 let ((name, align), second) = flavour.tables();
557 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
558 obj.append_section_data(frames, &text.unwind.bytes, align);
559 let mut described = HashMap::new();
564 if !text.unwind.info.is_empty() {
565 let Some((name, align)) = second else {
566 let why = "an unwind table here is one section and it was given two".to_owned();
567 return Err(Error::Refused { why });
568 };
569 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
570 obj.append_section_data(codes, &text.unwind.info, align);
571 for label in &text.unwind.labels {
572 let id = obj.add_symbol(Symbol {
573 name: label.name.clone().into_bytes(),
574 value: label.at as u64,
575 size: 0,
576 kind: SymbolKind::Label,
577 scope: SymbolScope::Compilation,
578 weak: false,
579 section: SymbolSection::Section(codes),
580 flags: SymbolFlags::None,
581 });
582 described.insert(label.name.clone(), id);
583 }
584 }
585 for reloc in &text.unwind.relocs {
586 let (symbol, addend) = match described.get(&reloc.symbol) {
587 Some(&id) => (id, reloc.addend),
591 None => {
605 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
606 let Some((section, at)) = found.map(|i| split[i]) else {
607 let why = format!(
608 "'{}' has an unwind record and is not a function here",
609 reloc.symbol
610 );
611 return Err(Error::Refused { why });
612 };
613 (obj.section_symbol(section), reloc.addend + at as i64)
617 }
618 };
619 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
620 why: format!("no relocation is {:?}", reloc.kind),
621 })?;
622 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
623 obj.add_relocation(frames, record)
624 .map_err(|why| Error::Refused { why: why.to_string() })?;
625 }
626 }
627 let mut named = HashMap::new();
636 for chunk in &info.chunks {
637 let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
638 obj.append_section_data(id, &chunk.bytes, 1);
639 named.insert(chunk.name.as_str(), id);
640 }
641 for chunk in &info.chunks {
642 let section = named[chunk.name.as_str()];
643 for reloc in &chunk.relocs {
644 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
645 Some(&id) => (obj.section_symbol(id), reloc.addend),
648 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
653 Some(which) => {
654 let (section, at) = split[which];
655 (obj.section_symbol(section), reloc.addend + at as i64)
656 }
657 None => {
663 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
664 let Some(which) = found else {
665 let why = format!(
666 "'{}' is named by the debug information and is not defined here",
667 reloc.symbol
668 );
669 return Err(Error::Refused { why });
670 };
671 match placed[which] {
672 (Some(section), at) => {
673 (obj.section_symbol(section), reloc.addend + at as i64)
674 }
675 (None, _) => (symbols[&reloc.symbol], reloc.addend),
676 }
677 }
678 },
679 };
680 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
681 why: format!("no relocation is {:?}", reloc.kind),
682 })?;
683 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
684 obj.add_relocation(section, record)
685 .map_err(|why| Error::Refused { why: why.to_string() })?;
686 }
687 }
688 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
689 let Some(section) = section else { continue };
690 for reloc in &object.relocs {
691 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
692 }
693 }
694
695 flavour.property(&mut obj, property);
699
700 flavour.marker(&mut obj);
703
704 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
705 flavour.finish(&mut bytes, &ordered);
706 Ok(bytes)
707}
708
709fn distance(
716 obj: &Writer<'_>,
717 symbols: &BTreeMap<String, SymbolId>,
718 apart: &Apart,
719) -> Result<i64, Error> {
720 let find = |name: &str| match symbols.get(name) {
721 Some(&id) => Ok(obj.symbol(id)),
722 None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
723 };
724 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
725 if to.section != from.section {
726 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
727 return Err(Error::Refused { why });
728 }
729 let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
730 let bits = u32::from(apart.bytes) * 8;
731 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
732 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
733 return Err(Error::Refused { why });
734 }
735 Ok(value)
736}
737
738fn beyond(text: &Text, data: &Data, info: &Info) -> Result<(), Error> {
751 let why = |why: String| Err(Error::Refused { why });
752 if !info.chunks.is_empty() {
753 return why("debug information here goes in sections this writer does not name".to_owned());
754 }
755 if text.funcs.iter().any(|func| func.patch.is_some()) {
756 return why("a record of where a patcher's room is has no section flags here".to_owned());
757 }
758 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
759 if matches!(reloc.kind, Reference::Got | Reference::Thread) {
760 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
761 }
762 }
763 for object in &data.objects {
764 if matches!(object.place, Place::Thread { .. }) {
765 return why(format!("'{}' is thread-local and this format is not", object.name));
766 }
767 let Place::Named(name) = &object.place else { continue };
768 if Array::of(name).is_some() {
769 return why(format!("'{name}' is not a list the startup code here gathers"));
770 }
771 }
772 Ok(())
773}
774
775pub fn defines(
799 text: &Text,
800 data: &Data,
801 aliases: &[Alias],
802 target: &TargetInfo,
803) -> Result<Vec<String>, Error> {
804 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
805 return Err(Error::Format { triple: target.tuple.to_string() });
806 }
807 let names = text
808 .funcs
809 .iter()
810 .filter(|func| func.binding != Binding::Local)
811 .map(|func| func.name.clone())
812 .chain(
813 data.objects
814 .iter()
815 .filter(|object| object.binding != Binding::Local)
816 .map(|object| object.name.clone()),
817 )
818 .chain(
819 aliases
820 .iter()
821 .filter(|alias| alias.binding != Binding::Local)
822 .map(|alias| alias.name.clone()),
823 )
824 .collect();
825 Ok(names)
826}
827
828fn put(
835 obj: &mut Writer<'_>,
836 object: &Object,
837 named: &mut HashMap<String, object::write::SectionId>,
838 sections: Sections,
839 flavour: Flavour,
840) -> (SymbolSection, u64) {
841 if sections.data {
847 if let Some(name) = object.place.split(&object.name) {
848 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
849 let offset = if carries_no_bytes(&object.place) {
850 obj.append_section_bss(section, object.size, object.align)
851 } else {
852 obj.append_section_data(section, &object.bytes, object.align)
853 };
854 return (SymbolSection::Section(section), offset);
855 }
856 }
857 let section = match &object.place {
858 Place::Written => obj.section_id(StandardSection::Data),
859 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
860 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
866 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
867 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
868 },
869 Place::Zero => obj.section_id(StandardSection::UninitializedData),
870 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
871 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
872 Place::Merged => return (SymbolSection::Common, 0),
873 Place::Named(name) => {
879 let section = made(obj, named, name, SectionKind::Data);
880 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
881 obj.section_mut(section).flags = flags;
882 }
883 section
884 }
885 };
886 let offset = if carries_no_bytes(&object.place) {
887 obj.append_section_bss(section, object.size, object.align)
888 } else {
889 obj.append_section_data(section, &object.bytes, object.align)
890 };
891 (SymbolSection::Section(section), offset)
892}
893
894fn carries_no_bytes(place: &Place) -> bool {
900 matches!(place, Place::Zero | Place::Thread { zero: true })
901}
902
903fn made(
911 obj: &mut Writer<'_>,
912 named: &mut HashMap<String, object::write::SectionId>,
913 name: &str,
914 kind: SectionKind,
915) -> object::write::SectionId {
916 if let Some(section) = named.get(name) {
917 return *section;
918 }
919 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
920 named.insert(name.to_owned(), section);
921 section
922}
923
924fn kind_of(place: &Place) -> SectionKind {
933 match place {
934 Place::ReadOnly => SectionKind::ReadOnlyData,
935 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
936 Place::Zero => SectionKind::UninitializedData,
937 Place::Thread { zero: false } => SectionKind::Tls,
938 Place::Thread { zero: true } => SectionKind::UninitializedTls,
939 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
940 }
941}
942
943fn add(
950 obj: &mut Writer<'_>,
951 section: object::write::SectionId,
952 at: u64,
953 reloc: &Reloc,
954 symbols: &BTreeMap<String, SymbolId>,
955 flavour: Flavour,
956) -> Result<(), Error> {
957 let flags = flavour
958 .reloc(reloc.kind, reloc.after)
959 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
960 obj.add_relocation(
961 section,
962 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
963 )
964 .map_err(|why| Error::Refused { why: why.to_string() })
965}
966
967pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
980 match binding {
981 Binding::Local => SymbolScope::Compilation,
982 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
983 }
984}
985
986#[cfg(test)]
987mod tests {
988 use super::*;
989
990 use object::read::elf::Sym as _;
991 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
992 use object::{elf, pe};
993 use rucc_target::{Arch, Env, Os, Triple};
994
995 use crate::elf::PATCHABLE;
996 use crate::section::{Extent, Marker, Patch, Reloc};
997
998 fn target() -> TargetInfo {
1000 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1001 }
1002
1003 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1008 Extent {
1009 name,
1010 start,
1011 len,
1012 align: crate::FUNC_ALIGN,
1013 binding,
1014 visibility: Visibility::Default,
1015 patch: None,
1016 }
1017 }
1018
1019 fn calling(name: &str) -> Text {
1021 Text {
1022 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1023 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1024 relocs: vec![Reloc {
1025 at: 1,
1026 symbol: name.to_owned(),
1027 kind: Reference::Call,
1028 addend: -4,
1029 after: 0,
1030 }],
1031 ..Text::default()
1032 }
1033 }
1034
1035 #[test]
1036 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1037 let text = calling("puts");
1038 let bytes =
1039 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1040 .expect("an object");
1041 let file = object::File::parse(&bytes[..]).expect("a readable object");
1042 let section = file.section_by_name(".text").expect("a text section");
1043 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1044 }
1045
1046 #[test]
1047 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1048 let mut text = calling("puts");
1049 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1050 text.bytes.resize(17, 0x90);
1051 let bytes =
1052 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1053 .expect("an object");
1054 let file = object::File::parse(&bytes[..]).expect("a readable object");
1055 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1056 assert_eq!(g.address(), 16);
1057 assert_eq!(g.size(), 1);
1058 assert_eq!(g.kind(), SymbolKind::Text);
1059 assert!(g.is_global(), "nothing said otherwise about this one");
1060 }
1061
1062 #[test]
1063 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1064 let mut text = calling("puts");
1065 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1066 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1067 text.bytes.resize(33, 0x90);
1068 let bytes =
1069 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1070 .expect("an object");
1071 let file = object::File::parse(&bytes[..]).expect("a readable object");
1072 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1073 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1076 assert!(!hidden.is_weak());
1077 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1078 assert!(shared.is_weak(), "a weak function has to be able to lose");
1079 assert!(shared.is_global());
1080 }
1081
1082 #[test]
1099 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1100 let mut text = calling("puts");
1101 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1102 text.funcs[0].start = 3;
1103 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1104 text.relocs[0].at = 4;
1105 let bytes =
1106 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1107 .expect("an object");
1108 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1109 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1110 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1111 assert_eq!(section.align(), 8);
1112 let header = section.elf_section_header();
1113 assert_eq!(
1114 header.sh_flags.get(Endianness::Little),
1115 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1116 );
1117 let index = file.section_by_name(".text").expect("a text section").index().0;
1120 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1121 assert_ne!(index, 0);
1122
1123 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1125 panic!("one address in the record")
1126 };
1127 assert_eq!(*at, 0);
1128 assert_eq!(reloc.addend(), 0);
1129 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1130 }
1131
1132 #[test]
1134 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1135 let text = calling("puts");
1136 let bytes =
1137 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1138 .expect("an object");
1139 let file = object::File::parse(&bytes[..]).expect("a readable object");
1140 assert!(file.section_by_name(PATCHABLE).is_none());
1141 }
1142
1143 #[test]
1149 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1150 let mut text = calling("puts");
1151 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1152 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1153 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1154 text.bytes.resize(17, 0x90);
1155 let output =
1156 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1157 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1158 .expect("an object");
1159 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1160 let links: Vec<usize> = file
1161 .sections()
1162 .filter(|section| section.name() == Ok(PATCHABLE))
1163 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1164 .collect();
1165 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1166 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1167 }
1168
1169 #[test]
1170 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1171 let mut text = calling("puts");
1172 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1173 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1174 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1175 text.bytes.resize(49, 0x90);
1176 let bytes =
1177 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1178 .expect("an object");
1179 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1180 let visibility = |name: &str| {
1181 file.symbols()
1182 .find(|s| s.name() == Ok(name))
1183 .expect("the function")
1184 .elf_symbol()
1185 .st_visibility()
1186 };
1187 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1189 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1190 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1193 }
1194
1195 #[test]
1205 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1206 let mut text = calling("puts");
1207 for (index, (name, seen)) in
1208 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1209 {
1210 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1211 func.visibility = seen;
1212 text.funcs.push(func);
1213 }
1214 text.bytes.resize(49, 0x90);
1215 let mut data = Data::default();
1216 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1217 let mut object = variable(name, Place::Written);
1218 object.visibility = seen;
1219 data.objects.push(object);
1220 }
1221 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1222 .expect("an object");
1223 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1224 let visibility = |name: &str| {
1225 file.symbols()
1226 .find(|s| s.name() == Ok(name))
1227 .expect("the symbol")
1228 .elf_symbol()
1229 .st_visibility()
1230 };
1231 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1232 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1233 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1234 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1235 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1238 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1239 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1240 }
1241
1242 #[test]
1243 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1244 let bytes = write(
1245 &calling("puts"),
1246 &Data::default(),
1247 &[],
1248 &target(),
1249 Output::default(),
1250 &Info::default(),
1251 )
1252 .expect("an object");
1253 let file = object::File::parse(&bytes[..]).expect("a readable object");
1254 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1255 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1256 }
1257
1258 #[test]
1259 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1260 for (reference, wanted) in [
1261 (Reference::Call, elf::R_X86_64_PLT32),
1262 (Reference::Data, elf::R_X86_64_PC32),
1263 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1264 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1265 ] {
1266 let mut text = calling("puts");
1267 text.relocs[0].kind = reference;
1268 let bytes =
1269 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1270 .expect("an object");
1271 let file = object::File::parse(&bytes[..]).expect("a readable object");
1272 let section = file.section_by_name(".text").expect("a text section");
1273 let (offset, reloc) = section.relocations().next().expect("one relocation");
1274 assert_eq!(offset, 1);
1275 assert_eq!(reloc.addend(), -4);
1276 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1277 }
1278 }
1279
1280 #[test]
1281 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1282 let mut text = calling("puts");
1283 text.relocs.push(Reloc {
1284 at: 1,
1285 symbol: "puts".to_owned(),
1286 kind: Reference::Call,
1287 addend: -4,
1288 after: 0,
1289 });
1290 let bytes =
1291 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1292 .expect("an object");
1293 let file = object::File::parse(&bytes[..]).expect("a readable object");
1294 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1295 }
1296
1297 #[test]
1298 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1299 let text = calling("f");
1300 let bytes =
1301 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1302 .expect("an object");
1303 let file = object::File::parse(&bytes[..]).expect("a readable object");
1304 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1305 let f = found.next().expect("the function");
1306 assert!(!f.is_undefined(), "the file defines it");
1307 assert!(found.next().is_none(), "and defines it once");
1308 }
1309
1310 #[test]
1311 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1312 let bytes = write(
1313 &calling("puts"),
1314 &Data::default(),
1315 &[],
1316 &target(),
1317 Output::default(),
1318 &Info::default(),
1319 )
1320 .expect("an object");
1321 let file = object::File::parse(&bytes[..]).expect("a readable object");
1322 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1323 assert!(note.data().expect("no bytes").is_empty());
1324 }
1325
1326 #[test]
1333 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1334 let property = Property { features: Property::IBT | Property::SHSTK };
1335 let output = Output { property, ..Output::default() };
1336 let bytes =
1337 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1338 .expect("an object");
1339 let file = object::File::parse(&bytes[..]).expect("a readable object");
1340 let note = file.section_by_name(".note.gnu.property").expect("the note");
1341 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1342 let want: Vec<u8> = [
1343 4u32,
1344 16,
1345 5,
1346 u32::from_le_bytes(*b"GNU\0"),
1347 Property::X86_FEATURES,
1348 4,
1349 Property::IBT | Property::SHSTK,
1350 0,
1351 ]
1352 .iter()
1353 .flat_map(|word| word.to_le_bytes())
1354 .collect();
1355 assert_eq!(note.data().expect("the bytes"), &want[..]);
1356 }
1357
1358 #[test]
1364 fn a_file_built_to_have_nothing_checked_says_nothing() {
1365 let bytes = write(
1366 &calling("puts"),
1367 &Data::default(),
1368 &[],
1369 &target(),
1370 Output::default(),
1371 &Info::default(),
1372 )
1373 .expect("an object");
1374 let file = object::File::parse(&bytes[..]).expect("a readable object");
1375 assert!(file.section_by_name(".note.gnu.property").is_none());
1376 }
1377
1378 #[test]
1387 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1388 let mut text = calling("puts");
1389 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1390 text.bytes.resize(17, 0x90);
1391 text.unwind.bytes = vec![0; 64];
1394 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1395 text.unwind.relocs.push(Reloc {
1396 at,
1397 symbol: name.to_owned(),
1398 kind: Reference::Address { bytes: 8 },
1399 addend: 0,
1400 after: 0,
1401 });
1402 }
1403 let bytes =
1404 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1405 .expect("an object");
1406 let file = object::File::parse(&bytes[..]).expect("a readable object");
1407 let mut found = points_at(&file);
1408 found.sort_unstable();
1409 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1410 }
1411
1412 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1415 let frames = file.section_by_name(".eh_frame").expect("the table");
1416 frames
1417 .relocations()
1418 .map(|(offset, reloc)| {
1419 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1420 panic!("a record points at something that is not a symbol");
1421 };
1422 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1423 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1424 let section = symbol.section_index().expect("a section symbol is in one");
1425 let name = file.section_by_index(section).expect("a readable section");
1426 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1427 })
1428 .collect()
1429 }
1430
1431 #[test]
1444 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1445 let mut text = two();
1446 text.unwind.bytes = vec![0; 64];
1447 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1448 text.unwind.relocs.push(Reloc {
1449 at,
1450 symbol: name.to_owned(),
1451 kind: Reference::Data,
1452 addend: 0,
1453 after: 0,
1454 });
1455 }
1456 let bytes =
1457 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1458 .expect("an object");
1459 let file = object::File::parse(&bytes[..]).expect("a readable object");
1460 let mut whole = points_at(&file);
1461 whole.sort_unstable();
1462 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1463
1464 let sections =
1465 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1466 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1467 .expect("an object");
1468 let file = object::File::parse(&bytes[..]).expect("a readable object");
1469 let mut split = points_at(&file);
1470 split.sort_unstable();
1471 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1472 }
1473
1474 #[test]
1481 fn a_record_about_something_this_file_does_not_define_is_refused() {
1482 let mut text = calling("puts");
1483 text.unwind.bytes = vec![0; 64];
1484 text.unwind.relocs.push(Reloc {
1485 at: 32,
1486 symbol: "puts".to_owned(),
1487 kind: Reference::Data,
1488 addend: 0,
1489 after: 0,
1490 });
1491 let why =
1492 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1493 .expect_err("a record about a name from somewhere else");
1494 assert!(why.to_string().contains("puts"), "{why}");
1495 }
1496
1497 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1499 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1500 let index = symbol.section_index().expect("a section to be defined in");
1501 let section = file.section_by_index(index).expect("a readable section");
1502 section.name().expect("a named section").to_owned()
1503 }
1504
1505 fn two() -> Text {
1507 let mut text = calling("puts");
1508 text.bytes.resize(16, 0x90);
1511 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1512 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1513 text.relocs.push(Reloc {
1514 at: 17,
1515 symbol: "puts".to_owned(),
1516 kind: Reference::Call,
1517 addend: -4,
1518 after: 0,
1519 });
1520 text
1521 }
1522
1523 #[test]
1530 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1531 let sections =
1532 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1533 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1534 .expect("an object");
1535 let file = object::File::parse(&bytes[..]).expect("a readable object");
1536 assert_eq!(lives_in(&file, "f"), ".text.f");
1537 assert_eq!(lives_in(&file, "g"), ".text.g");
1538 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1539 for name in ["f", "g"] {
1542 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1543 assert_eq!(symbol.address(), 0, "{name}");
1544 assert_eq!(symbol.size(), 6, "{name}");
1545 }
1546 let section = file.section_by_name(".text.g").expect("the second function");
1547 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1548 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1551 }
1552
1553 #[test]
1559 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1560 let sections =
1561 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1562 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1563 .expect("an object");
1564 let file = object::File::parse(&bytes[..]).expect("a readable object");
1565 for name in [".text.f", ".text.g"] {
1566 let section = file.section_by_name(name).expect("a function");
1567 let (offset, _) = section.relocations().next().expect("the call in it");
1568 assert_eq!(offset, 1, "{name}");
1571 assert_eq!(section.relocations().count(), 1, "{name}");
1572 }
1573 }
1574
1575 fn variable(name: &str, place: Place) -> Object {
1577 Object {
1578 name: name.to_owned(),
1579 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1580 size: 4,
1581 align: 4,
1582 place,
1583 binding: Binding::Global,
1584 visibility: Visibility::Default,
1585 relocs: Vec::new(),
1586 }
1587 }
1588
1589 fn measured() -> (Text, Data) {
1591 let mut text = calling("puts");
1592 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1593 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1594 let mut table = variable("table", Place::ReadOnly);
1595 table.bytes = vec![0; 8];
1596 table.size = 8;
1597 let apart = |at, to: &str, from: &str| Apart {
1598 object: 0,
1599 at,
1600 to: to.to_owned(),
1601 from: from.to_owned(),
1602 addend: 0,
1603 bytes: 4,
1604 };
1605 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1606 (text, Data { apart, weak: Vec::new(), objects: vec![table] })
1607 }
1608
1609 #[test]
1610 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1611 let (text, data) = measured();
1612 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1613 .expect("an object");
1614 let file = object::File::parse(&bytes[..]).expect("a readable object");
1615 let section = file.section_by_name(".rodata").expect("a read only section");
1616 assert_eq!(section.relocations().count(), 0);
1617 let image = section.data().expect("the image");
1618 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1619 }
1620
1621 #[test]
1622 fn a_distance_between_labels_in_two_sections_is_refused() {
1623 let (mut text, data) = measured();
1626 text.bytes.resize(22, 0x90);
1627 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1628 text.labels[1].at = 17;
1629 let output =
1630 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1631 let refused = write(&text, &data, &[], &target(), output, &Info::default());
1632 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1633 }
1634
1635 fn holding(object: Object) -> Vec<u8> {
1637 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
1638 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1639 .expect("an object")
1640 }
1641
1642 #[test]
1643 fn what_a_variable_is_decides_which_section_it_goes_in() {
1644 for (place, wanted) in [
1645 (Place::Written, ".data"),
1646 (Place::ReadOnly, ".rodata"),
1647 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1648 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1649 (Place::Zero, ".bss"),
1650 (Place::Thread { zero: false }, ".tdata"),
1651 (Place::Thread { zero: true }, ".tbss"),
1652 (Place::Named(".init_array".to_owned()), ".init_array"),
1653 ] {
1654 let bytes = holding(variable("x", place.clone()));
1655 let file = object::File::parse(&bytes[..]).expect("a readable object");
1656 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1657 assert_eq!(section.size(), 4, "{place:?}");
1658 let carried = section.data().expect("the bytes").len();
1661 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1662 }
1663 }
1664
1665 #[test]
1671 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1672 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1673 let bytes = holding(variable("counter", place.clone()));
1674 let file = object::File::parse(&bytes[..]).expect("a readable object");
1675 let symbol = file
1676 .symbols()
1677 .find(|symbol| symbol.name() == Ok("counter"))
1678 .unwrap_or_else(|| panic!("{place:?}"));
1679 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1680 }
1681 }
1682
1683 #[test]
1689 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1690 for (name, wanted) in [
1691 (".init_array", elf::SHT_INIT_ARRAY),
1692 (".init_array.00101", elf::SHT_INIT_ARRAY),
1693 (".fini_array", elf::SHT_FINI_ARRAY),
1694 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1695 (".init_arrays", elf::SHT_PROGBITS),
1696 ] {
1697 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1698 let file = object::File::parse(&bytes[..]).expect("a readable object");
1699 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1700 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1701 panic!("{name} is not an elf section");
1702 };
1703 assert_eq!(sh_type, wanted, "{name}");
1704 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1705 }
1706 }
1707
1708 #[test]
1714 fn two_variables_in_one_named_section_share_it() {
1715 let objects = vec![
1716 variable("x", Place::Named(".init_array".to_owned())),
1717 variable("y", Place::Named(".init_array".to_owned())),
1718 ];
1719 let data = Data { apart: Vec::new(), weak: Vec::new(), objects };
1720 let bytes =
1721 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1722 .expect("an object");
1723 let file = object::File::parse(&bytes[..]).expect("a readable object");
1724 let named: Vec<_> =
1725 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1726 assert_eq!(named.len(), 1);
1727 assert_eq!(named[0].size(), 8);
1728 }
1729
1730 #[test]
1734 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1735 let sections =
1736 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1737 for (place, wanted) in [
1738 (Place::Written, ".data.x"),
1739 (Place::ReadOnly, ".rodata.x"),
1740 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1741 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1742 (Place::Zero, ".bss.x"),
1743 (Place::Thread { zero: false }, ".tdata.x"),
1744 (Place::Thread { zero: true }, ".tbss.x"),
1745 ] {
1746 let data = Data {
1747 apart: Vec::new(),
1748 weak: Vec::new(),
1749 objects: vec![variable("x", place.clone())],
1750 };
1751 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1752 .expect("object");
1753 let file = object::File::parse(&bytes[..]).expect("a readable object");
1754 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1755 let section = file.section_by_name(wanted).expect("the section it named");
1756 assert_eq!(section.size(), 4, "{place:?}");
1757 let carried = section.data().expect("the bytes").len();
1760 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1761 }
1762 }
1763
1764 #[test]
1768 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1769 let sections =
1770 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1771 let named = Place::Named(".init_array".to_owned());
1772 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1773 let bytes = write(
1774 &Text::default(),
1775 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1776 &[],
1777 &target(),
1778 sections,
1779 &Info::default(),
1780 )
1781 .expect("object");
1782 let file = object::File::parse(&bytes[..]).expect("a readable object");
1783 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1784 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1785 assert_eq!(lives_in(&file, "n"), ".init_array");
1786 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1787 }
1788
1789 #[test]
1793 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1794 let sections =
1795 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1796 let pointer = Object {
1797 bytes: vec![0; 8],
1798 size: 8,
1799 align: 8,
1800 relocs: vec![Reloc {
1801 at: 0,
1802 symbol: "y".to_owned(),
1803 kind: Reference::Address { bytes: 8 },
1804 addend: 0,
1805 after: 0,
1806 }],
1807 ..variable("p", Place::Written)
1808 };
1809 let objects = vec![variable("first", Place::Written), pointer];
1810 let bytes = write(
1811 &Text::default(),
1812 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1813 &[],
1814 &target(),
1815 sections,
1816 &Info::default(),
1817 )
1818 .expect("object");
1819 let file = object::File::parse(&bytes[..]).expect("a readable object");
1820 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1821 let (offset, reloc) = section.relocations().next().expect("one relocation");
1822 assert_eq!(offset, 0);
1825 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1826 }
1827
1828 #[test]
1836 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1837 let place = Place::RelocReadOnly { local: true };
1838 let data = Data {
1839 apart: Vec::new(),
1840 weak: Vec::new(),
1841 objects: vec![variable("first", place.clone()), variable("second", place)],
1842 };
1843 let bytes =
1844 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1845 .expect("an object");
1846 let file = object::File::parse(&bytes[..]).expect("a readable object");
1847 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1848 assert_eq!(named, 1, "one section holding both, not one each");
1849 }
1850
1851 #[test]
1852 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1853 let mut data = Data {
1854 apart: Vec::new(),
1855 weak: Vec::new(),
1856 objects: vec![variable("first", Place::Written)],
1857 };
1858 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1859 let bytes =
1860 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1861 .expect("an object");
1862 let file = object::File::parse(&bytes[..]).expect("a readable object");
1863 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1864 assert_eq!(second.kind(), SymbolKind::Data);
1865 assert_eq!(second.size(), 4);
1866 assert_eq!(second.address(), 16);
1870 }
1871
1872 #[test]
1873 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1874 for (binding, global, weak) in [
1875 (Binding::Global, true, false),
1876 (Binding::Local, false, false),
1877 (Binding::Weak, true, true),
1878 ] {
1879 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1880 let file = object::File::parse(&bytes[..]).expect("a readable object");
1881 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1882 assert_eq!(x.is_global(), global, "{binding:?}");
1883 assert_eq!(x.is_weak(), weak, "{binding:?}");
1884 }
1885 }
1886
1887 #[test]
1888 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1889 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1890 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1891 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1892 assert!(x.is_common(), "the linker merges every definition of this name into one");
1893 assert_eq!(x.size(), 4);
1894 assert_eq!(x.address(), 0);
1898 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1899 }
1900
1901 #[test]
1902 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1903 let object = Object {
1904 bytes: vec![0; 8],
1905 size: 8,
1906 align: 8,
1907 relocs: vec![Reloc {
1908 at: 0,
1909 symbol: "y".to_owned(),
1910 kind: Reference::Address { bytes: 8 },
1911 addend: 16,
1912 after: 0,
1913 }],
1914 ..variable("p", Place::Written)
1915 };
1916 let bytes = holding(object);
1917 let file = object::File::parse(&bytes[..]).expect("a readable object");
1918 let section = file.section_by_name(".data").expect("a data section");
1919 let (offset, reloc) = section.relocations().next().expect("one relocation");
1920 assert_eq!(offset, 0);
1921 assert_eq!(reloc.addend(), 16);
1922 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1923 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1924 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1925 }
1926
1927 #[test]
1934 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
1935 let mut text = Text::default();
1936 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
1937 text.bytes.resize(8, 0x90);
1938 text.relocs.push(Reloc {
1939 at: 1,
1940 symbol: "hook".to_owned(),
1941 kind: Reference::Call,
1942 addend: -4,
1943 after: 0,
1944 });
1945 let data = Data {
1946 apart: Vec::new(),
1947 weak: vec!["hook".to_owned(), "never_called".to_owned()],
1948 objects: vec![],
1949 };
1950 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1951 .expect("an object");
1952 let file = object::File::parse(&bytes[..]).expect("a readable object");
1953
1954 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
1955 assert!(hook.is_undefined(), "nothing here defines it");
1956 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
1957
1958 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
1962 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
1963 }
1964
1965 #[test]
1980 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
1981 let mut text = Text::default();
1982 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
1983 text.bytes.resize(16, 0x90);
1984 text.relocs.push(Reloc {
1985 at: 3,
1986 symbol: "flags".to_owned(),
1987 kind: Reference::Thread,
1988 addend: -4,
1989 after: 0,
1990 });
1991 text.relocs.push(Reloc {
1994 at: 10,
1995 symbol: "shared".to_owned(),
1996 kind: Reference::Got,
1997 addend: -4,
1998 after: 0,
1999 });
2000 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![] };
2001 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2002 .expect("an object");
2003 let file = object::File::parse(&bytes[..]).expect("a readable object");
2004
2005 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2006 assert!(flags.is_undefined(), "nothing here defines it");
2007 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2008
2009 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2010 assert!(shared.is_undefined(), "nothing here defines this one either");
2011 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2012 }
2013
2014 #[test]
2016 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2017 let mut data = Data {
2018 apart: Vec::new(),
2019 weak: Vec::new(),
2020 objects: vec![variable("first", Place::Written)],
2021 };
2022 data.objects.push(Object {
2023 bytes: vec![0; 16],
2024 size: 16,
2025 align: 8,
2026 relocs: vec![Reloc {
2027 at: 8,
2028 symbol: "y".to_owned(),
2029 kind: Reference::Address { bytes: 8 },
2030 addend: 0,
2031 after: 0,
2032 }],
2033 ..variable("second", Place::Written)
2034 });
2035 let bytes =
2036 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2037 .expect("an object");
2038 let file = object::File::parse(&bytes[..]).expect("a readable object");
2039 let section = file.section_by_name(".data").expect("a data section");
2040 let (offset, _) = section.relocations().next().expect("one relocation");
2041 assert_eq!(offset, 16);
2044 }
2045
2046 #[test]
2047 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2048 let data = Data {
2049 apart: Vec::new(),
2050 weak: Vec::new(),
2051 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2052 };
2053 let aliases = [Alias {
2054 name: "b".to_owned(),
2055 target: "a".to_owned(),
2056 binding: Binding::Global,
2057 visibility: Visibility::Default,
2058 }];
2059 let bytes = write(
2060 &Text::default(),
2061 &data,
2062 &aliases,
2063 &target(),
2064 Output::default(),
2065 &Info::default(),
2066 )
2067 .expect("an object");
2068 let file = object::File::parse(&bytes[..]).expect("a readable object");
2069 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2070 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2071 assert_eq!(b.address(), a.address(), "the same place");
2072 assert_eq!(b.size(), a.size());
2073 assert_eq!(b.section_index(), a.section_index());
2074 assert!(a.is_local(), "the target was written `static`");
2077 assert!(b.is_global(), "and the name given to it was not");
2078 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2080 }
2081
2082 #[test]
2083 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2084 let text = calling("puts");
2085 let aliases = [Alias {
2086 name: "g".to_owned(),
2087 target: "f".to_owned(),
2088 binding: Binding::Weak,
2089 visibility: Visibility::Default,
2090 }];
2091 let bytes = write(
2092 &text,
2093 &Data::default(),
2094 &aliases,
2095 &target(),
2096 Output::default(),
2097 &Info::default(),
2098 )
2099 .expect("an object");
2100 let file = object::File::parse(&bytes[..]).expect("a readable object");
2101 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2102 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2103 assert_eq!(g.address(), f.address());
2104 assert_eq!(g.size(), f.size());
2105 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2106 assert!(g.is_weak(), "so that a program may define the name itself instead");
2107 }
2108
2109 #[test]
2112 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2113 let aliases = [Alias {
2114 name: "b".to_owned(),
2115 target: "a".to_owned(),
2116 binding: Binding::Global,
2117 visibility: Visibility::Default,
2118 }];
2119 let error = write(
2120 &Text::default(),
2121 &Data::default(),
2122 &aliases,
2123 &target(),
2124 Output::default(),
2125 &Info::default(),
2126 )
2127 .expect_err("nothing to point at");
2128 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2129 }
2130
2131 #[test]
2132 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2133 let text = calling("puts");
2134 for triple in [
2135 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2136 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2137 ] {
2138 let error = write(
2139 &text,
2140 &Data::default(),
2141 &[],
2142 &TargetInfo::new(triple),
2143 Output::default(),
2144 &Info::default(),
2145 )
2146 .expect_err("no writer");
2147 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2148 }
2149 }
2150
2151 #[test]
2157 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2158 let mut text = calling("puts");
2159 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2160 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2161 text.bytes.resize(33, 0x90);
2162 let data = Data {
2163 apart: Vec::new(),
2164 weak: Vec::new(),
2165 objects: vec![variable("seen", Place::Written), {
2166 let mut quiet = variable("quiet", Place::Zero);
2167 quiet.binding = Binding::Local;
2168 quiet
2169 }],
2170 };
2171 let aliases = [Alias {
2172 name: "second".to_owned(),
2173 target: "f".to_owned(),
2174 binding: Binding::Global,
2175 visibility: Visibility::Default,
2176 }];
2177
2178 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2179 assert_eq!(names, ["f", "shared", "seen", "second"]);
2180
2181 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2182 .expect("an object");
2183 let file = object::File::parse(&bytes[..]).expect("a readable object");
2184 let found: Vec<String> = file
2185 .symbols()
2186 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2187 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2188 .collect();
2189 let mut sorted = names.clone();
2190 sorted.sort();
2191 let mut theirs = found;
2192 theirs.sort();
2193 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2194 }
2195
2196 fn windows() -> TargetInfo {
2198 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2199 }
2200
2201 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2203 let file = object::File::parse(bytes).expect("a readable object");
2204 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2205 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2206 }
2207
2208 #[test]
2209 fn a_windows_target_is_written_rather_than_refused() {
2210 let text = calling("puts");
2211 let bytes =
2212 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2213 .expect("an object");
2214 let file = object::File::parse(&bytes[..]).expect("a readable object");
2215 assert_eq!(file.format(), BinaryFormat::Coff);
2216 let section = file.section_by_name(".text").expect("a text section");
2217 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2218 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2219 assert!(names.contains(&"f"), "{names:?}");
2220 assert!(names.contains(&"puts"), "{names:?}");
2221 }
2222
2223 #[test]
2231 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2232 for (after, typ) in [
2233 (0, pe::IMAGE_REL_AMD64_REL32),
2234 (1, pe::IMAGE_REL_AMD64_REL32_1),
2235 (4, pe::IMAGE_REL_AMD64_REL32_4),
2236 (5, pe::IMAGE_REL_AMD64_REL32_5),
2237 ] {
2238 let mut text = calling("puts");
2239 text.relocs[0].addend = -4 - i64::from(after);
2242 text.relocs[0].after = after;
2243 text.bytes.resize(6 + after as usize, 0x90);
2244 text.funcs[0].len = text.bytes.len();
2245 let bytes = write(
2246 &text,
2247 &Data::default(),
2248 &[],
2249 &windows(),
2250 Output::default(),
2251 &Info::default(),
2252 )
2253 .expect("an object");
2254 let file = object::File::parse(&bytes[..]).expect("a readable object");
2255 let section = file.section_by_name(".text").expect("a text section");
2256 let (_, reloc) = section.relocations().next().expect("the relocation");
2257 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2258 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2261 }
2262 }
2263
2264 #[test]
2267 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2268 let mut text = calling("puts");
2269 text.relocs[0].addend = 12;
2270 let bytes =
2271 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2272 .expect("an object");
2273 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2274 }
2275
2276 #[test]
2277 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2278 let object = Object {
2279 bytes: vec![0; 8],
2280 size: 8,
2281 align: 8,
2282 relocs: vec![Reloc {
2283 at: 0,
2284 symbol: "y".to_owned(),
2285 kind: Reference::Address { bytes: 8 },
2286 addend: 0,
2287 after: 0,
2288 }],
2289 ..variable("p", Place::Written)
2290 };
2291 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
2292 let bytes =
2293 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2294 .expect("an object");
2295 let file = object::File::parse(&bytes[..]).expect("a readable object");
2296 let section = file.section_by_name(".data").expect("a data section");
2297 let (_, reloc) = section.relocations().next().expect("the relocation");
2298 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2299 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2300 }
2301
2302 #[test]
2305 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2306 for local in [false, true] {
2307 let data = Data {
2308 apart: Vec::new(),
2309 weak: Vec::new(),
2310 objects: vec![variable("p", Place::RelocReadOnly { local })],
2311 };
2312 let bytes = write(
2313 &Text::default(),
2314 &data,
2315 &[],
2316 &windows(),
2317 Output::default(),
2318 &Info::default(),
2319 )
2320 .expect("an object");
2321 let file = object::File::parse(&bytes[..]).expect("a readable object");
2322 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2323 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2324 }
2325 }
2326
2327 #[test]
2330 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2331 let text = calling("puts");
2332 let output = Output { property: Property { features: 3 }, ..Output::default() };
2333 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2334 .expect("an object");
2335 let file = object::File::parse(&bytes[..]).expect("a readable object");
2336 assert!(file.section_by_name(".note.GNU-stack").is_none());
2337 assert!(file.section_by_name(".note.gnu.property").is_none());
2338 }
2339
2340 #[test]
2345 fn what_this_format_cannot_say_is_refused_by_name() {
2346 let ordinary = Text::default();
2347 let empty = Data::default();
2348
2349 let mut thread = Data::default();
2350 thread.objects.push(variable("t", Place::Thread { zero: false }));
2351
2352 let mut gathered = Data::default();
2353 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2354
2355 let mut table = calling("puts");
2356 table.relocs[0].kind = Reference::Got;
2357
2358 let mut room = calling("puts");
2359 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2360
2361 let cases: [(&str, &Text, &Data); 4] = [
2362 ("thread-local", &ordinary, &thread),
2363 ("startup", &ordinary, &gathered),
2364 ("table", &table, &empty),
2365 ("patcher", &room, &empty),
2366 ];
2367 for (what, text, data) in cases {
2368 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2369 .expect_err("something this format cannot write");
2370 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2371 }
2372 }
2373
2374 #[test]
2378 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2379 let mut text = calling("puts");
2380 text.funcs[0].visibility = Visibility::Hidden;
2381 let bytes =
2382 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2383 .expect("an object");
2384 let file = object::File::parse(&bytes[..]).expect("a readable object");
2385 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2386 assert!(symbol.is_global(), "a name others may use either way");
2387 }
2388
2389 #[test]
2390 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2391 let text = calling("puts");
2392 let data = Data {
2393 apart: Vec::new(),
2394 weak: Vec::new(),
2395 objects: vec![variable("shared", Place::Written)],
2396 };
2397 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2398 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2399 }
2400
2401 #[test]
2405 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2406 let text = calling("puts");
2407 for triple in [
2408 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2409 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2410 ] {
2411 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2412 .expect_err("no writer");
2413 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2414 }
2415 }
2416}