1use std::collections::BTreeMap;
34
35use object::write::{
36 Comdat, Mangling, Object as Writer, Relocation, StandardSection, Symbol, SymbolId,
37 SymbolSection,
38};
39use object::{
40 Architecture, BinaryFormat, ComdatKind, Endianness, RelocationFlags, SectionFlags, SectionKind,
41 SymbolFlags, SymbolKind, SymbolScope,
42};
43use rucc_base::hash::{Map, Set};
44use rucc_target::{ObjectFormat, TargetInfo};
45use rucc_tuple::Arch;
46
47use crate::section::{
48 Alias, Apart, Array, Binding, Compress, Data, EXCEPT_TABLE, Export, Holds, Info, Object,
49 Output, Place, Property, Reference, Reloc, Sections, Text, Visibility,
50};
51use crate::{coff, elf};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub(crate) enum Flavour {
61 Elf,
63 Coff,
65 MachO,
68}
69
70impl Flavour {
71 pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
73 match target.object_format {
74 ObjectFormat::Elf => Some(Flavour::Elf),
75 ObjectFormat::Coff => Some(Flavour::Coff),
76 ObjectFormat::MachO | ObjectFormat::Wasm => None,
77 }
78 }
79
80 pub(crate) fn binary(self) -> BinaryFormat {
82 match self {
83 Flavour::Elf => BinaryFormat::Elf,
84 Flavour::Coff => BinaryFormat::Coff,
85 Flavour::MachO => BinaryFormat::MachO,
86 }
87 }
88
89 pub(crate) fn reloc(
95 self,
96 machine: Architecture,
97 reference: Reference,
98 after: u8,
99 ) -> Option<RelocationFlags> {
100 let flags = |r_type| RelocationFlags::Elf { r_type };
101 match (self, machine) {
102 (Flavour::Elf, Architecture::I386) => elf::r_type_i386(reference).map(flags),
103 (Flavour::Elf, Architecture::Aarch64) => elf::r_type_aarch64(reference).map(flags),
104 (Flavour::Elf, _) => elf::r_type(reference).map(flags),
105 (Flavour::Coff, Architecture::Aarch64) => {
106 coff::arm64(reference).map(|typ| RelocationFlags::Coff { typ })
107 }
108 (Flavour::Coff, Architecture::I386) => {
109 coff::i386(reference).map(|typ| RelocationFlags::Coff { typ })
110 }
111 (Flavour::Coff, _) => coff::reloc(reference, after),
112 (Flavour::MachO, _) => crate::macho::reloc(reference, 0).ok(),
113 }
114 }
115
116 pub(crate) fn machine(self, arch: Arch) -> Option<Architecture> {
122 match (self, arch) {
123 (Flavour::Elf | Flavour::Coff, Arch::X86_64) => Some(Architecture::X86_64),
124 (Flavour::Elf | Flavour::Coff, Arch::Aarch64) => Some(Architecture::Aarch64),
125 (Flavour::Elf | Flavour::Coff, Arch::X86) => Some(Architecture::I386),
126 _ => None,
127 }
128 }
129
130 pub(crate) fn spell(self, machine: Architecture, name: &str) -> String {
138 match (self, machine) {
139 (Flavour::Coff, Architecture::I386) => coff::decorate(name),
140 _ => name.to_owned(),
141 }
142 }
143
144 pub(crate) fn see(
150 self,
151 obj: &mut Writer<'_>,
152 id: SymbolId,
153 binding: Binding,
154 visibility: Visibility,
155 ) {
156 match self {
157 Flavour::Elf => elf::see(obj, id, binding, visibility),
158 Flavour::Coff => {}
159 Flavour::MachO => {
162 if binding != Binding::Local && visibility == Visibility::Hidden {
163 obj.symbol_mut(id).scope = SymbolScope::Linkage;
164 }
165 }
166 }
167 }
168
169 fn rel_ro_local(self) -> Option<&'static str> {
173 match self {
174 Flavour::Elf => elf::REL_RO_LOCAL,
175 Flavour::Coff => coff::REL_RO_LOCAL,
176 Flavour::MachO => None,
177 }
178 }
179
180 pub(crate) fn debug(self, kind: Reference, into_debug: bool) -> Reference {
192 match kind {
193 Reference::Address { bytes: 4 } if self == Flavour::Coff && into_debug => {
194 Reference::Section
195 }
196 kind => kind,
197 }
198 }
199
200 fn gathered(self, array: Array) -> Option<SectionFlags> {
201 match self {
202 Flavour::Elf => Some(elf::gathered(array)),
203 Flavour::Coff | Flavour::MachO => None,
204 }
205 }
206
207 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
216 match self {
217 Flavour::Elf => {
218 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
219 }
220 Flavour::Coff => (shape.coff != 0).then_some(SectionFlags::Coff {
221 characteristics: object::pe::SectionFlags(shape.coff),
222 }),
223 Flavour::MachO => Some(SectionFlags::MachO {
224 flags: object::macho::SectionFlags(shape.mach),
225 reserved2: 0,
226 }),
227 }
228 }
229
230 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
246 if self == Flavour::MachO {
247 return match sort {
248 crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
249 crate::source::Sort::File => SymbolKind::File,
250 _ => SymbolKind::Data,
251 };
252 }
253 match sort {
254 crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
257 crate::source::Sort::Object => SymbolKind::Data,
258 crate::source::Sort::Thread => SymbolKind::Tls,
259 crate::source::Sort::File => SymbolKind::File,
260 crate::source::Sort::Untyped => match (self, binding) {
261 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
262 _ => SymbolKind::Label,
263 },
264 }
265 }
266
267 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
269 match self {
270 Flavour::Elf => elf::marker(obj),
271 Flavour::Coff => coff::marker(obj),
272 Flavour::MachO => {}
273 }
274 }
275
276 fn property(self, obj: &mut Writer<'_>, property: Property) {
282 if !property.any() {
283 return;
284 }
285 match self {
286 Flavour::Elf => {
287 let note = obj.section_id(StandardSection::GnuProperty);
288 let align = if obj.architecture() == Architecture::I386 { 4 } else { 8 };
289 obj.append_section_data(note, &elf::record(property, align), u64::from(align));
290 }
291 Flavour::Coff | Flavour::MachO => {}
292 }
293 }
294
295 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
299 match self {
300 Flavour::Elf => (elf::FRAMES, None),
301 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
302 Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
303 }
304 }
305
306 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
308 match self {
309 Flavour::Elf => elf::link(bytes, ordered),
310 Flavour::Coff | Flavour::MachO => {
311 debug_assert!(ordered.is_empty(), "a record this format cannot write");
312 }
313 }
314 }
315}
316
317#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum Error {
320 Format {
322 triple: String,
324 },
325 Refused {
327 why: String,
329 },
330}
331
332impl std::fmt::Display for Error {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 match self {
335 Error::Format { triple } => {
336 write!(f, "there is no object writer for {triple} in this compiler yet")
337 }
338 Error::Refused { why } => {
339 write!(f, "the object writer refused what it was given: {why}")
340 }
341 }
342 }
343}
344
345impl std::error::Error for Error {}
346
347pub fn write(
363 text: &Text,
364 data: &Data,
365 aliases: &[Alias],
366 target: &TargetInfo,
367 output: Output,
368 info: &Info,
369) -> Result<Vec<u8>, Error> {
370 let Output { sections, property, ident, .. } = output;
371 let Some((flavour, machine)) = written(target) else {
372 return Err(Error::Format { triple: target.tuple.to_string() });
373 };
374 if flavour == Flavour::Coff {
375 beyond(text, data)?;
376 }
377 let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
378 obj.set_mangling(Mangling::None);
381 let spell = |name: &str| flavour.spell(machine, name).into_bytes();
382 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
384 let whole = obj.section_id(StandardSection::Text);
388 if flavour == Flavour::Elf {
392 obj.section_id(StandardSection::Data);
393 obj.section_id(StandardSection::UninitializedData);
394 }
395 if !sections.functions {
396 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
397 }
398
399 let mut symbols = BTreeMap::new();
403 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
408 let mut ordered: Vec<String> = Vec::new();
411 for func in &text.funcs {
412 let ahead = func.patch.map_or(0, |patch| patch.before);
422 let (section, at) = if sections.functions {
423 let name = format!(".text.{}", func.name).into_bytes();
424 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
425 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
426 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
427 (id, ahead as u64)
428 } else {
429 (whole, func.start as u64)
430 };
431 if let Some(patch) = func.patch {
446 let base = if sections.functions { func.start - ahead } else { 0 };
447 let name = elf::PATCHABLE.as_bytes().to_vec();
448 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
449 obj.section_mut(id).flags = elf::ordered();
450 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
451 let symbol = obj.section_symbol(section);
452 let flags =
453 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(
454 || Error::Refused { why: "no relocation holds an address here".to_owned() },
455 )?;
456 relocate(
457 &mut obj,
458 id,
459 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
460 )?;
461 ordered.push(if sections.functions {
462 format!(".text.{}", func.name)
463 } else {
464 ".text".to_owned()
465 });
466 }
467 let id = obj.add_symbol(Symbol {
468 name: spell(&func.name),
469 value: at,
470 size: func.len as u64,
471 kind: SymbolKind::Text,
472 scope: scope_of(func.binding),
473 weak: func.binding == Binding::Weak,
474 section: SymbolSection::Section(section),
475 flags: SymbolFlags::None,
476 });
477 flavour.see(&mut obj, id, func.binding, func.visibility);
478 symbols.insert(func.name.clone(), id);
479 split.push((section, at));
480 }
481
482 let mut places: Places = BTreeMap::new();
486 for label in &text.labels {
487 let after = text.funcs.partition_point(|func| func.start <= label.at);
488 let Some(index) = after.checked_sub(1) else {
489 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
490 return Err(Error::Refused { why });
491 };
492 let func = &text.funcs[index];
493 let (section, at) = if sections.functions {
494 let base = func.start - func.patch.map_or(0, |patch| patch.before);
497 (split[index].0, (label.at - base) as u64)
498 } else {
499 (whole, label.at as u64)
500 };
501 if flavour == Flavour::Elf {
507 places.insert(label.name.clone(), (section, at));
508 continue;
509 }
510 let id = obj.add_symbol(Symbol {
511 name: label.name.clone().into_bytes(),
512 value: at,
513 size: 0,
516 kind: SymbolKind::Label,
517 scope: SymbolScope::Compilation,
521 weak: false,
522 section: SymbolSection::Section(section),
523 flags: SymbolFlags::None,
524 });
525 symbols.insert(label.name.clone(), id);
526 }
527
528 if !text.mcount.is_empty() {
534 let name = crate::section::MCOUNT_LOC.as_bytes().to_vec();
535 let id = obj.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
536 let flags =
537 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(|| {
538 Error::Refused { why: "no relocation holds an address here".to_owned() }
539 })?;
540 for &call in &text.mcount {
541 let after = text.funcs.partition_point(|func| func.start <= call);
542 let Some(index) = after.checked_sub(1) else {
543 let why = format!("a profiler call at {call} is in front of every function");
544 return Err(Error::Refused { why });
545 };
546 let func = &text.funcs[index];
547 let (section, at) = if sections.functions {
548 let base = func.start - func.patch.map_or(0, |patch| patch.before);
549 (split[index].0, call - base)
550 } else {
551 (whole, call)
552 };
553 let offset =
554 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
555 let symbol = obj.section_symbol(section);
556 relocate(&mut obj, id, Relocation { offset, symbol, addend: at as i64, flags })?;
557 }
558 }
559
560 let mut placed = Vec::with_capacity(data.objects.len());
565 let mut named = Map::default();
569 for object in &data.objects {
570 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
571 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
575 obj.section_symbol(section);
576 }
577 let id = obj.add_symbol(Symbol {
578 name: spell(&object.name),
579 value: if object.place == Place::Merged { object.align } else { offset },
582 size: object.size,
583 kind: match object.place {
588 Place::Thread { .. } => SymbolKind::Tls,
589 _ => SymbolKind::Data,
590 },
591 scope: scope_of(object.binding),
592 weak: object.binding == Binding::Weak,
593 section,
594 flags: SymbolFlags::None,
595 });
596 flavour.see(&mut obj, id, object.binding, object.visibility);
597 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
601 obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
602 }
603 symbols.insert(object.name.clone(), id);
604 placed.push((section.id(), offset));
605 }
606
607 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
610
611 for apart in &data.apart {
615 let (Some(section), offset) = placed[apart.object] else { continue };
616 let value = distance(&obj, &symbols, &places, apart)?;
617 let bytes = usize::from(apart.bytes);
618 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
619 let at = at + apart.at;
620 let image = obj.section_mut(section).data_mut();
621 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
622 }
623
624 for alias in aliases {
630 let Some(&id) = symbols.get(&alias.target) else {
631 let why =
632 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
633 return Err(Error::Refused { why });
634 };
635 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
636 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
637 let id = obj.add_symbol(Symbol {
638 name: spell(&alias.name),
639 value,
640 size,
641 kind,
642 scope: scope_of(alias.binding),
643 weak: alias.binding == Binding::Weak,
644 section,
645 flags: SymbolFlags::None,
646 });
647 flavour.see(&mut obj, id, alias.binding, alias.visibility);
648 if alias.ifunc {
649 if flavour != Flavour::Elf {
650 let why = format!("'{}' is an indirect function, which only ELF has", alias.name);
651 return Err(Error::Refused { why });
652 }
653 elf::indirect(&mut obj, id);
654 }
655 symbols.insert(alias.name.clone(), id);
656 }
657
658 let weak: Set<&str> = data.weak.iter().map(String::as_str).collect();
665 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
666 let thread: Set<&str> = relocs()
671 .filter(|reloc| reloc.kind == Reference::Thread)
672 .map(|reloc| reloc.symbol.as_str())
673 .collect();
674 let wanted: Vec<&String> =
675 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
676 for name in wanted {
677 if symbols.contains_key(name) || tables.contains_key(name) || places.contains_key(name) {
678 continue;
679 }
680 let id = obj.add_symbol(Symbol {
681 name: spell(name),
682 value: 0,
683 size: 0,
684 kind: if thread.contains(name.as_str()) {
694 SymbolKind::Tls
695 } else {
696 SymbolKind::Unknown
697 },
698 scope: SymbolScope::Dynamic,
699 weak: weak.contains(name.as_str()),
700 section: SymbolSection::Undefined,
701 flags: SymbolFlags::None,
702 });
703 symbols.insert(name.clone(), id);
704 }
705
706 for reloc in &text.relocs {
707 let (section, at) = if sections.functions {
712 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
713 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
714 let why = format!("a relocation at {} is in front of every function", reloc.at);
715 return Err(Error::Refused { why });
716 };
717 let base = func.start - func.patch.map_or(0, |patch| patch.before);
720 (split[after - 1].0, (reloc.at - base) as u64)
721 } else {
722 (whole, reloc.at as u64)
723 };
724 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
728 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
729 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
730 })?;
731 let symbol = obj.section_symbol(table);
732 let addend = reloc.addend + offset as i64;
733 relocate(&mut obj, section, Relocation { offset: at, symbol, addend, flags })?;
734 continue;
735 }
736 add(&mut obj, section, at, reloc, &symbols, &places, flavour)?;
737 }
738
739 let seh_free = flavour == Flavour::Coff && machine == Architecture::I386;
749 if !text.unwind.bytes.is_empty() && !seh_free {
750 let ((name, align), second) = flavour.tables();
751 let align = if machine == Architecture::I386 { 4 } else { align };
754 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
755 obj.append_section_data(frames, &text.unwind.bytes, align);
756 let mut described = Map::default();
761 if !text.unwind.info.is_empty() {
762 let Some((name, align)) = second else {
763 let why = "an unwind table here is one section and it was given two".to_owned();
764 return Err(Error::Refused { why });
765 };
766 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
767 obj.append_section_data(codes, &text.unwind.info, align);
768 for label in &text.unwind.labels {
769 let id = obj.add_symbol(Symbol {
770 name: label.name.clone().into_bytes(),
771 value: label.at as u64,
772 size: 0,
773 kind: SymbolKind::Label,
774 scope: SymbolScope::Compilation,
775 weak: false,
776 section: SymbolSection::Section(codes),
777 flags: SymbolFlags::None,
778 });
779 described.insert(label.name.clone(), id);
780 }
781 }
782 if !text.unwind.except.is_empty() {
787 let except =
788 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
789 obj.append_section_data(except, &text.unwind.except, 4);
790 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
791 }
792 for reloc in &text.unwind.relocs {
793 let found = described.get(&reloc.symbol).or_else(|| {
794 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
798 if ours { symbols.get(&reloc.symbol) } else { None }
799 });
800 let (symbol, addend) = match found {
801 Some(&id) => (id, reloc.addend),
805 None => {
819 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
820 let Some((section, at)) = found.map(|i| split[i]) else {
821 let why = format!(
822 "'{}' has an unwind record and is not a function here",
823 reloc.symbol
824 );
825 return Err(Error::Refused { why });
826 };
827 (obj.section_symbol(section), reloc.addend + at as i64)
831 }
832 };
833 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
834 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
835 })?;
836 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
837 relocate(&mut obj, frames, record)?;
838 }
839 }
840 let mut named = Map::default();
849 for chunk in &info.chunks {
850 let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
853 info.compress
854 } else {
855 Compress::None
856 };
857 let id = crate::zlib::debug_section(&mut obj, chunk, how);
858 named.insert(chunk.name.as_str(), id);
859 }
860 for chunk in &info.chunks {
861 let section = named[chunk.name.as_str()];
862 for reloc in &chunk.relocs {
863 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
864 Some(&id) => (obj.section_symbol(id), reloc.addend),
867 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
872 Some(which) => {
873 let (section, at) = split[which];
874 (obj.section_symbol(section), reloc.addend + at as i64)
875 }
876 None => {
882 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
883 let Some(which) = found else {
884 let why = format!(
885 "'{}' is named by the debug information and is not defined here",
886 reloc.symbol
887 );
888 return Err(Error::Refused { why });
889 };
890 match placed[which] {
891 (Some(section), at) => {
892 (obj.section_symbol(section), reloc.addend + at as i64)
893 }
894 (None, _) => (symbols[&reloc.symbol], reloc.addend),
895 }
896 }
897 },
898 };
899 let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
900 let flags = flavour
901 .reloc(machine, kind, reloc.after)
902 .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
903 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
904 relocate(&mut obj, section, record)?;
905 }
906 }
907 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
908 let Some(section) = section else { continue };
909 for reloc in &object.relocs {
910 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, &places, flavour)?;
911 }
912 }
913
914 if !data.exports.is_empty() {
918 let options: String = data.exports.iter().map(Export::option).collect();
919 let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
920 obj.append_section_data(id, options.as_bytes(), 1);
921 }
922
923 flavour.property(&mut obj, property);
927
928 if let Some(ident) = ident.filter(|_| flavour == Flavour::Elf) {
930 let id = obj.add_section(Vec::new(), b".comment".to_vec(), SectionKind::OtherString);
931 let bytes = [&[0][..], ident.as_bytes(), &[0]].concat();
932 obj.append_section_data(id, &bytes, 1);
933 }
934
935 flavour.marker(&mut obj);
938
939 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
940 flavour.finish(&mut bytes, &ordered);
941 Ok(bytes)
942}
943
944type Places = BTreeMap<String, (object::write::SectionId, u64)>;
947
948fn distance(
956 obj: &Writer<'_>,
957 symbols: &BTreeMap<String, SymbolId>,
958 places: &Places,
959 apart: &Apart,
960) -> Result<i64, Error> {
961 let find = |name: &str| match (symbols.get(name), places.get(name)) {
962 (Some(&id), _) => Ok((obj.symbol(id).section, obj.symbol(id).value)),
963 (None, Some(&(section, at))) => Ok((SymbolSection::Section(section), at)),
964 (None, None) => {
965 Err(Error::Refused { why: format!("'{name}' is measured from and is not here") })
966 }
967 };
968 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
969 if to.0 != from.0 {
970 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
971 return Err(Error::Refused { why });
972 }
973 let value = (to.1 as i64).wrapping_sub(from.1 as i64).wrapping_add(apart.addend);
974 let bits = u32::from(apart.bytes) * 8;
975 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
976 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
977 return Err(Error::Refused { why });
978 }
979 Ok(value)
980}
981
982fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
995 let why = |why: String| Err(Error::Refused { why });
996 if text.funcs.iter().any(|func| func.patch.is_some()) {
997 return why("a record of where a patcher's room is has no section flags here".to_owned());
998 }
999 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
1000 if matches!(
1001 reloc.kind,
1002 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
1003 ) {
1004 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
1005 }
1006 }
1007 for object in &data.objects {
1008 if matches!(object.place, Place::Thread { zero: true }) {
1009 return why(format!(
1010 "'{}' is zeroed thread-local storage, which is not here",
1011 object.name
1012 ));
1013 }
1014 let Place::Named(name, _) = &object.place else { continue };
1015 if Array::of(name).is_some() {
1016 return why(format!("'{name}' is not a list the startup code here gathers"));
1017 }
1018 }
1019 Ok(())
1020}
1021
1022pub fn defines(
1045 text: &Text,
1046 data: &Data,
1047 aliases: &[Alias],
1048 target: &TargetInfo,
1049) -> Result<Vec<String>, Error> {
1050 let Some((flavour, machine)) = written(target) else {
1051 return Err(Error::Format { triple: target.tuple.to_string() });
1052 };
1053 let spell = |name: &String| flavour.spell(machine, name);
1054 let names = text
1055 .funcs
1056 .iter()
1057 .filter(|func| func.binding != Binding::Local)
1058 .map(|func| spell(&func.name))
1059 .chain(
1060 data.objects
1061 .iter()
1062 .filter(|object| object.binding != Binding::Local)
1063 .map(|object| spell(&object.name)),
1064 )
1065 .chain(
1066 aliases
1067 .iter()
1068 .filter(|alias| alias.binding != Binding::Local)
1069 .map(|alias| spell(&alias.name)),
1070 )
1071 .collect();
1072 Ok(names)
1073}
1074
1075fn put(
1082 obj: &mut Writer<'_>,
1083 object: &Object,
1084 named: &mut Map<String, object::write::SectionId>,
1085 sections: Sections,
1086 flavour: Flavour,
1087) -> (SymbolSection, u64) {
1088 if sections.data {
1094 if let Some(name) = object.place.split(&object.name) {
1095 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
1096 let offset = if carries_no_bytes(&object.place) {
1097 obj.append_section_bss(section, object.size, object.align)
1098 } else {
1099 obj.append_section_data(section, &object.bytes, object.align)
1100 };
1101 return (SymbolSection::Section(section), offset);
1102 }
1103 }
1104 let section = match &object.place {
1105 Place::Written => obj.section_id(StandardSection::Data),
1106 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
1107 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
1113 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
1114 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
1115 },
1116 Place::Zero => obj.section_id(StandardSection::UninitializedData),
1117 Place::Strings { align } => {
1120 made(obj, named, &Place::strings(*align), SectionKind::ReadOnlyString)
1121 }
1122 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
1123 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
1124 Place::Merged => return (SymbolSection::Common, 0),
1125 Place::Named(name, _) => {
1132 let section = made(obj, named, name, kind_of(&object.place));
1133 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
1134 obj.section_mut(section).flags = flags;
1135 }
1136 section
1137 }
1138 Place::Pointer => {
1142 let name = format!(".rdata${}", object.name);
1143 made(obj, named, &name, SectionKind::ReadOnlyData)
1144 }
1145 };
1146 let offset = if carries_no_bytes(&object.place) {
1147 obj.append_section_bss(section, object.size, object.align)
1148 } else {
1149 obj.append_section_data(section, &object.bytes, object.align)
1150 };
1151 (SymbolSection::Section(section), offset)
1152}
1153
1154fn tables(
1167 obj: &mut Writer<'_>,
1168 text: &Text,
1169 split: &[(object::write::SectionId, u64)],
1170 named: &mut Map<String, object::write::SectionId>,
1171 sections: Sections,
1172 flavour: Flavour,
1173) -> Result<Map<String, (object::write::SectionId, u64)>, Error> {
1174 let mut placed = Map::default();
1175 if text.tables.is_empty() {
1176 return Ok(placed);
1177 }
1178 if flavour != Flavour::Elf {
1179 let why = "a jump table outside the code is written on ELF only".to_owned();
1180 return Err(Error::Refused { why });
1181 }
1182 let machine = obj.architecture();
1183 let flags = flavour.reloc(machine, Reference::Away, 0).ok_or_else(|| Error::Refused {
1184 why: "no relocation is a distance from where it is written".to_owned(),
1185 })?;
1186 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
1188 for table in &text.tables {
1189 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1190 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1191 })?;
1192 let section = if sections.data {
1193 let name = format!(".rodata.{}", func.name);
1194 made(obj, named, &name, SectionKind::ReadOnlyData)
1195 } else {
1196 obj.section_id(StandardSection::ReadOnlyData)
1197 };
1198 let (width, flags) = if table.absolute {
1201 let reference = Reference::Address { bytes: pointer };
1202 let wide = flavour.reloc(machine, reference, 0).ok_or_else(|| Error::Refused {
1203 why: format!("no relocation is an address in {pointer} bytes"),
1204 })?;
1205 (usize::from(pointer), wide)
1206 } else {
1207 (4, flags)
1208 };
1209 let offset =
1210 obj.append_section_data(section, &vec![0; width * table.cells.len()], width as u64);
1211 placed.insert(table.name.clone(), (section, offset));
1212 let (code, at) = split[table.func];
1213 let symbol = obj.section_symbol(code);
1214 for (index, &cell) in table.cells.iter().enumerate() {
1215 let place = (width * index) as u64;
1216 let addend = at as i64 + cell as i64 + if table.absolute { 0 } else { place as i64 };
1217 let record = Relocation { offset: offset + place, symbol, addend, flags };
1218 relocate(obj, section, record)?;
1219 }
1220 }
1221 Ok(placed)
1222}
1223
1224fn carries_no_bytes(place: &Place) -> bool {
1230 matches!(place, Place::Zero | Place::Thread { zero: true } | Place::Named(_, Holds::Zero))
1231}
1232
1233fn made(
1241 obj: &mut Writer<'_>,
1242 named: &mut Map<String, object::write::SectionId>,
1243 name: &str,
1244 kind: SectionKind,
1245) -> object::write::SectionId {
1246 if let Some(section) = named.get(name) {
1247 return *section;
1248 }
1249 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1250 named.insert(name.to_owned(), section);
1251 section
1252}
1253
1254fn kind_of(place: &Place) -> SectionKind {
1266 match place {
1267 Place::ReadOnly | Place::Pointer | Place::Named(_, Holds::ReadOnly) => {
1268 SectionKind::ReadOnlyData
1269 }
1270 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1271 Place::Strings { .. } => SectionKind::ReadOnlyString,
1272 Place::Zero | Place::Named(_, Holds::Zero) => SectionKind::UninitializedData,
1273 Place::Thread { zero: false } => SectionKind::Tls,
1274 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1275 Place::Written | Place::Merged | Place::Named(_, Holds::Written) => SectionKind::Data,
1276 }
1277}
1278
1279fn add(
1286 obj: &mut Writer<'_>,
1287 section: object::write::SectionId,
1288 at: u64,
1289 reloc: &Reloc,
1290 symbols: &BTreeMap<String, SymbolId>,
1291 places: &Places,
1292 flavour: Flavour,
1293) -> Result<(), Error> {
1294 let flags = flavour
1295 .reloc(obj.architecture(), reloc.kind, reloc.after)
1296 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1297 let (symbol, addend) = match places.get(&reloc.symbol) {
1299 Some(&(held, offset)) => (obj.section_symbol(held), reloc.addend + offset as i64),
1300 None => (symbols[&reloc.symbol], reloc.addend),
1301 };
1302 relocate(obj, section, Relocation { offset: at, symbol, addend, flags })
1303}
1304
1305pub(crate) fn relocate(
1324 obj: &mut Writer<'_>,
1325 section: object::write::SectionId,
1326 mut relocation: Relocation,
1327) -> Result<(), Error> {
1328 if let (Architecture::I386, RelocationFlags::Elf { r_type }) =
1329 (obj.architecture(), relocation.flags)
1330 {
1331 let Some(width) = elf::width_i386(r_type) else {
1332 let why = format!("relocation type {} has no width this writer knows", r_type.0);
1333 return Err(Error::Refused { why });
1334 };
1335 let addend = relocation.addend;
1336 let bits = 8 * width as u32;
1337 let least = if width == 4 { -(1i64 << bits) } else { -(1i64 << (bits - 1)) };
1343 if addend < least || addend >= 1i64 << bits {
1344 let why =
1345 format!("{addend} added to a name, and there are {width} bytes to keep it in");
1346 return Err(Error::Refused { why });
1347 }
1348 let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1349 let data = obj.section_mut(section).data_mut();
1350 let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1351 let why = format!("a relocation at {at} is past the end of its section");
1352 return Err(Error::Refused { why });
1353 };
1354 place.copy_from_slice(&addend.to_le_bytes()[..width]);
1355 relocation.addend = 0;
1356 }
1357 obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1358}
1359
1360fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1366 let flavour = Flavour::of(target)?;
1367 let machine = flavour.machine(target.tuple.arch())?;
1368 (machine != Architecture::Aarch64).then_some((flavour, machine))
1369}
1370
1371pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1384 match binding {
1385 Binding::Local => SymbolScope::Compilation,
1386 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1387 }
1388}
1389
1390#[cfg(test)]
1391mod tests {
1392 use super::*;
1393
1394 use object::read::elf::Sym as _;
1395 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1396 use object::{elf, pe};
1397 use rucc_target::{Arch, Env, Os, Triple};
1398
1399 use crate::elf::PATCHABLE;
1400 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1401
1402 fn target() -> TargetInfo {
1404 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1405 }
1406
1407 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1412 Extent {
1413 name,
1414 start,
1415 len,
1416 align: crate::FUNC_ALIGN,
1417 binding,
1418 visibility: Visibility::Default,
1419 patch: None,
1420 landings: Vec::new(),
1421 }
1422 }
1423
1424 fn calling(name: &str) -> Text {
1426 Text {
1427 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1428 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1429 relocs: vec![Reloc {
1430 at: 1,
1431 symbol: name.to_owned(),
1432 kind: Reference::Call,
1433 addend: -4,
1434 after: 0,
1435 }],
1436 ..Text::default()
1437 }
1438 }
1439
1440 #[test]
1441 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1442 let text = calling("puts");
1443 let bytes =
1444 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1445 .expect("an object");
1446 let file = object::File::parse(&bytes[..]).expect("a readable object");
1447 let section = file.section_by_name(".text").expect("a text section");
1448 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1449 }
1450
1451 #[test]
1452 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1453 let mut text = calling("puts");
1454 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1455 text.bytes.resize(17, 0x90);
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 g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1461 assert_eq!(g.address(), 16);
1462 assert_eq!(g.size(), 1);
1463 assert_eq!(g.kind(), SymbolKind::Text);
1464 assert!(g.is_global(), "nothing said otherwise about this one");
1465 }
1466
1467 #[test]
1468 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1469 let mut text = calling("puts");
1470 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1471 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1472 text.bytes.resize(33, 0x90);
1473 let bytes =
1474 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1475 .expect("an object");
1476 let file = object::File::parse(&bytes[..]).expect("a readable object");
1477 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1478 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1481 assert!(!hidden.is_weak());
1482 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1483 assert!(shared.is_weak(), "a weak function has to be able to lose");
1484 assert!(shared.is_global());
1485 }
1486
1487 #[test]
1504 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1505 let mut text = calling("puts");
1506 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1507 text.funcs[0].start = 3;
1508 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1509 text.relocs[0].at = 4;
1510 let bytes =
1511 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1512 .expect("an object");
1513 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1514 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1515 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1516 assert_eq!(section.align(), 8);
1517 let header = section.elf_section_header();
1518 assert_eq!(
1519 header.sh_flags.get(Endianness::Little),
1520 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1521 );
1522 let index = file.section_by_name(".text").expect("a text section").index().0;
1525 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1526 assert_ne!(index, 0);
1527
1528 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1530 panic!("one address in the record")
1531 };
1532 assert_eq!(*at, 0);
1533 assert_eq!(reloc.addend(), 0);
1534 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1535 }
1536
1537 #[test]
1539 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1540 let text = calling("puts");
1541 let bytes =
1542 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1543 .expect("an object");
1544 let file = object::File::parse(&bytes[..]).expect("a readable object");
1545 assert!(file.section_by_name(PATCHABLE).is_none());
1546 }
1547
1548 #[test]
1554 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1555 let mut text = calling("puts");
1556 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1557 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1558 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1559 text.bytes.resize(17, 0x90);
1560 let output =
1561 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1562 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1563 .expect("an object");
1564 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1565 let links: Vec<usize> = file
1566 .sections()
1567 .filter(|section| section.name() == Ok(PATCHABLE))
1568 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1569 .collect();
1570 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1571 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1572 }
1573
1574 #[test]
1575 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1576 let mut text = calling("puts");
1577 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1578 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1579 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1580 text.bytes.resize(49, 0x90);
1581 let bytes =
1582 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1583 .expect("an object");
1584 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1585 let visibility = |name: &str| {
1586 file.symbols()
1587 .find(|s| s.name() == Ok(name))
1588 .expect("the function")
1589 .elf_symbol()
1590 .st_visibility()
1591 };
1592 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1594 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1595 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1598 }
1599
1600 #[test]
1610 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1611 let mut text = calling("puts");
1612 for (index, (name, seen)) in
1613 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1614 {
1615 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1616 func.visibility = seen;
1617 text.funcs.push(func);
1618 }
1619 text.bytes.resize(49, 0x90);
1620 let mut data = Data::default();
1621 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1622 let mut object = variable(name, Place::Written);
1623 object.visibility = seen;
1624 data.objects.push(object);
1625 }
1626 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1627 .expect("an object");
1628 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1629 let visibility = |name: &str| {
1630 file.symbols()
1631 .find(|s| s.name() == Ok(name))
1632 .expect("the symbol")
1633 .elf_symbol()
1634 .st_visibility()
1635 };
1636 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1637 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1638 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1639 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1640 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1643 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1644 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1645 }
1646
1647 #[test]
1648 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1649 let bytes = write(
1650 &calling("puts"),
1651 &Data::default(),
1652 &[],
1653 &target(),
1654 Output::default(),
1655 &Info::default(),
1656 )
1657 .expect("an object");
1658 let file = object::File::parse(&bytes[..]).expect("a readable object");
1659 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1660 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1661 }
1662
1663 #[test]
1664 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1665 for (reference, wanted) in [
1666 (Reference::Call, elf::R_X86_64_PLT32),
1667 (Reference::Data, elf::R_X86_64_PC32),
1668 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1669 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1670 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1671 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1672 ] {
1673 let mut text = calling("puts");
1674 text.relocs[0].kind = reference;
1675 let bytes =
1676 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1677 .expect("an object");
1678 let file = object::File::parse(&bytes[..]).expect("a readable object");
1679 let section = file.section_by_name(".text").expect("a text section");
1680 let (offset, reloc) = section.relocations().next().expect("one relocation");
1681 assert_eq!(offset, 1);
1682 assert_eq!(reloc.addend(), -4);
1683 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1684 }
1685 }
1686
1687 #[test]
1690 fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1691 for (reference, wanted) in [
1692 (Reference::AwayWide, elf::R_X86_64_PC64),
1693 (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1694 (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1695 ] {
1696 assert_eq!(crate::elf::r_type(reference), Some(wanted));
1697 }
1698 assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1699 }
1700
1701 #[test]
1702 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1703 let mut text = calling("puts");
1704 text.relocs.push(Reloc {
1705 at: 1,
1706 symbol: "puts".to_owned(),
1707 kind: Reference::Call,
1708 addend: -4,
1709 after: 0,
1710 });
1711 let bytes =
1712 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1713 .expect("an object");
1714 let file = object::File::parse(&bytes[..]).expect("a readable object");
1715 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1716 }
1717
1718 #[test]
1719 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1720 let text = calling("f");
1721 let bytes =
1722 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1723 .expect("an object");
1724 let file = object::File::parse(&bytes[..]).expect("a readable object");
1725 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1726 let f = found.next().expect("the function");
1727 assert!(!f.is_undefined(), "the file defines it");
1728 assert!(found.next().is_none(), "and defines it once");
1729 }
1730
1731 #[test]
1732 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1733 let bytes = write(
1734 &calling("puts"),
1735 &Data::default(),
1736 &[],
1737 &target(),
1738 Output::default(),
1739 &Info::default(),
1740 )
1741 .expect("an object");
1742 let file = object::File::parse(&bytes[..]).expect("a readable object");
1743 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1744 assert!(note.data().expect("no bytes").is_empty());
1745 }
1746
1747 #[test]
1754 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1755 let property = Property { features: Property::IBT | Property::SHSTK };
1756 let output = Output { property, ..Output::default() };
1757 let bytes =
1758 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1759 .expect("an object");
1760 let file = object::File::parse(&bytes[..]).expect("a readable object");
1761 let note = file.section_by_name(".note.gnu.property").expect("the note");
1762 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1763 let want: Vec<u8> = [
1764 4u32,
1765 16,
1766 5,
1767 u32::from_le_bytes(*b"GNU\0"),
1768 Property::X86_FEATURES,
1769 4,
1770 Property::IBT | Property::SHSTK,
1771 0,
1772 ]
1773 .iter()
1774 .flat_map(|word| word.to_le_bytes())
1775 .collect();
1776 assert_eq!(note.data().expect("the bytes"), &want[..]);
1777 }
1778
1779 #[test]
1785 fn a_file_built_to_have_nothing_checked_says_nothing() {
1786 let bytes = write(
1787 &calling("puts"),
1788 &Data::default(),
1789 &[],
1790 &target(),
1791 Output::default(),
1792 &Info::default(),
1793 )
1794 .expect("an object");
1795 let file = object::File::parse(&bytes[..]).expect("a readable object");
1796 assert!(file.section_by_name(".note.gnu.property").is_none());
1797 }
1798
1799 #[test]
1808 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1809 let mut text = calling("puts");
1810 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1811 text.bytes.resize(17, 0x90);
1812 text.unwind.bytes = vec![0; 64];
1815 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1816 text.unwind.relocs.push(Reloc {
1817 at,
1818 symbol: name.to_owned(),
1819 kind: Reference::Address { bytes: 8 },
1820 addend: 0,
1821 after: 0,
1822 });
1823 }
1824 let bytes =
1825 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1826 .expect("an object");
1827 let file = object::File::parse(&bytes[..]).expect("a readable object");
1828 let mut found = points_at(&file);
1829 found.sort_unstable();
1830 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1831 }
1832
1833 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1836 let frames = file.section_by_name(".eh_frame").expect("the table");
1837 frames
1838 .relocations()
1839 .map(|(offset, reloc)| {
1840 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1841 panic!("a record points at something that is not a symbol");
1842 };
1843 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1844 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1845 let section = symbol.section_index().expect("a section symbol is in one");
1846 let name = file.section_by_index(section).expect("a readable section");
1847 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1848 })
1849 .collect()
1850 }
1851
1852 #[test]
1865 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1866 let mut text = two();
1867 text.unwind.bytes = vec![0; 64];
1868 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1869 text.unwind.relocs.push(Reloc {
1870 at,
1871 symbol: name.to_owned(),
1872 kind: Reference::Data,
1873 addend: 0,
1874 after: 0,
1875 });
1876 }
1877 let bytes =
1878 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1879 .expect("an object");
1880 let file = object::File::parse(&bytes[..]).expect("a readable object");
1881 let mut whole = points_at(&file);
1882 whole.sort_unstable();
1883 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1884
1885 let sections =
1886 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1887 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1888 .expect("an object");
1889 let file = object::File::parse(&bytes[..]).expect("a readable object");
1890 let mut split = points_at(&file);
1891 split.sort_unstable();
1892 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1893 }
1894
1895 #[test]
1902 fn a_record_about_something_this_file_does_not_define_is_refused() {
1903 let mut text = calling("puts");
1904 text.unwind.bytes = vec![0; 64];
1905 text.unwind.relocs.push(Reloc {
1906 at: 32,
1907 symbol: "puts".to_owned(),
1908 kind: Reference::Data,
1909 addend: 0,
1910 after: 0,
1911 });
1912 let why =
1913 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1914 .expect_err("a record about a name from somewhere else");
1915 assert!(why.to_string().contains("puts"), "{why}");
1916 }
1917
1918 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1920 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1921 let index = symbol.section_index().expect("a section to be defined in");
1922 let section = file.section_by_index(index).expect("a readable section");
1923 section.name().expect("a named section").to_owned()
1924 }
1925
1926 fn two() -> Text {
1928 let mut text = calling("puts");
1929 text.bytes.resize(16, 0x90);
1932 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1933 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1934 text.relocs.push(Reloc {
1935 at: 17,
1936 symbol: "puts".to_owned(),
1937 kind: Reference::Call,
1938 addend: -4,
1939 after: 0,
1940 });
1941 text
1942 }
1943
1944 #[test]
1951 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1952 let sections =
1953 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1954 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1955 .expect("an object");
1956 let file = object::File::parse(&bytes[..]).expect("a readable object");
1957 assert_eq!(lives_in(&file, "f"), ".text.f");
1958 assert_eq!(lives_in(&file, "g"), ".text.g");
1959 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1960 for name in ["f", "g"] {
1963 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1964 assert_eq!(symbol.address(), 0, "{name}");
1965 assert_eq!(symbol.size(), 6, "{name}");
1966 }
1967 let section = file.section_by_name(".text.g").expect("the second function");
1968 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1969 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1972 }
1973
1974 #[test]
1980 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1981 let sections =
1982 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1983 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1984 .expect("an object");
1985 let file = object::File::parse(&bytes[..]).expect("a readable object");
1986 for name in [".text.f", ".text.g"] {
1987 let section = file.section_by_name(name).expect("a function");
1988 let (offset, _) = section.relocations().next().expect("the call in it");
1989 assert_eq!(offset, 1, "{name}");
1992 assert_eq!(section.relocations().count(), 1, "{name}");
1993 }
1994 }
1995
1996 fn switching() -> Text {
1998 let mut text = two();
1999 let name = ".Lg_j0".to_owned();
2000 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
2001 text
2002 }
2003
2004 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
2006 let section = file.section_by_name(section).expect("the table's section");
2007 section
2008 .relocations()
2009 .map(|(offset, reloc)| {
2010 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
2011 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2012 panic!("a cell against something that is not a symbol");
2013 };
2014 let symbol = file.symbol_by_index(index).expect("a symbol");
2015 assert_eq!(symbol.kind(), SymbolKind::Section);
2016 let at = symbol.section_index().expect("a section symbol is in one");
2017 let name = file.section_by_index(at).expect("a section").name().expect("a name");
2018 (offset, name.to_owned(), reloc.addend())
2019 })
2020 .collect()
2021 }
2022
2023 #[test]
2024 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
2025 let mut text = switching();
2028 text.relocs[1].symbol = ".Lg_j0".to_owned();
2029 text.relocs[1].kind = Reference::Data;
2030 let bytes =
2031 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2032 .expect("an object");
2033 let file = object::File::parse(&bytes[..]).expect("a readable object");
2034 let rodata = file.section_by_name(".rodata").expect("the table's section");
2035 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2036 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2037 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2038 let (at, reloc) = file
2039 .section_by_name(".text")
2040 .expect("the code")
2041 .relocations()
2042 .find(|(at, _)| *at == 17)
2043 .expect("the reference to the table");
2044 assert_eq!((at, reloc.addend()), (17, -4));
2045 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2046 panic!("a reference against something that is not a symbol");
2047 };
2048 let symbol = file.symbol_by_index(index).expect("a symbol");
2049 assert_eq!(symbol.section_index(), Some(rodata.index()));
2050 assert_eq!(symbol.kind(), SymbolKind::Section);
2051 assert_eq!(
2054 cells(&file, ".rodata"),
2055 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2056 );
2057 }
2058
2059 #[test]
2060 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2061 let sections =
2062 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2063 let bytes =
2064 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2065 .expect("an object");
2066 let file = object::File::parse(&bytes[..]).expect("a readable object");
2067 assert_eq!(
2069 cells(&file, ".rodata.g"),
2070 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2071 );
2072 }
2073
2074 #[test]
2075 fn a_jump_table_outside_the_code_is_refused_on_windows() {
2076 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2077 let written = write(
2078 &switching(),
2079 &Data::default(),
2080 &[],
2081 &target,
2082 Output::default(),
2083 &Info::default(),
2084 );
2085 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2086 }
2087
2088 #[test]
2091 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2092 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2093 let reloc = |at, symbol: &str, bytes| Reloc {
2094 at,
2095 symbol: symbol.to_owned(),
2096 kind: Reference::Address { bytes },
2097 addend: 0,
2098 after: 0,
2099 };
2100 let info = Info {
2101 chunks: vec![
2102 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2103 Chunk {
2104 name: ".debug_info".to_owned(),
2105 bytes: vec![0; 12],
2106 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2107 },
2108 ],
2109 ..Info::default()
2110 };
2111 let bytes =
2112 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2113 .expect("object");
2114 let file = object::File::parse(&bytes[..]).expect("a readable object");
2115 let section = file.section_by_name(".debug_info").expect("the debug section");
2116 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2117 assert_eq!(
2118 kinds,
2119 [
2120 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2121 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2122 ]
2123 );
2124 }
2125
2126 fn variable(name: &str, place: Place) -> Object {
2128 Object {
2129 name: name.to_owned(),
2130 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2131 size: 4,
2132 align: 4,
2133 place,
2134 binding: Binding::Global,
2135 visibility: Visibility::Default,
2136 relocs: Vec::new(),
2137 }
2138 }
2139
2140 fn measured() -> (Text, Data) {
2142 let mut text = calling("puts");
2143 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2144 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2145 let mut table = variable("table", Place::ReadOnly);
2146 table.bytes = vec![0; 8];
2147 table.size = 8;
2148 let apart = |at, to: &str, from: &str| Apart {
2149 object: 0,
2150 at,
2151 to: to.to_owned(),
2152 from: from.to_owned(),
2153 addend: 0,
2154 bytes: 4,
2155 };
2156 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2157 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2158 }
2159
2160 #[test]
2161 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2162 let (text, data) = measured();
2163 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2164 .expect("an object");
2165 let file = object::File::parse(&bytes[..]).expect("a readable object");
2166 let section = file.section_by_name(".rodata").expect("a read only section");
2167 assert_eq!(section.relocations().count(), 0);
2168 let image = section.data().expect("the image");
2169 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2170 }
2171
2172 #[test]
2176 fn a_label_an_image_holds_is_not_in_the_symbol_table() {
2177 let (text, mut data) = measured();
2178 data.apart.clear();
2179 data.objects[0].relocs.push(Reloc {
2180 at: 0,
2181 symbol: ".L1".to_owned(),
2182 kind: Reference::Address { bytes: 8 },
2183 addend: 0,
2184 after: 0,
2185 });
2186 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2187 .expect("an object");
2188 let file = object::File::parse(&bytes[..]).expect("a readable object");
2189 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2190 assert!(names.iter().all(|name| !name.starts_with(".L")), "{names:?}");
2191 let section = file.section_by_name(".rodata").expect("a read only section");
2192 let relocs: Vec<_> = section.relocations().collect();
2193 assert_eq!(relocs.len(), 1);
2194 let (_, reloc) = &relocs[0];
2195 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2196 panic!("a relocation against a symbol, not {reloc:?}");
2197 };
2198 let symbol = file.symbol_by_index(index).expect("a symbol");
2199 assert_eq!(symbol.kind(), SymbolKind::Section);
2200 assert_eq!(reloc.addend(), 5);
2201 }
2202
2203 #[test]
2204 fn a_distance_between_labels_in_two_sections_is_refused() {
2205 let (mut text, data) = measured();
2208 text.bytes.resize(22, 0x90);
2209 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2210 text.labels[1].at = 17;
2211 let output =
2212 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2213 let refused = write(&text, &data, &[], &target(), output, &Info::default());
2214 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2215 }
2216
2217 fn holding(object: Object) -> Vec<u8> {
2219 let data = Data {
2220 apart: Vec::new(),
2221 exports: Vec::new(),
2222 weak: Vec::new(),
2223 objects: vec![object],
2224 };
2225 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2226 .expect("an object")
2227 }
2228
2229 #[test]
2230 fn what_a_variable_is_decides_which_section_it_goes_in() {
2231 for (place, wanted) in [
2232 (Place::Written, ".data"),
2233 (Place::ReadOnly, ".rodata"),
2234 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2235 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2236 (Place::Zero, ".bss"),
2237 (Place::Thread { zero: false }, ".tdata"),
2238 (Place::Thread { zero: true }, ".tbss"),
2239 (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2240 ] {
2241 let bytes = holding(variable("x", place.clone()));
2242 let file = object::File::parse(&bytes[..]).expect("a readable object");
2243 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2244 assert_eq!(section.size(), 4, "{place:?}");
2245 let carried = section.data().expect("the bytes").len();
2248 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2249 }
2250 }
2251
2252 #[test]
2258 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2259 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2260 let bytes = holding(variable("counter", place.clone()));
2261 let file = object::File::parse(&bytes[..]).expect("a readable object");
2262 let symbol = file
2263 .symbols()
2264 .find(|symbol| symbol.name() == Ok("counter"))
2265 .unwrap_or_else(|| panic!("{place:?}"));
2266 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2267 }
2268 }
2269
2270 #[test]
2276 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2277 for (name, wanted) in [
2278 (".init_array", elf::SHT_INIT_ARRAY),
2279 (".init_array.00101", elf::SHT_INIT_ARRAY),
2280 (".fini_array", elf::SHT_FINI_ARRAY),
2281 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2282 (".init_arrays", elf::SHT_PROGBITS),
2283 ] {
2284 let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2285 let file = object::File::parse(&bytes[..]).expect("a readable object");
2286 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2287 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2288 panic!("{name} is not an elf section");
2289 };
2290 assert_eq!(sh_type, wanted, "{name}");
2291 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2292 }
2293 }
2294
2295 #[test]
2299 fn a_named_section_carries_the_flags_of_what_is_in_it() {
2300 for (name, holds, kind, flags) in [
2301 (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2302 (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2303 (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2304 ] {
2305 let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2306 let file = object::File::parse(&bytes[..]).expect("a readable object");
2307 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2308 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2309 panic!("{name} is not an elf section");
2310 };
2311 assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2312 assert_eq!(section.size(), 4, "{name}");
2313 }
2314 }
2315
2316 #[test]
2322 fn two_variables_in_one_named_section_share_it() {
2323 let objects = vec![
2324 variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2325 variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2326 ];
2327 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2328 let bytes =
2329 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2330 .expect("an object");
2331 let file = object::File::parse(&bytes[..]).expect("a readable object");
2332 let named: Vec<_> =
2333 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2334 assert_eq!(named.len(), 1);
2335 assert_eq!(named[0].size(), 8);
2336 }
2337
2338 #[test]
2342 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2343 let sections =
2344 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2345 for (place, wanted) in [
2346 (Place::Written, ".data.x"),
2347 (Place::ReadOnly, ".rodata.x"),
2348 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2349 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2350 (Place::Zero, ".bss.x"),
2351 (Place::Thread { zero: false }, ".tdata.x"),
2352 (Place::Thread { zero: true }, ".tbss.x"),
2353 ] {
2354 let data = Data {
2355 apart: Vec::new(),
2356 exports: Vec::new(),
2357 weak: Vec::new(),
2358 objects: vec![variable("x", place.clone())],
2359 };
2360 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2361 .expect("object");
2362 let file = object::File::parse(&bytes[..]).expect("a readable object");
2363 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2364 let section = file.section_by_name(wanted).expect("the section it named");
2365 assert_eq!(section.size(), 4, "{place:?}");
2366 let carried = section.data().expect("the bytes").len();
2369 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2370 }
2371 }
2372
2373 #[test]
2377 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2378 let sections =
2379 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2380 let named = Place::Named(".init_array".to_owned(), Holds::Written);
2381 let objects = vec![variable("m", Place::Merged), variable("n", named)];
2382 let bytes = write(
2383 &Text::default(),
2384 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2385 &[],
2386 &target(),
2387 sections,
2388 &Info::default(),
2389 )
2390 .expect("object");
2391 let file = object::File::parse(&bytes[..]).expect("a readable object");
2392 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2393 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2394 assert_eq!(lives_in(&file, "n"), ".init_array");
2395 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2396 }
2397
2398 #[test]
2402 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2403 let sections =
2404 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2405 let pointer = Object {
2406 bytes: vec![0; 8],
2407 size: 8,
2408 align: 8,
2409 relocs: vec![Reloc {
2410 at: 0,
2411 symbol: "y".to_owned(),
2412 kind: Reference::Address { bytes: 8 },
2413 addend: 0,
2414 after: 0,
2415 }],
2416 ..variable("p", Place::Written)
2417 };
2418 let objects = vec![variable("first", Place::Written), pointer];
2419 let bytes = write(
2420 &Text::default(),
2421 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2422 &[],
2423 &target(),
2424 sections,
2425 &Info::default(),
2426 )
2427 .expect("object");
2428 let file = object::File::parse(&bytes[..]).expect("a readable object");
2429 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2430 let (offset, reloc) = section.relocations().next().expect("one relocation");
2431 assert_eq!(offset, 0);
2434 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2435 }
2436
2437 #[test]
2445 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2446 let place = Place::RelocReadOnly { local: true };
2447 let data = Data {
2448 apart: Vec::new(),
2449 exports: Vec::new(),
2450 weak: Vec::new(),
2451 objects: vec![variable("first", place.clone()), variable("second", place)],
2452 };
2453 let bytes =
2454 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2455 .expect("an object");
2456 let file = object::File::parse(&bytes[..]).expect("a readable object");
2457 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2458 assert_eq!(named, 1, "one section holding both, not one each");
2459 }
2460
2461 #[test]
2462 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2463 let mut data = Data {
2464 apart: Vec::new(),
2465 exports: Vec::new(),
2466 weak: Vec::new(),
2467 objects: vec![variable("first", Place::Written)],
2468 };
2469 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2470 let bytes =
2471 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2472 .expect("an object");
2473 let file = object::File::parse(&bytes[..]).expect("a readable object");
2474 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2475 assert_eq!(second.kind(), SymbolKind::Data);
2476 assert_eq!(second.size(), 4);
2477 assert_eq!(second.address(), 16);
2481 }
2482
2483 #[test]
2484 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2485 for (binding, global, weak) in [
2486 (Binding::Global, true, false),
2487 (Binding::Local, false, false),
2488 (Binding::Weak, true, true),
2489 ] {
2490 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2491 let file = object::File::parse(&bytes[..]).expect("a readable object");
2492 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2493 assert_eq!(x.is_global(), global, "{binding:?}");
2494 assert_eq!(x.is_weak(), weak, "{binding:?}");
2495 }
2496 }
2497
2498 #[test]
2499 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2500 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2501 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2502 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2503 assert!(x.is_common(), "the linker merges every definition of this name into one");
2504 assert_eq!(x.size(), 4);
2505 assert_eq!(x.address(), 0);
2509 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2510 }
2511
2512 #[test]
2513 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2514 let object = Object {
2515 bytes: vec![0; 8],
2516 size: 8,
2517 align: 8,
2518 relocs: vec![Reloc {
2519 at: 0,
2520 symbol: "y".to_owned(),
2521 kind: Reference::Address { bytes: 8 },
2522 addend: 16,
2523 after: 0,
2524 }],
2525 ..variable("p", Place::Written)
2526 };
2527 let bytes = holding(object);
2528 let file = object::File::parse(&bytes[..]).expect("a readable object");
2529 let section = file.section_by_name(".data").expect("a data section");
2530 let (offset, reloc) = section.relocations().next().expect("one relocation");
2531 assert_eq!(offset, 0);
2532 assert_eq!(reloc.addend(), 16);
2533 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2534 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2535 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2536 }
2537
2538 #[test]
2545 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2546 let mut text = Text::default();
2547 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2548 text.bytes.resize(8, 0x90);
2549 text.relocs.push(Reloc {
2550 at: 1,
2551 symbol: "hook".to_owned(),
2552 kind: Reference::Call,
2553 addend: -4,
2554 after: 0,
2555 });
2556 let data = Data {
2557 apart: Vec::new(),
2558 exports: Vec::new(),
2559 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2560 objects: vec![],
2561 };
2562 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2563 .expect("an object");
2564 let file = object::File::parse(&bytes[..]).expect("a readable object");
2565
2566 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2567 assert!(hook.is_undefined(), "nothing here defines it");
2568 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2569
2570 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2574 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2575 }
2576
2577 #[test]
2592 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2593 let mut text = Text::default();
2594 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2595 text.bytes.resize(16, 0x90);
2596 text.relocs.push(Reloc {
2597 at: 3,
2598 symbol: "flags".to_owned(),
2599 kind: Reference::Thread,
2600 addend: -4,
2601 after: 0,
2602 });
2603 text.relocs.push(Reloc {
2606 at: 10,
2607 symbol: "shared".to_owned(),
2608 kind: Reference::Got,
2609 addend: -4,
2610 after: 0,
2611 });
2612 let data =
2613 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2614 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2615 .expect("an object");
2616 let file = object::File::parse(&bytes[..]).expect("a readable object");
2617
2618 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2619 assert!(flags.is_undefined(), "nothing here defines it");
2620 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2621
2622 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2623 assert!(shared.is_undefined(), "nothing here defines this one either");
2624 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2625 }
2626
2627 #[test]
2629 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2630 let mut data = Data {
2631 apart: Vec::new(),
2632 exports: Vec::new(),
2633 weak: Vec::new(),
2634 objects: vec![variable("first", Place::Written)],
2635 };
2636 data.objects.push(Object {
2637 bytes: vec![0; 16],
2638 size: 16,
2639 align: 8,
2640 relocs: vec![Reloc {
2641 at: 8,
2642 symbol: "y".to_owned(),
2643 kind: Reference::Address { bytes: 8 },
2644 addend: 0,
2645 after: 0,
2646 }],
2647 ..variable("second", Place::Written)
2648 });
2649 let bytes =
2650 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2651 .expect("an object");
2652 let file = object::File::parse(&bytes[..]).expect("a readable object");
2653 let section = file.section_by_name(".data").expect("a data section");
2654 let (offset, _) = section.relocations().next().expect("one relocation");
2655 assert_eq!(offset, 16);
2658 }
2659
2660 #[test]
2661 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2662 let data = Data {
2663 apart: Vec::new(),
2664 exports: Vec::new(),
2665 weak: Vec::new(),
2666 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2667 };
2668 let aliases = [Alias {
2669 name: "b".to_owned(),
2670 target: "a".to_owned(),
2671 binding: Binding::Global,
2672 visibility: Visibility::Default,
2673 ifunc: false,
2674 }];
2675 let bytes = write(
2676 &Text::default(),
2677 &data,
2678 &aliases,
2679 &target(),
2680 Output::default(),
2681 &Info::default(),
2682 )
2683 .expect("an object");
2684 let file = object::File::parse(&bytes[..]).expect("a readable object");
2685 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2686 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2687 assert_eq!(b.address(), a.address(), "the same place");
2688 assert_eq!(b.size(), a.size());
2689 assert_eq!(b.section_index(), a.section_index());
2690 assert!(a.is_local(), "the target was written `static`");
2693 assert!(b.is_global(), "and the name given to it was not");
2694 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2696 }
2697
2698 #[test]
2699 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2700 let text = calling("puts");
2701 let aliases = [Alias {
2702 name: "g".to_owned(),
2703 target: "f".to_owned(),
2704 binding: Binding::Weak,
2705 visibility: Visibility::Default,
2706 ifunc: false,
2707 }];
2708 let bytes = write(
2709 &text,
2710 &Data::default(),
2711 &aliases,
2712 &target(),
2713 Output::default(),
2714 &Info::default(),
2715 )
2716 .expect("an object");
2717 let file = object::File::parse(&bytes[..]).expect("a readable object");
2718 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2719 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2720 assert_eq!(g.address(), f.address());
2721 assert_eq!(g.size(), f.size());
2722 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2723 assert!(g.is_weak(), "so that a program may define the name itself instead");
2724 }
2725
2726 #[test]
2730 fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2731 let text = calling("puts");
2732 for (binding, bind) in [
2733 (Binding::Global, elf::STB_GLOBAL),
2734 (Binding::Weak, elf::STB_WEAK),
2735 (Binding::Local, elf::STB_LOCAL),
2736 ] {
2737 let aliases = [Alias {
2738 name: "g".to_owned(),
2739 target: "f".to_owned(),
2740 binding,
2741 visibility: Visibility::Default,
2742 ifunc: true,
2743 }];
2744 let bytes = write(
2745 &text,
2746 &Data::default(),
2747 &aliases,
2748 &target(),
2749 Output::default(),
2750 &Info::default(),
2751 )
2752 .expect("an object");
2753 let file = object::File::parse(&bytes[..]).expect("a readable object");
2754 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2755 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2756 assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2757 let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2758 panic!("an ELF symbol");
2759 };
2760 assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2761 let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2762 let os_abi = elf.elf_header().e_ident.os_abi;
2763 assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2764 }
2765 }
2766
2767 #[test]
2770 fn an_ifunc_is_refused_on_a_format_without_the_type() {
2771 let aliases = [Alias {
2772 name: "g".to_owned(),
2773 target: "f".to_owned(),
2774 binding: Binding::Global,
2775 visibility: Visibility::Default,
2776 ifunc: true,
2777 }];
2778 let error = write(
2779 &calling("puts"),
2780 &Data::default(),
2781 &aliases,
2782 &windows(),
2783 Output::default(),
2784 &Info::default(),
2785 )
2786 .expect_err("no ifunc on COFF");
2787 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2788 }
2789
2790 #[test]
2793 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2794 let aliases = [Alias {
2795 name: "b".to_owned(),
2796 target: "a".to_owned(),
2797 binding: Binding::Global,
2798 visibility: Visibility::Default,
2799 ifunc: false,
2800 }];
2801 let error = write(
2802 &Text::default(),
2803 &Data::default(),
2804 &aliases,
2805 &target(),
2806 Output::default(),
2807 &Info::default(),
2808 )
2809 .expect_err("nothing to point at");
2810 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2811 }
2812
2813 #[test]
2814 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2815 let text = calling("puts");
2816 for triple in [
2817 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2818 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2819 ] {
2820 let error = write(
2821 &text,
2822 &Data::default(),
2823 &[],
2824 &TargetInfo::new(triple),
2825 Output::default(),
2826 &Info::default(),
2827 )
2828 .expect_err("no writer");
2829 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2830 }
2831 }
2832
2833 #[test]
2839 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2840 let mut text = calling("puts");
2841 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2842 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2843 text.bytes.resize(33, 0x90);
2844 let data = Data {
2845 apart: Vec::new(),
2846 exports: Vec::new(),
2847 weak: Vec::new(),
2848 objects: vec![variable("seen", Place::Written), {
2849 let mut quiet = variable("quiet", Place::Zero);
2850 quiet.binding = Binding::Local;
2851 quiet
2852 }],
2853 };
2854 let aliases = [Alias {
2855 name: "second".to_owned(),
2856 target: "f".to_owned(),
2857 binding: Binding::Global,
2858 visibility: Visibility::Default,
2859 ifunc: false,
2860 }];
2861
2862 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2863 assert_eq!(names, ["f", "shared", "seen", "second"]);
2864
2865 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2866 .expect("an object");
2867 let file = object::File::parse(&bytes[..]).expect("a readable object");
2868 let found: Vec<String> = file
2869 .symbols()
2870 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2871 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2872 .collect();
2873 let mut sorted = names.clone();
2874 sorted.sort();
2875 let mut theirs = found;
2876 theirs.sort();
2877 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2878 }
2879
2880 fn windows() -> TargetInfo {
2882 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2883 }
2884
2885 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2887 let file = object::File::parse(bytes).expect("a readable object");
2888 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2889 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2890 }
2891
2892 #[test]
2893 fn a_windows_target_is_written_rather_than_refused() {
2894 let text = calling("puts");
2895 let bytes =
2896 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2897 .expect("an object");
2898 let file = object::File::parse(&bytes[..]).expect("a readable object");
2899 assert_eq!(file.format(), BinaryFormat::Coff);
2900 let section = file.section_by_name(".text").expect("a text section");
2901 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2902 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2903 assert!(names.contains(&"f"), "{names:?}");
2904 assert!(names.contains(&"puts"), "{names:?}");
2905 }
2906
2907 #[test]
2915 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2916 for (after, typ) in [
2917 (0, pe::IMAGE_REL_AMD64_REL32),
2918 (1, pe::IMAGE_REL_AMD64_REL32_1),
2919 (4, pe::IMAGE_REL_AMD64_REL32_4),
2920 (5, pe::IMAGE_REL_AMD64_REL32_5),
2921 ] {
2922 let mut text = calling("puts");
2923 text.relocs[0].addend = -4 - i64::from(after);
2926 text.relocs[0].after = after;
2927 text.bytes.resize(6 + after as usize, 0x90);
2928 text.funcs[0].len = text.bytes.len();
2929 let bytes = write(
2930 &text,
2931 &Data::default(),
2932 &[],
2933 &windows(),
2934 Output::default(),
2935 &Info::default(),
2936 )
2937 .expect("an object");
2938 let file = object::File::parse(&bytes[..]).expect("a readable object");
2939 let section = file.section_by_name(".text").expect("a text section");
2940 let (_, reloc) = section.relocations().next().expect("the relocation");
2941 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2942 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2945 }
2946 }
2947
2948 #[test]
2951 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2952 let mut text = calling("puts");
2953 text.relocs[0].addend = 12;
2954 let bytes =
2955 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2956 .expect("an object");
2957 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2958 }
2959
2960 #[test]
2961 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2962 let object = Object {
2963 bytes: vec![0; 8],
2964 size: 8,
2965 align: 8,
2966 relocs: vec![Reloc {
2967 at: 0,
2968 symbol: "y".to_owned(),
2969 kind: Reference::Address { bytes: 8 },
2970 addend: 0,
2971 after: 0,
2972 }],
2973 ..variable("p", Place::Written)
2974 };
2975 let data = Data {
2976 apart: Vec::new(),
2977 exports: Vec::new(),
2978 weak: Vec::new(),
2979 objects: vec![object],
2980 };
2981 let bytes =
2982 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2983 .expect("an object");
2984 let file = object::File::parse(&bytes[..]).expect("a readable object");
2985 let section = file.section_by_name(".data").expect("a data section");
2986 let (_, reloc) = section.relocations().next().expect("the relocation");
2987 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2988 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2989 }
2990
2991 #[test]
2995 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
2996 let pointer = Object {
2997 bytes: vec![0; 8],
2998 size: 8,
2999 align: 8,
3000 relocs: vec![Reloc {
3001 at: 0,
3002 symbol: "environ".to_owned(),
3003 kind: Reference::Address { bytes: 8 },
3004 addend: 0,
3005 after: 0,
3006 }],
3007 ..variable(".refptr.environ", Place::Pointer)
3008 };
3009 let data = Data { objects: vec![pointer], ..Data::default() };
3010 let bytes =
3011 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3012 .expect("an object");
3013 let file = object::File::parse(&bytes[..]).expect("a readable object");
3014 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
3015 let SectionFlags::Coff { characteristics } = section.flags() else {
3016 panic!("a COFF section has COFF flags");
3017 };
3018 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
3019 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
3021 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
3022 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
3023 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
3024 assert_eq!(comdat.kind(), ComdatKind::Any);
3025 assert_eq!(comdat.name(), Ok(".refptr.environ"));
3026 let (_, reloc) = section.relocations().next().expect("the address it holds");
3027 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
3028 }
3029
3030 #[test]
3034 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
3035 let exports = vec![
3036 Export { name: "offered".to_owned(), kind: Offer::Function },
3037 Export { name: "count".to_owned(), kind: Offer::Variable },
3038 Export { name: "kept".to_owned(), kind: Offer::Hidden },
3039 ];
3040 let data = Data { exports, ..Data::default() };
3041 let bytes =
3042 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3043 .expect("an object");
3044 let file = object::File::parse(&bytes[..]).expect("a readable object");
3045 let section = file.section_by_name(".drectve").expect("the options section");
3046 assert_eq!(
3047 section.data().expect("the options"),
3048 b" -export:offered -export:count,data -exclude-symbols:kept"
3049 );
3050 let SectionFlags::Coff { characteristics } = section.flags() else {
3051 panic!("a COFF section has COFF flags");
3052 };
3053 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
3054 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
3055
3056 let none = write(
3057 &Text::default(),
3058 &Data::default(),
3059 &[],
3060 &windows(),
3061 Output::default(),
3062 &Info::default(),
3063 )
3064 .expect("an object");
3065 let file = object::File::parse(&none[..]).expect("a readable object");
3066 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3067 }
3068
3069 #[test]
3072 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3073 for local in [false, true] {
3074 let data = Data {
3075 apart: Vec::new(),
3076 exports: Vec::new(),
3077 weak: Vec::new(),
3078 objects: vec![variable("p", Place::RelocReadOnly { local })],
3079 };
3080 let bytes = write(
3081 &Text::default(),
3082 &data,
3083 &[],
3084 &windows(),
3085 Output::default(),
3086 &Info::default(),
3087 )
3088 .expect("an object");
3089 let file = object::File::parse(&bytes[..]).expect("a readable object");
3090 assert!(file.section_by_name(".rdata").is_some(), "{local}");
3091 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3092 }
3093 }
3094
3095 #[test]
3098 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3099 let text = calling("puts");
3100 let output = Output { property: Property { features: 3 }, ..Output::default() };
3101 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3102 .expect("an object");
3103 let file = object::File::parse(&bytes[..]).expect("a readable object");
3104 assert!(file.section_by_name(".note.GNU-stack").is_none());
3105 assert!(file.section_by_name(".note.gnu.property").is_none());
3106 }
3107
3108 #[test]
3113 fn what_this_format_cannot_say_is_refused_by_name() {
3114 let ordinary = Text::default();
3115 let empty = Data::default();
3116
3117 let mut thread = Data::default();
3118 thread.objects.push(variable("t", Place::Thread { zero: true }));
3119
3120 let mut gathered = Data::default();
3121 gathered
3122 .objects
3123 .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3124
3125 let mut table = calling("puts");
3126 table.relocs[0].kind = Reference::Got;
3127
3128 let mut room = calling("puts");
3129 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3130
3131 let cases: [(&str, &Text, &Data); 4] = [
3132 ("thread-local", &ordinary, &thread),
3133 ("startup", &ordinary, &gathered),
3134 ("table", &table, &empty),
3135 ("patcher", &room, &empty),
3136 ];
3137 for (what, text, data) in cases {
3138 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3139 .expect_err("something this format cannot write");
3140 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3141 }
3142 }
3143
3144 #[test]
3147 fn a_thread_local_variable_goes_in_the_tls_section() {
3148 let mut thread = Data::default();
3149 thread.objects.push(variable("t", Place::Thread { zero: false }));
3150 let bytes =
3151 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3152 .expect("an object");
3153 let file = object::File::parse(&bytes[..]).expect("a readable object");
3154 assert!(file.section_by_name(".tls$").is_some());
3155 }
3156
3157 #[test]
3161 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3162 let mut text = calling("puts");
3163 text.funcs[0].visibility = Visibility::Hidden;
3164 let bytes =
3165 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3166 .expect("an object");
3167 let file = object::File::parse(&bytes[..]).expect("a readable object");
3168 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3169 assert!(symbol.is_global(), "a name others may use either way");
3170 }
3171
3172 #[test]
3173 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3174 let text = calling("puts");
3175 let data = Data {
3176 apart: Vec::new(),
3177 exports: Vec::new(),
3178 weak: Vec::new(),
3179 objects: vec![variable("shared", Place::Written)],
3180 };
3181 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3182 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3183 }
3184
3185 #[test]
3189 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3190 let text = calling("puts");
3191 for triple in [
3192 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3193 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3194 ] {
3195 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3196 .expect_err("no writer");
3197 assert!(matches!(error, Error::Format { .. }), "{error:?}");
3198 }
3199 }
3200
3201 fn i386() -> TargetInfo {
3203 TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3204 }
3205
3206 #[test]
3213 fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3214 let mut text = calling("puts");
3215 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3216 text.funcs[0].start = 3;
3217 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3218 text.relocs[0].at = 4;
3219 let data = Data {
3220 objects: vec![Object {
3221 name: "p".to_owned(),
3222 bytes: vec![0; 4],
3223 size: 4,
3224 align: 4,
3225 place: Place::Written,
3226 binding: Binding::Global,
3227 visibility: Visibility::Default,
3228 relocs: vec![Reloc {
3229 at: 0,
3230 symbol: "x".to_owned(),
3231 kind: Reference::Address { bytes: 4 },
3232 addend: 12,
3233 after: 0,
3234 }],
3235 }],
3236 ..Data::default()
3237 };
3238 let property = Property { features: Property::IBT | Property::SHSTK };
3239 let output = Output { property, ..Output::default() };
3240 let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3241 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3242 assert_eq!(file.architecture(), Architecture::I386);
3243 assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3244 assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3245
3246 let code = file.section_by_name(".text").expect("a text section");
3248 let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3249 panic!("one relocation in the text")
3250 };
3251 assert_eq!(*at, 4);
3252 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3253 assert!(reloc.has_implicit_addend());
3254 assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3255
3256 let variable = file.section_by_name(".data").expect("a data section");
3258 let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3259 panic!("one relocation in the data")
3260 };
3261 assert_eq!(*at, 0);
3262 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3263 assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3264
3265 let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3267 assert_eq!(record.size(), 4);
3268 assert_eq!(record.align(), 4);
3269 let index = code.index().0;
3270 assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3271 let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3272 panic!("one address in the record")
3273 };
3274 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3275
3276 let note = file.section_by_name(".note.gnu.property").expect("the note");
3278 assert_eq!(note.align(), 4);
3279 let want: Vec<u8> = [
3280 4u32,
3281 12,
3282 5,
3283 u32::from_le_bytes(*b"GNU\0"),
3284 Property::X86_FEATURES,
3285 4,
3286 Property::IBT | Property::SHSTK,
3287 ]
3288 .iter()
3289 .flat_map(|word| word.to_le_bytes())
3290 .collect();
3291 assert_eq!(note.data().expect("the bytes"), &want[..]);
3292 }
3293
3294 #[test]
3297 fn an_i386_address_less_three_gigabytes_wraps_in_its_four_bytes() {
3298 let object = |bytes: u8| Object {
3299 name: "cr3".to_owned(),
3300 bytes: vec![0; usize::from(bytes)],
3301 size: u64::from(bytes),
3302 align: u64::from(bytes),
3303 place: Place::Written,
3304 binding: Binding::Global,
3305 visibility: Visibility::Default,
3306 relocs: vec![Reloc {
3307 at: 0,
3308 symbol: "swapper_pg_dir".to_owned(),
3309 kind: Reference::Address { bytes },
3310 addend: -0xC000_0000,
3311 after: 0,
3312 }],
3313 };
3314 let data = Data { objects: vec![object(4)], ..Data::default() };
3315 let bytes =
3316 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default())
3317 .expect("an object");
3318 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3319 let variable = file.section_by_name(".data").expect("a data section");
3320 assert_eq!(variable.data().expect("the bytes"), &0x4000_0000u32.to_le_bytes());
3321
3322 let data = Data { objects: vec![object(2)], ..Data::default() };
3323 let refused =
3324 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default());
3325 assert!(refused.is_err(), "two bytes cannot hold a name less three gigabytes");
3326 }
3327
3328 #[test]
3331 fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3332 let info = Info {
3333 chunks: vec![Chunk {
3334 name: ".debug_info".to_owned(),
3335 bytes: vec![0; 64],
3336 relocs: vec![Reloc {
3337 at: 8,
3338 symbol: "f".to_owned(),
3339 kind: Reference::Address { bytes: 4 },
3340 addend: 7,
3341 after: 0,
3342 }],
3343 }],
3344 compress: Compress::Zlib,
3345 };
3346 let bytes =
3347 write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3348 .expect("an object");
3349 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3350 let section = file.section_by_name(".debug_info").expect("the debug section");
3351 let packed =
3352 SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3353 assert_ne!(section.flags(), packed);
3354 let data = section.data().expect("the bytes");
3355 assert_eq!(data.len(), 64);
3356 assert_eq!(&data[8..12], &7u32.to_le_bytes());
3357 }
3358
3359 fn i386_windows() -> TargetInfo {
3361 TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3362 }
3363
3364 #[test]
3373 fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3374 let mut text = calling("puts");
3375 text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3376 text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3377 text.relocs.push(Reloc {
3378 at: 7,
3379 symbol: "__imp_GetTickCount".to_owned(),
3380 kind: Reference::Call,
3381 addend: -4,
3382 after: 0,
3383 });
3384 let data = Data {
3385 objects: vec![Object {
3386 name: "p".to_owned(),
3387 bytes: vec![0; 12],
3388 size: 12,
3389 align: 4,
3390 place: Place::Written,
3391 binding: Binding::Global,
3392 visibility: Visibility::Default,
3393 relocs: vec![
3394 Reloc {
3395 at: 0,
3396 symbol: "x".to_owned(),
3397 kind: Reference::Address { bytes: 4 },
3398 addend: 12,
3399 after: 0,
3400 },
3401 Reloc {
3402 at: 4,
3403 symbol: "f".to_owned(),
3404 kind: Reference::Image,
3405 addend: 0,
3406 after: 0,
3407 },
3408 Reloc {
3409 at: 8,
3410 symbol: "x".to_owned(),
3411 kind: Reference::Away,
3412 addend: 0,
3413 after: 0,
3414 },
3415 ],
3416 }],
3417 ..Data::default()
3418 };
3419 let aliases = [Alias {
3420 name: "g".to_owned(),
3421 target: "f".to_owned(),
3422 binding: Binding::Global,
3423 visibility: Visibility::Default,
3424 ifunc: false,
3425 }];
3426 let target = i386_windows();
3427 let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3428 .expect("an object");
3429 let file = object::File::parse(&bytes[..]).expect("a readable object");
3430 assert_eq!(file.format(), BinaryFormat::Coff);
3431 assert_eq!(file.architecture(), Architecture::I386);
3432 assert!(!file.is_64());
3433
3434 let named = |name: &str| file.symbol_by_name(name).is_some();
3435 for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3436 assert!(named(name), "{name}");
3437 }
3438 for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3439 assert!(!named(name), "{name}");
3440 }
3441
3442 let code = file.section_by_name(".text").expect("a text section");
3443 let relocs: Vec<_> = code.relocations().collect();
3444 assert_eq!(relocs.len(), 2);
3445 for (at, reloc) in &relocs {
3446 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3447 let at = *at as usize;
3448 assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3449 }
3450
3451 let variable = file.section_by_name(".data").expect("a data section");
3452 let types: Vec<_> = variable
3453 .relocations()
3454 .map(|(at, reloc)| match reloc.flags() {
3455 RelocationFlags::Coff { typ } => (at, typ),
3456 flags => panic!("{flags:?}"),
3457 })
3458 .collect();
3459 assert_eq!(
3460 types,
3461 [
3462 (0, pe::IMAGE_REL_I386_DIR32),
3463 (4, pe::IMAGE_REL_I386_DIR32NB),
3464 (8, pe::IMAGE_REL_I386_REL32)
3465 ]
3466 );
3467 let image = variable.data().expect("the bytes");
3470 assert_eq!(&image[0..4], &12u32.to_le_bytes());
3471 assert_eq!(&image[8..12], &4u32.to_le_bytes());
3472
3473 let listed = defines(&text, &data, &aliases, &target).expect("a list");
3475 assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3476 }
3477
3478 #[test]
3481 fn an_i386_windows_object_has_no_unwind_table() {
3482 let mut text = calling("puts");
3483 text.unwind.bytes = vec![0; 12];
3484 let bytes = write(
3485 &text,
3486 &Data::default(),
3487 &[],
3488 &i386_windows(),
3489 Output::default(),
3490 &Info::default(),
3491 )
3492 .expect("an object");
3493 let file = object::File::parse(&bytes[..]).expect("a readable object");
3494 assert!(file.section_by_name(".pdata").is_none());
3495 assert!(file.section_by_name(".xdata").is_none());
3496 assert!(file.section_by_name(".eh_frame").is_none());
3497 }
3498
3499 #[test]
3502 fn an_i386_windows_object_says_it_is_safe_for_safeseh() {
3503 let write_for = |target: &TargetInfo| {
3504 write(
3505 &calling("puts"),
3506 &Data::default(),
3507 &[],
3508 target,
3509 Output::default(),
3510 &Info::default(),
3511 )
3512 .expect("an object")
3513 };
3514 let bytes = write_for(&i386_windows());
3515 let file = object::File::parse(&bytes[..]).expect("a readable object");
3516 let feat = file.symbol_by_name("@feat.00").expect("the feature symbol");
3517 let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
3519 let raw = coff.symbol_by_name("@feat.00").expect("the feature symbol");
3520 assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
3521 assert_eq!(feat.section(), object::SymbolSection::Absolute);
3522 assert!(feat.is_local());
3523 let bytes = write_for(&windows());
3524 let file = object::File::parse(&bytes[..]).expect("a readable object");
3525 assert!(file.symbol_by_name("@feat.00").is_none());
3526 }
3527
3528 #[test]
3531 fn i386_windows_has_no_eight_byte_or_table_relocations() {
3532 for kind in [
3533 Reference::Address { bytes: 8 },
3534 Reference::AwayWide,
3535 Reference::Got,
3536 Reference::GotOffset,
3537 Reference::Slot,
3538 Reference::Thread,
3539 ] {
3540 assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3541 }
3542 assert_eq!(
3543 Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3544 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3545 );
3546 assert_eq!(
3547 Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3548 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3549 "an address an instruction holds"
3550 );
3551 }
3552}