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.ahead();
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.ahead();
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 let mut listed: Vec<(&str, object::write::SectionId)> = Vec::new();
536 if !text.mcount.is_empty() {
537 let flags =
538 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(|| {
539 Error::Refused { why: "no relocation holds an address here".to_owned() }
540 })?;
541 for (call, name) in &text.mcount {
542 let call = *call;
543 let id = match listed.iter().find(|(made, _)| *made == name.as_str()) {
544 Some(&(_, id)) => id,
545 None => {
546 let made = name.as_bytes().to_vec();
547 let id = obj.add_section(Vec::new(), made, SectionKind::ReadOnlyData);
548 listed.push((name, id));
549 id
550 }
551 };
552 let after = text.funcs.partition_point(|func| func.start <= call);
553 let Some(index) = after.checked_sub(1) else {
554 let why = format!("a profiler call at {call} is in front of every function");
555 return Err(Error::Refused { why });
556 };
557 let func = &text.funcs[index];
558 let (section, at) = if sections.functions {
559 let base = func.start - func.ahead();
560 (split[index].0, call - base)
561 } else {
562 (whole, call)
563 };
564 let offset =
565 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
566 let symbol = obj.section_symbol(section);
567 relocate(&mut obj, id, Relocation { offset, symbol, addend: at as i64, flags })?;
568 }
569 }
570
571 let mut placed = Vec::with_capacity(data.objects.len());
576 let mut named = Map::default();
580 for object in &data.objects {
581 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
582 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
586 obj.section_symbol(section);
587 }
588 let id = obj.add_symbol(Symbol {
589 name: spell(&object.name),
590 value: if object.place == Place::Merged { object.align } else { offset },
593 size: object.size,
594 kind: match object.place {
599 Place::Thread { .. } => SymbolKind::Tls,
600 _ => SymbolKind::Data,
601 },
602 scope: scope_of(object.binding),
603 weak: object.binding == Binding::Weak,
604 section,
605 flags: SymbolFlags::None,
606 });
607 flavour.see(&mut obj, id, object.binding, object.visibility);
608 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
612 obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
613 }
614 symbols.insert(object.name.clone(), id);
615 placed.push((section.id(), offset));
616 }
617
618 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
621
622 for apart in &data.apart {
626 let (Some(section), offset) = placed[apart.object] else { continue };
627 let value = distance(&obj, &symbols, &places, apart)?;
628 let bytes = usize::from(apart.bytes);
629 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
630 let at = at + apart.at;
631 let image = obj.section_mut(section).data_mut();
632 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
633 }
634
635 let written: BTreeMap<&str, usize> = text
643 .funcs
644 .iter()
645 .map(|func| func.name.as_str())
646 .chain(data.objects.iter().map(|object| object.name.as_str()))
647 .enumerate()
648 .map(|(at, name)| (name, at))
649 .collect();
650 let mut by_target: Vec<&Alias> = aliases.iter().collect();
651 by_target
652 .sort_by_key(|alias| written.get(alias.target.as_str()).copied().unwrap_or(usize::MAX));
653 for alias in by_target {
654 let Some(&id) = symbols.get(&alias.target) else {
655 let why =
656 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
657 return Err(Error::Refused { why });
658 };
659 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
660 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
661 let id = obj.add_symbol(Symbol {
662 name: spell(&alias.name),
663 value,
664 size,
665 kind,
666 scope: scope_of(alias.binding),
667 weak: alias.binding == Binding::Weak,
668 section,
669 flags: SymbolFlags::None,
670 });
671 flavour.see(&mut obj, id, alias.binding, alias.visibility);
672 if alias.ifunc {
673 if flavour != Flavour::Elf {
674 let why = format!("'{}' is an indirect function, which only ELF has", alias.name);
675 return Err(Error::Refused { why });
676 }
677 elf::indirect(&mut obj, id);
678 }
679 symbols.insert(alias.name.clone(), id);
680 }
681
682 let weak: Set<&str> = data.weak.iter().map(String::as_str).collect();
689 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
690 let thread: Set<&str> = relocs()
695 .filter(|reloc| reloc.kind == Reference::Thread)
696 .map(|reloc| reloc.symbol.as_str())
697 .collect();
698 let wanted: Vec<&String> =
699 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
700 for name in wanted {
701 if symbols.contains_key(name) || tables.contains_key(name) || places.contains_key(name) {
702 continue;
703 }
704 let id = obj.add_symbol(Symbol {
705 name: spell(name),
706 value: 0,
707 size: 0,
708 kind: if thread.contains(name.as_str()) {
718 SymbolKind::Tls
719 } else {
720 SymbolKind::Unknown
721 },
722 scope: SymbolScope::Dynamic,
723 weak: weak.contains(name.as_str()),
724 section: SymbolSection::Undefined,
725 flags: SymbolFlags::None,
726 });
727 symbols.insert(name.clone(), id);
728 }
729
730 for reloc in &text.relocs {
731 let (section, at) = if sections.functions {
736 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
737 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
738 let why = format!("a relocation at {} is in front of every function", reloc.at);
739 return Err(Error::Refused { why });
740 };
741 let base = func.start - func.ahead();
744 (split[after - 1].0, (reloc.at - base) as u64)
745 } else {
746 (whole, reloc.at as u64)
747 };
748 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
752 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
753 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
754 })?;
755 let symbol = obj.section_symbol(table);
756 let addend = reloc.addend + offset as i64;
757 relocate(&mut obj, section, Relocation { offset: at, symbol, addend, flags })?;
758 continue;
759 }
760 add(&mut obj, section, at, reloc, &symbols, &places, flavour)?;
761 }
762
763 let seh_free = flavour == Flavour::Coff && machine == Architecture::I386;
773 if !text.unwind.bytes.is_empty() && !seh_free {
774 let ((name, align), second) = flavour.tables();
775 let align = if machine == Architecture::I386 { 4 } else { align };
778 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
779 obj.append_section_data(frames, &text.unwind.bytes, align);
780 let mut described = Map::default();
785 if !text.unwind.info.is_empty() {
786 let Some((name, align)) = second else {
787 let why = "an unwind table here is one section and it was given two".to_owned();
788 return Err(Error::Refused { why });
789 };
790 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
791 obj.append_section_data(codes, &text.unwind.info, align);
792 for label in &text.unwind.labels {
793 let id = obj.add_symbol(Symbol {
794 name: label.name.clone().into_bytes(),
795 value: label.at as u64,
796 size: 0,
797 kind: SymbolKind::Label,
798 scope: SymbolScope::Compilation,
799 weak: false,
800 section: SymbolSection::Section(codes),
801 flags: SymbolFlags::None,
802 });
803 described.insert(label.name.clone(), id);
804 }
805 }
806 if !text.unwind.except.is_empty() {
811 let except =
812 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
813 obj.append_section_data(except, &text.unwind.except, 4);
814 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
815 }
816 for reloc in &text.unwind.relocs {
817 let found = described.get(&reloc.symbol).or_else(|| {
818 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
822 if ours { symbols.get(&reloc.symbol) } else { None }
823 });
824 let (symbol, addend) = match found {
825 Some(&id) => (id, reloc.addend),
829 None => {
843 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
844 let Some((section, at)) = found.map(|i| split[i]) else {
845 let why = format!(
846 "'{}' has an unwind record and is not a function here",
847 reloc.symbol
848 );
849 return Err(Error::Refused { why });
850 };
851 (obj.section_symbol(section), reloc.addend + at as i64)
855 }
856 };
857 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
858 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
859 })?;
860 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
861 relocate(&mut obj, frames, record)?;
862 }
863 }
864 let mut named = Map::default();
873 for chunk in &info.chunks {
874 let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
877 info.compress
878 } else {
879 Compress::None
880 };
881 let id = crate::zlib::debug_section(&mut obj, chunk, how);
882 named.insert(chunk.name.as_str(), id);
883 }
884 for chunk in &info.chunks {
885 let section = named[chunk.name.as_str()];
886 for reloc in &chunk.relocs {
887 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
888 Some(&id) => (obj.section_symbol(id), reloc.addend),
891 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
896 Some(which) => {
897 let (section, at) = split[which];
898 (obj.section_symbol(section), reloc.addend + at as i64)
899 }
900 None => {
906 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
907 let Some(which) = found else {
908 let why = format!(
909 "'{}' is named by the debug information and is not defined here",
910 reloc.symbol
911 );
912 return Err(Error::Refused { why });
913 };
914 match placed[which] {
915 (Some(section), at) => {
916 (obj.section_symbol(section), reloc.addend + at as i64)
917 }
918 (None, _) => (symbols[&reloc.symbol], reloc.addend),
919 }
920 }
921 },
922 };
923 let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
924 let flags = flavour
925 .reloc(machine, kind, reloc.after)
926 .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
927 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
928 relocate(&mut obj, section, record)?;
929 }
930 }
931 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
932 let Some(section) = section else { continue };
933 for reloc in &object.relocs {
934 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, &places, flavour)?;
935 }
936 }
937
938 if !data.exports.is_empty() {
942 let options: String = data.exports.iter().map(Export::option).collect();
943 let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
944 obj.append_section_data(id, options.as_bytes(), 1);
945 }
946
947 flavour.property(&mut obj, property);
951
952 if let Some(ident) = ident.filter(|_| flavour == Flavour::Elf) {
954 let id = obj.add_section(Vec::new(), b".comment".to_vec(), SectionKind::OtherString);
955 let bytes = [&[0][..], ident.as_bytes(), &[0]].concat();
956 obj.append_section_data(id, &bytes, 1);
957 }
958
959 flavour.marker(&mut obj);
962
963 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
964 flavour.finish(&mut bytes, &ordered);
965 Ok(bytes)
966}
967
968type Places = BTreeMap<String, (object::write::SectionId, u64)>;
971
972fn distance(
980 obj: &Writer<'_>,
981 symbols: &BTreeMap<String, SymbolId>,
982 places: &Places,
983 apart: &Apart,
984) -> Result<i64, Error> {
985 let find = |name: &str| match (symbols.get(name), places.get(name)) {
986 (Some(&id), _) => Ok((obj.symbol(id).section, obj.symbol(id).value)),
987 (None, Some(&(section, at))) => Ok((SymbolSection::Section(section), at)),
988 (None, None) => {
989 Err(Error::Refused { why: format!("'{name}' is measured from and is not here") })
990 }
991 };
992 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
993 if to.0 != from.0 {
994 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
995 return Err(Error::Refused { why });
996 }
997 let value = (to.1 as i64).wrapping_sub(from.1 as i64).wrapping_add(apart.addend);
998 let bits = u32::from(apart.bytes) * 8;
999 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
1000 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
1001 return Err(Error::Refused { why });
1002 }
1003 Ok(value)
1004}
1005
1006fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
1019 let why = |why: String| Err(Error::Refused { why });
1020 if text.funcs.iter().any(|func| func.patch.is_some()) {
1021 return why("a record of where a patcher's room is has no section flags here".to_owned());
1022 }
1023 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
1024 if matches!(
1025 reloc.kind,
1026 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
1027 ) {
1028 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
1029 }
1030 }
1031 for object in &data.objects {
1032 if matches!(object.place, Place::Thread { zero: true }) {
1033 return why(format!(
1034 "'{}' is zeroed thread-local storage, which is not here",
1035 object.name
1036 ));
1037 }
1038 let Place::Named(name, _) = &object.place else { continue };
1039 if Array::of(name).is_some() {
1040 return why(format!("'{name}' is not a list the startup code here gathers"));
1041 }
1042 }
1043 Ok(())
1044}
1045
1046pub fn defines(
1069 text: &Text,
1070 data: &Data,
1071 aliases: &[Alias],
1072 target: &TargetInfo,
1073) -> Result<Vec<String>, Error> {
1074 let Some((flavour, machine)) = written(target) else {
1075 return Err(Error::Format { triple: target.tuple.to_string() });
1076 };
1077 let spell = |name: &String| flavour.spell(machine, name);
1078 let names = text
1079 .funcs
1080 .iter()
1081 .filter(|func| func.binding != Binding::Local)
1082 .map(|func| spell(&func.name))
1083 .chain(
1084 data.objects
1085 .iter()
1086 .filter(|object| object.binding != Binding::Local)
1087 .map(|object| spell(&object.name)),
1088 )
1089 .chain(
1090 aliases
1091 .iter()
1092 .filter(|alias| alias.binding != Binding::Local)
1093 .map(|alias| spell(&alias.name)),
1094 )
1095 .collect();
1096 Ok(names)
1097}
1098
1099fn put(
1106 obj: &mut Writer<'_>,
1107 object: &Object,
1108 named: &mut Map<String, object::write::SectionId>,
1109 sections: Sections,
1110 flavour: Flavour,
1111) -> (SymbolSection, u64) {
1112 if sections.data {
1118 if let Some(name) = object.place.split(&object.name) {
1119 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
1120 let offset = if carries_no_bytes(&object.place) {
1121 obj.append_section_bss(section, object.size, object.align)
1122 } else {
1123 obj.append_section_data(section, &object.bytes, object.align)
1124 };
1125 return (SymbolSection::Section(section), offset);
1126 }
1127 }
1128 let section = match &object.place {
1129 Place::Written => obj.section_id(StandardSection::Data),
1130 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
1131 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
1137 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
1138 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
1139 },
1140 Place::Zero => obj.section_id(StandardSection::UninitializedData),
1141 Place::Strings { align } => {
1144 made(obj, named, &Place::strings(*align), SectionKind::ReadOnlyString)
1145 }
1146 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
1147 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
1148 Place::Merged => return (SymbolSection::Common, 0),
1149 Place::NoInit => made(obj, named, ".noinit", SectionKind::UninitializedData),
1152 Place::Persistent => made(obj, named, ".persistent", SectionKind::Data),
1153 Place::Named(name, _) => {
1160 let section = made(obj, named, name, kind_of(&object.place));
1161 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
1162 obj.section_mut(section).flags = flags;
1163 }
1164 section
1165 }
1166 Place::Pointer => {
1170 let name = format!(".rdata${}", object.name);
1171 made(obj, named, &name, SectionKind::ReadOnlyData)
1172 }
1173 };
1174 let offset = if carries_no_bytes(&object.place) {
1175 obj.append_section_bss(section, object.size, object.align)
1176 } else {
1177 obj.append_section_data(section, &object.bytes, object.align)
1178 };
1179 (SymbolSection::Section(section), offset)
1180}
1181
1182fn tables(
1195 obj: &mut Writer<'_>,
1196 text: &Text,
1197 split: &[(object::write::SectionId, u64)],
1198 named: &mut Map<String, object::write::SectionId>,
1199 sections: Sections,
1200 flavour: Flavour,
1201) -> Result<Map<String, (object::write::SectionId, u64)>, Error> {
1202 let mut placed = Map::default();
1203 if text.tables.is_empty() {
1204 return Ok(placed);
1205 }
1206 if flavour != Flavour::Elf {
1207 let why = "a jump table outside the code is written on ELF only".to_owned();
1208 return Err(Error::Refused { why });
1209 }
1210 let machine = obj.architecture();
1211 let flags = flavour.reloc(machine, Reference::Away, 0).ok_or_else(|| Error::Refused {
1212 why: "no relocation is a distance from where it is written".to_owned(),
1213 })?;
1214 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
1216 for table in &text.tables {
1217 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1218 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1219 })?;
1220 let section = if sections.data {
1221 let name = format!(".rodata.{}", func.name);
1222 made(obj, named, &name, SectionKind::ReadOnlyData)
1223 } else {
1224 obj.section_id(StandardSection::ReadOnlyData)
1225 };
1226 let (width, flags) = if table.absolute {
1229 let reference = Reference::Address { bytes: pointer };
1230 let wide = flavour.reloc(machine, reference, 0).ok_or_else(|| Error::Refused {
1231 why: format!("no relocation is an address in {pointer} bytes"),
1232 })?;
1233 (usize::from(pointer), wide)
1234 } else {
1235 (4, flags)
1236 };
1237 let offset =
1238 obj.append_section_data(section, &vec![0; width * table.cells.len()], width as u64);
1239 placed.insert(table.name.clone(), (section, offset));
1240 let (code, at) = split[table.func];
1241 let symbol = obj.section_symbol(code);
1242 for (index, &cell) in table.cells.iter().enumerate() {
1243 let place = (width * index) as u64;
1244 let addend = at as i64 + cell as i64 + if table.absolute { 0 } else { place as i64 };
1245 let record = Relocation { offset: offset + place, symbol, addend, flags };
1246 relocate(obj, section, record)?;
1247 }
1248 }
1249 Ok(placed)
1250}
1251
1252fn carries_no_bytes(place: &Place) -> bool {
1259 matches!(
1260 place,
1261 Place::Zero | Place::Thread { zero: true } | Place::Named(_, Holds::Zero) | Place::NoInit
1262 )
1263}
1264
1265fn made(
1273 obj: &mut Writer<'_>,
1274 named: &mut Map<String, object::write::SectionId>,
1275 name: &str,
1276 kind: SectionKind,
1277) -> object::write::SectionId {
1278 if let Some(section) = named.get(name) {
1279 return *section;
1280 }
1281 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1282 named.insert(name.to_owned(), section);
1283 section
1284}
1285
1286fn kind_of(place: &Place) -> SectionKind {
1298 match place {
1299 Place::ReadOnly | Place::Pointer | Place::Named(_, Holds::ReadOnly) => {
1300 SectionKind::ReadOnlyData
1301 }
1302 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1303 Place::Strings { .. } => SectionKind::ReadOnlyString,
1304 Place::Zero | Place::Named(_, Holds::Zero) | Place::NoInit => {
1305 SectionKind::UninitializedData
1306 }
1307 Place::Thread { zero: false } => SectionKind::Tls,
1308 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1309 Place::Written | Place::Merged | Place::Named(_, Holds::Written) | Place::Persistent => {
1310 SectionKind::Data
1311 }
1312 }
1313}
1314
1315fn add(
1322 obj: &mut Writer<'_>,
1323 section: object::write::SectionId,
1324 at: u64,
1325 reloc: &Reloc,
1326 symbols: &BTreeMap<String, SymbolId>,
1327 places: &Places,
1328 flavour: Flavour,
1329) -> Result<(), Error> {
1330 let flags = flavour
1331 .reloc(obj.architecture(), reloc.kind, reloc.after)
1332 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1333 let (symbol, addend) = match places.get(&reloc.symbol) {
1335 Some(&(held, offset)) => (obj.section_symbol(held), reloc.addend + offset as i64),
1336 None => (symbols[&reloc.symbol], reloc.addend),
1337 };
1338 relocate(obj, section, Relocation { offset: at, symbol, addend, flags })
1339}
1340
1341pub(crate) fn relocate(
1360 obj: &mut Writer<'_>,
1361 section: object::write::SectionId,
1362 mut relocation: Relocation,
1363) -> Result<(), Error> {
1364 if let (Architecture::I386, RelocationFlags::Elf { r_type }) =
1365 (obj.architecture(), relocation.flags)
1366 {
1367 let Some(width) = elf::width_i386(r_type) else {
1368 let why = format!("relocation type {} has no width this writer knows", r_type.0);
1369 return Err(Error::Refused { why });
1370 };
1371 let addend = relocation.addend;
1372 let bits = 8 * width as u32;
1373 let least = if width == 4 { -(1i64 << bits) } else { -(1i64 << (bits - 1)) };
1379 if addend < least || addend >= 1i64 << bits {
1380 let why =
1381 format!("{addend} added to a name, and there are {width} bytes to keep it in");
1382 return Err(Error::Refused { why });
1383 }
1384 let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1385 let data = obj.section_mut(section).data_mut();
1386 let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1387 let why = format!("a relocation at {at} is past the end of its section");
1388 return Err(Error::Refused { why });
1389 };
1390 place.copy_from_slice(&addend.to_le_bytes()[..width]);
1391 relocation.addend = 0;
1392 }
1393 obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1394}
1395
1396fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1402 let flavour = Flavour::of(target)?;
1403 let machine = flavour.machine(target.tuple.arch())?;
1404 (machine != Architecture::Aarch64).then_some((flavour, machine))
1405}
1406
1407pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1420 match binding {
1421 Binding::Local => SymbolScope::Compilation,
1422 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1423 }
1424}
1425
1426#[cfg(test)]
1427mod tests {
1428 use super::*;
1429
1430 use object::read::elf::Sym as _;
1431 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1432 use object::{elf, pe};
1433 use rucc_target::{Arch, Env, Os, Triple};
1434
1435 use crate::elf::PATCHABLE;
1436 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1437
1438 fn target() -> TargetInfo {
1440 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1441 }
1442
1443 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1448 Extent {
1449 name,
1450 start,
1451 len,
1452 align: crate::FUNC_ALIGN,
1453 binding,
1454 visibility: Visibility::Default,
1455 patch: None,
1456 hooked: 0,
1457 landings: Vec::new(),
1458 }
1459 }
1460
1461 fn calling(name: &str) -> Text {
1463 Text {
1464 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1465 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1466 relocs: vec![Reloc {
1467 at: 1,
1468 symbol: name.to_owned(),
1469 kind: Reference::Call,
1470 addend: -4,
1471 after: 0,
1472 }],
1473 ..Text::default()
1474 }
1475 }
1476
1477 #[test]
1478 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1479 let text = calling("puts");
1480 let bytes =
1481 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1482 .expect("an object");
1483 let file = object::File::parse(&bytes[..]).expect("a readable object");
1484 let section = file.section_by_name(".text").expect("a text section");
1485 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1486 }
1487
1488 #[test]
1489 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1490 let mut text = calling("puts");
1491 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1492 text.bytes.resize(17, 0x90);
1493 let bytes =
1494 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1495 .expect("an object");
1496 let file = object::File::parse(&bytes[..]).expect("a readable object");
1497 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1498 assert_eq!(g.address(), 16);
1499 assert_eq!(g.size(), 1);
1500 assert_eq!(g.kind(), SymbolKind::Text);
1501 assert!(g.is_global(), "nothing said otherwise about this one");
1502 }
1503
1504 #[test]
1505 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1506 let mut text = calling("puts");
1507 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1508 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1509 text.bytes.resize(33, 0x90);
1510 let bytes =
1511 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1512 .expect("an object");
1513 let file = object::File::parse(&bytes[..]).expect("a readable object");
1514 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1515 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1518 assert!(!hidden.is_weak());
1519 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1520 assert!(shared.is_weak(), "a weak function has to be able to lose");
1521 assert!(shared.is_global());
1522 }
1523
1524 #[test]
1541 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1542 let mut text = calling("puts");
1543 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1544 text.funcs[0].start = 3;
1545 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1546 text.relocs[0].at = 4;
1547 let bytes =
1548 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1549 .expect("an object");
1550 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1551 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1552 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1553 assert_eq!(section.align(), 8);
1554 let header = section.elf_section_header();
1555 assert_eq!(
1556 header.sh_flags.get(Endianness::Little),
1557 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1558 );
1559 let index = file.section_by_name(".text").expect("a text section").index().0;
1562 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1563 assert_ne!(index, 0);
1564
1565 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1567 panic!("one address in the record")
1568 };
1569 assert_eq!(*at, 0);
1570 assert_eq!(reloc.addend(), 0);
1571 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1572 }
1573
1574 #[test]
1576 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1577 let text = calling("puts");
1578 let bytes =
1579 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1580 .expect("an object");
1581 let file = object::File::parse(&bytes[..]).expect("a readable object");
1582 assert!(file.section_by_name(PATCHABLE).is_none());
1583 }
1584
1585 #[test]
1591 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1592 let mut text = calling("puts");
1593 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1594 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1595 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1596 text.bytes.resize(17, 0x90);
1597 let output =
1598 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1599 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1600 .expect("an object");
1601 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1602 let links: Vec<usize> = file
1603 .sections()
1604 .filter(|section| section.name() == Ok(PATCHABLE))
1605 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1606 .collect();
1607 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1608 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1609 }
1610
1611 #[test]
1612 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1613 let mut text = calling("puts");
1614 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1615 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1616 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1617 text.bytes.resize(49, 0x90);
1618 let bytes =
1619 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1620 .expect("an object");
1621 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1622 let visibility = |name: &str| {
1623 file.symbols()
1624 .find(|s| s.name() == Ok(name))
1625 .expect("the function")
1626 .elf_symbol()
1627 .st_visibility()
1628 };
1629 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1631 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1632 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1635 }
1636
1637 #[test]
1647 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1648 let mut text = calling("puts");
1649 for (index, (name, seen)) in
1650 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1651 {
1652 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1653 func.visibility = seen;
1654 text.funcs.push(func);
1655 }
1656 text.bytes.resize(49, 0x90);
1657 let mut data = Data::default();
1658 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1659 let mut object = variable(name, Place::Written);
1660 object.visibility = seen;
1661 data.objects.push(object);
1662 }
1663 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1664 .expect("an object");
1665 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1666 let visibility = |name: &str| {
1667 file.symbols()
1668 .find(|s| s.name() == Ok(name))
1669 .expect("the symbol")
1670 .elf_symbol()
1671 .st_visibility()
1672 };
1673 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1674 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1675 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1676 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1677 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1680 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1681 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1682 }
1683
1684 #[test]
1685 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1686 let bytes = write(
1687 &calling("puts"),
1688 &Data::default(),
1689 &[],
1690 &target(),
1691 Output::default(),
1692 &Info::default(),
1693 )
1694 .expect("an object");
1695 let file = object::File::parse(&bytes[..]).expect("a readable object");
1696 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1697 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1698 }
1699
1700 #[test]
1701 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1702 for (reference, wanted) in [
1703 (Reference::Call, elf::R_X86_64_PLT32),
1704 (Reference::Data, elf::R_X86_64_PC32),
1705 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1706 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1707 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1708 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1709 ] {
1710 let mut text = calling("puts");
1711 text.relocs[0].kind = reference;
1712 let bytes =
1713 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1714 .expect("an object");
1715 let file = object::File::parse(&bytes[..]).expect("a readable object");
1716 let section = file.section_by_name(".text").expect("a text section");
1717 let (offset, reloc) = section.relocations().next().expect("one relocation");
1718 assert_eq!(offset, 1);
1719 assert_eq!(reloc.addend(), -4);
1720 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1721 }
1722 }
1723
1724 #[test]
1727 fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1728 for (reference, wanted) in [
1729 (Reference::AwayWide, elf::R_X86_64_PC64),
1730 (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1731 (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1732 ] {
1733 assert_eq!(crate::elf::r_type(reference), Some(wanted));
1734 }
1735 assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1736 }
1737
1738 #[test]
1739 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1740 let mut text = calling("puts");
1741 text.relocs.push(Reloc {
1742 at: 1,
1743 symbol: "puts".to_owned(),
1744 kind: Reference::Call,
1745 addend: -4,
1746 after: 0,
1747 });
1748 let bytes =
1749 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1750 .expect("an object");
1751 let file = object::File::parse(&bytes[..]).expect("a readable object");
1752 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1753 }
1754
1755 #[test]
1756 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1757 let text = calling("f");
1758 let bytes =
1759 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1760 .expect("an object");
1761 let file = object::File::parse(&bytes[..]).expect("a readable object");
1762 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1763 let f = found.next().expect("the function");
1764 assert!(!f.is_undefined(), "the file defines it");
1765 assert!(found.next().is_none(), "and defines it once");
1766 }
1767
1768 #[test]
1769 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1770 let bytes = write(
1771 &calling("puts"),
1772 &Data::default(),
1773 &[],
1774 &target(),
1775 Output::default(),
1776 &Info::default(),
1777 )
1778 .expect("an object");
1779 let file = object::File::parse(&bytes[..]).expect("a readable object");
1780 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1781 assert!(note.data().expect("no bytes").is_empty());
1782 }
1783
1784 #[test]
1791 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1792 let property = Property { features: Property::IBT | Property::SHSTK };
1793 let output = Output { property, ..Output::default() };
1794 let bytes =
1795 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1796 .expect("an object");
1797 let file = object::File::parse(&bytes[..]).expect("a readable object");
1798 let note = file.section_by_name(".note.gnu.property").expect("the note");
1799 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1800 let want: Vec<u8> = [
1801 4u32,
1802 16,
1803 5,
1804 u32::from_le_bytes(*b"GNU\0"),
1805 Property::X86_FEATURES,
1806 4,
1807 Property::IBT | Property::SHSTK,
1808 0,
1809 ]
1810 .iter()
1811 .flat_map(|word| word.to_le_bytes())
1812 .collect();
1813 assert_eq!(note.data().expect("the bytes"), &want[..]);
1814 }
1815
1816 #[test]
1822 fn a_file_built_to_have_nothing_checked_says_nothing() {
1823 let bytes = write(
1824 &calling("puts"),
1825 &Data::default(),
1826 &[],
1827 &target(),
1828 Output::default(),
1829 &Info::default(),
1830 )
1831 .expect("an object");
1832 let file = object::File::parse(&bytes[..]).expect("a readable object");
1833 assert!(file.section_by_name(".note.gnu.property").is_none());
1834 }
1835
1836 #[test]
1845 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1846 let mut text = calling("puts");
1847 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1848 text.bytes.resize(17, 0x90);
1849 text.unwind.bytes = vec![0; 64];
1852 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1853 text.unwind.relocs.push(Reloc {
1854 at,
1855 symbol: name.to_owned(),
1856 kind: Reference::Address { bytes: 8 },
1857 addend: 0,
1858 after: 0,
1859 });
1860 }
1861 let bytes =
1862 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1863 .expect("an object");
1864 let file = object::File::parse(&bytes[..]).expect("a readable object");
1865 let mut found = points_at(&file);
1866 found.sort_unstable();
1867 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1868 }
1869
1870 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1873 let frames = file.section_by_name(".eh_frame").expect("the table");
1874 frames
1875 .relocations()
1876 .map(|(offset, reloc)| {
1877 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1878 panic!("a record points at something that is not a symbol");
1879 };
1880 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1881 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1882 let section = symbol.section_index().expect("a section symbol is in one");
1883 let name = file.section_by_index(section).expect("a readable section");
1884 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1885 })
1886 .collect()
1887 }
1888
1889 #[test]
1902 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1903 let mut text = two();
1904 text.unwind.bytes = vec![0; 64];
1905 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1906 text.unwind.relocs.push(Reloc {
1907 at,
1908 symbol: name.to_owned(),
1909 kind: Reference::Data,
1910 addend: 0,
1911 after: 0,
1912 });
1913 }
1914 let bytes =
1915 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1916 .expect("an object");
1917 let file = object::File::parse(&bytes[..]).expect("a readable object");
1918 let mut whole = points_at(&file);
1919 whole.sort_unstable();
1920 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1921
1922 let sections =
1923 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1924 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1925 .expect("an object");
1926 let file = object::File::parse(&bytes[..]).expect("a readable object");
1927 let mut split = points_at(&file);
1928 split.sort_unstable();
1929 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1930 }
1931
1932 #[test]
1939 fn a_record_about_something_this_file_does_not_define_is_refused() {
1940 let mut text = calling("puts");
1941 text.unwind.bytes = vec![0; 64];
1942 text.unwind.relocs.push(Reloc {
1943 at: 32,
1944 symbol: "puts".to_owned(),
1945 kind: Reference::Data,
1946 addend: 0,
1947 after: 0,
1948 });
1949 let why =
1950 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1951 .expect_err("a record about a name from somewhere else");
1952 assert!(why.to_string().contains("puts"), "{why}");
1953 }
1954
1955 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1957 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1958 let index = symbol.section_index().expect("a section to be defined in");
1959 let section = file.section_by_index(index).expect("a readable section");
1960 section.name().expect("a named section").to_owned()
1961 }
1962
1963 fn two() -> Text {
1965 let mut text = calling("puts");
1966 text.bytes.resize(16, 0x90);
1969 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1970 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1971 text.relocs.push(Reloc {
1972 at: 17,
1973 symbol: "puts".to_owned(),
1974 kind: Reference::Call,
1975 addend: -4,
1976 after: 0,
1977 });
1978 text
1979 }
1980
1981 #[test]
1988 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1989 let sections =
1990 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1991 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1992 .expect("an object");
1993 let file = object::File::parse(&bytes[..]).expect("a readable object");
1994 assert_eq!(lives_in(&file, "f"), ".text.f");
1995 assert_eq!(lives_in(&file, "g"), ".text.g");
1996 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1997 for name in ["f", "g"] {
2000 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
2001 assert_eq!(symbol.address(), 0, "{name}");
2002 assert_eq!(symbol.size(), 6, "{name}");
2003 }
2004 let section = file.section_by_name(".text.g").expect("the second function");
2005 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
2006 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
2009 }
2010
2011 #[test]
2017 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
2018 let sections =
2019 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2020 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
2021 .expect("an object");
2022 let file = object::File::parse(&bytes[..]).expect("a readable object");
2023 for name in [".text.f", ".text.g"] {
2024 let section = file.section_by_name(name).expect("a function");
2025 let (offset, _) = section.relocations().next().expect("the call in it");
2026 assert_eq!(offset, 1, "{name}");
2029 assert_eq!(section.relocations().count(), 1, "{name}");
2030 }
2031 }
2032
2033 fn switching() -> Text {
2035 let mut text = two();
2036 let name = ".Lg_j0".to_owned();
2037 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
2038 text
2039 }
2040
2041 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
2043 let section = file.section_by_name(section).expect("the table's section");
2044 section
2045 .relocations()
2046 .map(|(offset, reloc)| {
2047 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
2048 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2049 panic!("a cell against something that is not a symbol");
2050 };
2051 let symbol = file.symbol_by_index(index).expect("a symbol");
2052 assert_eq!(symbol.kind(), SymbolKind::Section);
2053 let at = symbol.section_index().expect("a section symbol is in one");
2054 let name = file.section_by_index(at).expect("a section").name().expect("a name");
2055 (offset, name.to_owned(), reloc.addend())
2056 })
2057 .collect()
2058 }
2059
2060 #[test]
2061 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
2062 let mut text = switching();
2065 text.relocs[1].symbol = ".Lg_j0".to_owned();
2066 text.relocs[1].kind = Reference::Data;
2067 let bytes =
2068 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2069 .expect("an object");
2070 let file = object::File::parse(&bytes[..]).expect("a readable object");
2071 let rodata = file.section_by_name(".rodata").expect("the table's section");
2072 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2073 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2074 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2075 let (at, reloc) = file
2076 .section_by_name(".text")
2077 .expect("the code")
2078 .relocations()
2079 .find(|(at, _)| *at == 17)
2080 .expect("the reference to the table");
2081 assert_eq!((at, reloc.addend()), (17, -4));
2082 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2083 panic!("a reference against something that is not a symbol");
2084 };
2085 let symbol = file.symbol_by_index(index).expect("a symbol");
2086 assert_eq!(symbol.section_index(), Some(rodata.index()));
2087 assert_eq!(symbol.kind(), SymbolKind::Section);
2088 assert_eq!(
2091 cells(&file, ".rodata"),
2092 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2093 );
2094 }
2095
2096 #[test]
2097 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2098 let sections =
2099 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2100 let bytes =
2101 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2102 .expect("an object");
2103 let file = object::File::parse(&bytes[..]).expect("a readable object");
2104 assert_eq!(
2106 cells(&file, ".rodata.g"),
2107 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2108 );
2109 }
2110
2111 #[test]
2112 fn a_jump_table_outside_the_code_is_refused_on_windows() {
2113 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2114 let written = write(
2115 &switching(),
2116 &Data::default(),
2117 &[],
2118 &target,
2119 Output::default(),
2120 &Info::default(),
2121 );
2122 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2123 }
2124
2125 #[test]
2128 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2129 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2130 let reloc = |at, symbol: &str, bytes| Reloc {
2131 at,
2132 symbol: symbol.to_owned(),
2133 kind: Reference::Address { bytes },
2134 addend: 0,
2135 after: 0,
2136 };
2137 let info = Info {
2138 chunks: vec![
2139 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2140 Chunk {
2141 name: ".debug_info".to_owned(),
2142 bytes: vec![0; 12],
2143 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2144 },
2145 ],
2146 ..Info::default()
2147 };
2148 let bytes =
2149 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2150 .expect("object");
2151 let file = object::File::parse(&bytes[..]).expect("a readable object");
2152 let section = file.section_by_name(".debug_info").expect("the debug section");
2153 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2154 assert_eq!(
2155 kinds,
2156 [
2157 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2158 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2159 ]
2160 );
2161 }
2162
2163 fn variable(name: &str, place: Place) -> Object {
2165 Object {
2166 name: name.to_owned(),
2167 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2168 size: 4,
2169 align: 4,
2170 place,
2171 binding: Binding::Global,
2172 visibility: Visibility::Default,
2173 relocs: Vec::new(),
2174 }
2175 }
2176
2177 fn measured() -> (Text, Data) {
2179 let mut text = calling("puts");
2180 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2181 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2182 let mut table = variable("table", Place::ReadOnly);
2183 table.bytes = vec![0; 8];
2184 table.size = 8;
2185 let apart = |at, to: &str, from: &str| Apart {
2186 object: 0,
2187 at,
2188 to: to.to_owned(),
2189 from: from.to_owned(),
2190 addend: 0,
2191 bytes: 4,
2192 };
2193 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2194 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2195 }
2196
2197 #[test]
2198 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2199 let (text, data) = measured();
2200 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2201 .expect("an object");
2202 let file = object::File::parse(&bytes[..]).expect("a readable object");
2203 let section = file.section_by_name(".rodata").expect("a read only section");
2204 assert_eq!(section.relocations().count(), 0);
2205 let image = section.data().expect("the image");
2206 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2207 }
2208
2209 #[test]
2213 fn a_label_an_image_holds_is_not_in_the_symbol_table() {
2214 let (text, mut data) = measured();
2215 data.apart.clear();
2216 data.objects[0].relocs.push(Reloc {
2217 at: 0,
2218 symbol: ".L1".to_owned(),
2219 kind: Reference::Address { bytes: 8 },
2220 addend: 0,
2221 after: 0,
2222 });
2223 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2224 .expect("an object");
2225 let file = object::File::parse(&bytes[..]).expect("a readable object");
2226 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2227 assert!(names.iter().all(|name| !name.starts_with(".L")), "{names:?}");
2228 let section = file.section_by_name(".rodata").expect("a read only section");
2229 let relocs: Vec<_> = section.relocations().collect();
2230 assert_eq!(relocs.len(), 1);
2231 let (_, reloc) = &relocs[0];
2232 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2233 panic!("a relocation against a symbol, not {reloc:?}");
2234 };
2235 let symbol = file.symbol_by_index(index).expect("a symbol");
2236 assert_eq!(symbol.kind(), SymbolKind::Section);
2237 assert_eq!(reloc.addend(), 5);
2238 }
2239
2240 #[test]
2241 fn a_distance_between_labels_in_two_sections_is_refused() {
2242 let (mut text, data) = measured();
2245 text.bytes.resize(22, 0x90);
2246 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2247 text.labels[1].at = 17;
2248 let output =
2249 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2250 let refused = write(&text, &data, &[], &target(), output, &Info::default());
2251 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2252 }
2253
2254 fn holding(object: Object) -> Vec<u8> {
2256 let data = Data {
2257 apart: Vec::new(),
2258 exports: Vec::new(),
2259 weak: Vec::new(),
2260 objects: vec![object],
2261 };
2262 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2263 .expect("an object")
2264 }
2265
2266 #[test]
2267 fn what_a_variable_is_decides_which_section_it_goes_in() {
2268 for (place, wanted) in [
2269 (Place::Written, ".data"),
2270 (Place::ReadOnly, ".rodata"),
2271 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2272 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2273 (Place::Zero, ".bss"),
2274 (Place::Thread { zero: false }, ".tdata"),
2275 (Place::Thread { zero: true }, ".tbss"),
2276 (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2277 ] {
2278 let bytes = holding(variable("x", place.clone()));
2279 let file = object::File::parse(&bytes[..]).expect("a readable object");
2280 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2281 assert_eq!(section.size(), 4, "{place:?}");
2282 let carried = section.data().expect("the bytes").len();
2285 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2286 }
2287 }
2288
2289 #[test]
2295 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2296 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2297 let bytes = holding(variable("counter", place.clone()));
2298 let file = object::File::parse(&bytes[..]).expect("a readable object");
2299 let symbol = file
2300 .symbols()
2301 .find(|symbol| symbol.name() == Ok("counter"))
2302 .unwrap_or_else(|| panic!("{place:?}"));
2303 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2304 }
2305 }
2306
2307 #[test]
2313 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2314 for (name, wanted) in [
2315 (".init_array", elf::SHT_INIT_ARRAY),
2316 (".init_array.00101", elf::SHT_INIT_ARRAY),
2317 (".fini_array", elf::SHT_FINI_ARRAY),
2318 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2319 (".init_arrays", elf::SHT_PROGBITS),
2320 ] {
2321 let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2322 let file = object::File::parse(&bytes[..]).expect("a readable object");
2323 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2324 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2325 panic!("{name} is not an elf section");
2326 };
2327 assert_eq!(sh_type, wanted, "{name}");
2328 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2329 }
2330 }
2331
2332 #[test]
2336 fn a_named_section_carries_the_flags_of_what_is_in_it() {
2337 for (name, holds, kind, flags) in [
2338 (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2339 (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2340 (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2341 ] {
2342 let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2343 let file = object::File::parse(&bytes[..]).expect("a readable object");
2344 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2345 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2346 panic!("{name} is not an elf section");
2347 };
2348 assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2349 assert_eq!(section.size(), 4, "{name}");
2350 }
2351 }
2352
2353 #[test]
2359 fn two_variables_in_one_named_section_share_it() {
2360 let objects = vec![
2361 variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2362 variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2363 ];
2364 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2365 let bytes =
2366 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2367 .expect("an object");
2368 let file = object::File::parse(&bytes[..]).expect("a readable object");
2369 let named: Vec<_> =
2370 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2371 assert_eq!(named.len(), 1);
2372 assert_eq!(named[0].size(), 8);
2373 }
2374
2375 #[test]
2379 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2380 let sections =
2381 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2382 for (place, wanted) in [
2383 (Place::Written, ".data.x"),
2384 (Place::ReadOnly, ".rodata.x"),
2385 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2386 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2387 (Place::Zero, ".bss.x"),
2388 (Place::Thread { zero: false }, ".tdata.x"),
2389 (Place::Thread { zero: true }, ".tbss.x"),
2390 ] {
2391 let data = Data {
2392 apart: Vec::new(),
2393 exports: Vec::new(),
2394 weak: Vec::new(),
2395 objects: vec![variable("x", place.clone())],
2396 };
2397 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2398 .expect("object");
2399 let file = object::File::parse(&bytes[..]).expect("a readable object");
2400 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2401 let section = file.section_by_name(wanted).expect("the section it named");
2402 assert_eq!(section.size(), 4, "{place:?}");
2403 let carried = section.data().expect("the bytes").len();
2406 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2407 }
2408 }
2409
2410 #[test]
2414 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2415 let sections =
2416 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2417 let named = Place::Named(".init_array".to_owned(), Holds::Written);
2418 let objects = vec![variable("m", Place::Merged), variable("n", named)];
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 m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2430 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2431 assert_eq!(lives_in(&file, "n"), ".init_array");
2432 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2433 }
2434
2435 #[test]
2439 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2440 let sections =
2441 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2442 let pointer = Object {
2443 bytes: vec![0; 8],
2444 size: 8,
2445 align: 8,
2446 relocs: vec![Reloc {
2447 at: 0,
2448 symbol: "y".to_owned(),
2449 kind: Reference::Address { bytes: 8 },
2450 addend: 0,
2451 after: 0,
2452 }],
2453 ..variable("p", Place::Written)
2454 };
2455 let objects = vec![variable("first", Place::Written), pointer];
2456 let bytes = write(
2457 &Text::default(),
2458 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2459 &[],
2460 &target(),
2461 sections,
2462 &Info::default(),
2463 )
2464 .expect("object");
2465 let file = object::File::parse(&bytes[..]).expect("a readable object");
2466 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2467 let (offset, reloc) = section.relocations().next().expect("one relocation");
2468 assert_eq!(offset, 0);
2471 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2472 }
2473
2474 #[test]
2482 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2483 let place = Place::RelocReadOnly { local: true };
2484 let data = Data {
2485 apart: Vec::new(),
2486 exports: Vec::new(),
2487 weak: Vec::new(),
2488 objects: vec![variable("first", place.clone()), variable("second", place)],
2489 };
2490 let bytes =
2491 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2492 .expect("an object");
2493 let file = object::File::parse(&bytes[..]).expect("a readable object");
2494 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2495 assert_eq!(named, 1, "one section holding both, not one each");
2496 }
2497
2498 #[test]
2499 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2500 let mut data = Data {
2501 apart: Vec::new(),
2502 exports: Vec::new(),
2503 weak: Vec::new(),
2504 objects: vec![variable("first", Place::Written)],
2505 };
2506 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2507 let bytes =
2508 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2509 .expect("an object");
2510 let file = object::File::parse(&bytes[..]).expect("a readable object");
2511 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2512 assert_eq!(second.kind(), SymbolKind::Data);
2513 assert_eq!(second.size(), 4);
2514 assert_eq!(second.address(), 16);
2518 }
2519
2520 #[test]
2521 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2522 for (binding, global, weak) in [
2523 (Binding::Global, true, false),
2524 (Binding::Local, false, false),
2525 (Binding::Weak, true, true),
2526 ] {
2527 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2528 let file = object::File::parse(&bytes[..]).expect("a readable object");
2529 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2530 assert_eq!(x.is_global(), global, "{binding:?}");
2531 assert_eq!(x.is_weak(), weak, "{binding:?}");
2532 }
2533 }
2534
2535 #[test]
2536 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2537 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2538 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2539 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2540 assert!(x.is_common(), "the linker merges every definition of this name into one");
2541 assert_eq!(x.size(), 4);
2542 assert_eq!(x.address(), 0);
2546 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2547 }
2548
2549 #[test]
2550 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2551 let object = Object {
2552 bytes: vec![0; 8],
2553 size: 8,
2554 align: 8,
2555 relocs: vec![Reloc {
2556 at: 0,
2557 symbol: "y".to_owned(),
2558 kind: Reference::Address { bytes: 8 },
2559 addend: 16,
2560 after: 0,
2561 }],
2562 ..variable("p", Place::Written)
2563 };
2564 let bytes = holding(object);
2565 let file = object::File::parse(&bytes[..]).expect("a readable object");
2566 let section = file.section_by_name(".data").expect("a data section");
2567 let (offset, reloc) = section.relocations().next().expect("one relocation");
2568 assert_eq!(offset, 0);
2569 assert_eq!(reloc.addend(), 16);
2570 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2571 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2572 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2573 }
2574
2575 #[test]
2582 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2583 let mut text = Text::default();
2584 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2585 text.bytes.resize(8, 0x90);
2586 text.relocs.push(Reloc {
2587 at: 1,
2588 symbol: "hook".to_owned(),
2589 kind: Reference::Call,
2590 addend: -4,
2591 after: 0,
2592 });
2593 let data = Data {
2594 apart: Vec::new(),
2595 exports: Vec::new(),
2596 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2597 objects: vec![],
2598 };
2599 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2600 .expect("an object");
2601 let file = object::File::parse(&bytes[..]).expect("a readable object");
2602
2603 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2604 assert!(hook.is_undefined(), "nothing here defines it");
2605 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2606
2607 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2611 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2612 }
2613
2614 #[test]
2629 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2630 let mut text = Text::default();
2631 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2632 text.bytes.resize(16, 0x90);
2633 text.relocs.push(Reloc {
2634 at: 3,
2635 symbol: "flags".to_owned(),
2636 kind: Reference::Thread,
2637 addend: -4,
2638 after: 0,
2639 });
2640 text.relocs.push(Reloc {
2643 at: 10,
2644 symbol: "shared".to_owned(),
2645 kind: Reference::Got,
2646 addend: -4,
2647 after: 0,
2648 });
2649 let data =
2650 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2651 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2652 .expect("an object");
2653 let file = object::File::parse(&bytes[..]).expect("a readable object");
2654
2655 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2656 assert!(flags.is_undefined(), "nothing here defines it");
2657 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2658
2659 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2660 assert!(shared.is_undefined(), "nothing here defines this one either");
2661 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2662 }
2663
2664 #[test]
2666 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2667 let mut data = Data {
2668 apart: Vec::new(),
2669 exports: Vec::new(),
2670 weak: Vec::new(),
2671 objects: vec![variable("first", Place::Written)],
2672 };
2673 data.objects.push(Object {
2674 bytes: vec![0; 16],
2675 size: 16,
2676 align: 8,
2677 relocs: vec![Reloc {
2678 at: 8,
2679 symbol: "y".to_owned(),
2680 kind: Reference::Address { bytes: 8 },
2681 addend: 0,
2682 after: 0,
2683 }],
2684 ..variable("second", Place::Written)
2685 });
2686 let bytes =
2687 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2688 .expect("an object");
2689 let file = object::File::parse(&bytes[..]).expect("a readable object");
2690 let section = file.section_by_name(".data").expect("a data section");
2691 let (offset, _) = section.relocations().next().expect("one relocation");
2692 assert_eq!(offset, 16);
2695 }
2696
2697 #[test]
2698 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2699 let data = Data {
2700 apart: Vec::new(),
2701 exports: Vec::new(),
2702 weak: Vec::new(),
2703 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2704 };
2705 let aliases = [Alias {
2706 name: "b".to_owned(),
2707 target: "a".to_owned(),
2708 binding: Binding::Global,
2709 visibility: Visibility::Default,
2710 ifunc: false,
2711 }];
2712 let bytes = write(
2713 &Text::default(),
2714 &data,
2715 &aliases,
2716 &target(),
2717 Output::default(),
2718 &Info::default(),
2719 )
2720 .expect("an object");
2721 let file = object::File::parse(&bytes[..]).expect("a readable object");
2722 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2723 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2724 assert_eq!(b.address(), a.address(), "the same place");
2725 assert_eq!(b.size(), a.size());
2726 assert_eq!(b.section_index(), a.section_index());
2727 assert!(a.is_local(), "the target was written `static`");
2730 assert!(b.is_global(), "and the name given to it was not");
2731 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2733 }
2734
2735 #[test]
2736 fn second_names_are_written_in_the_order_of_what_they_name() {
2737 let data = Data {
2738 apart: Vec::new(),
2739 exports: Vec::new(),
2740 weak: Vec::new(),
2741 objects: vec![variable("a", Place::Written), variable("b", Place::Written)],
2742 };
2743 let alias = |name: &str, target: &str| Alias {
2744 name: name.to_owned(),
2745 target: target.to_owned(),
2746 binding: Binding::Global,
2747 visibility: Visibility::Default,
2748 ifunc: false,
2749 };
2750 let aliases = [alias("for_b", "b"), alias("for_a", "a")];
2751 let bytes = write(
2752 &Text::default(),
2753 &data,
2754 &aliases,
2755 &target(),
2756 Output::default(),
2757 &Info::default(),
2758 )
2759 .expect("an object");
2760 let file = object::File::parse(&bytes[..]).expect("a readable object");
2761 let names: Vec<_> = file
2762 .symbols()
2763 .filter_map(|s| s.name().ok())
2764 .filter(|name| name.starts_with("for_"))
2765 .collect();
2766 assert_eq!(names, ["for_a", "for_b"]);
2767 }
2768
2769 #[test]
2770 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2771 let text = calling("puts");
2772 let aliases = [Alias {
2773 name: "g".to_owned(),
2774 target: "f".to_owned(),
2775 binding: Binding::Weak,
2776 visibility: Visibility::Default,
2777 ifunc: false,
2778 }];
2779 let bytes = write(
2780 &text,
2781 &Data::default(),
2782 &aliases,
2783 &target(),
2784 Output::default(),
2785 &Info::default(),
2786 )
2787 .expect("an object");
2788 let file = object::File::parse(&bytes[..]).expect("a readable object");
2789 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2790 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2791 assert_eq!(g.address(), f.address());
2792 assert_eq!(g.size(), f.size());
2793 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2794 assert!(g.is_weak(), "so that a program may define the name itself instead");
2795 }
2796
2797 #[test]
2801 fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2802 let text = calling("puts");
2803 for (binding, bind) in [
2804 (Binding::Global, elf::STB_GLOBAL),
2805 (Binding::Weak, elf::STB_WEAK),
2806 (Binding::Local, elf::STB_LOCAL),
2807 ] {
2808 let aliases = [Alias {
2809 name: "g".to_owned(),
2810 target: "f".to_owned(),
2811 binding,
2812 visibility: Visibility::Default,
2813 ifunc: true,
2814 }];
2815 let bytes = write(
2816 &text,
2817 &Data::default(),
2818 &aliases,
2819 &target(),
2820 Output::default(),
2821 &Info::default(),
2822 )
2823 .expect("an object");
2824 let file = object::File::parse(&bytes[..]).expect("a readable object");
2825 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2826 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2827 assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2828 let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2829 panic!("an ELF symbol");
2830 };
2831 assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2832 let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2833 let os_abi = elf.elf_header().e_ident.os_abi;
2834 assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2835 }
2836 }
2837
2838 #[test]
2841 fn an_ifunc_is_refused_on_a_format_without_the_type() {
2842 let aliases = [Alias {
2843 name: "g".to_owned(),
2844 target: "f".to_owned(),
2845 binding: Binding::Global,
2846 visibility: Visibility::Default,
2847 ifunc: true,
2848 }];
2849 let error = write(
2850 &calling("puts"),
2851 &Data::default(),
2852 &aliases,
2853 &windows(),
2854 Output::default(),
2855 &Info::default(),
2856 )
2857 .expect_err("no ifunc on COFF");
2858 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2859 }
2860
2861 #[test]
2864 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2865 let aliases = [Alias {
2866 name: "b".to_owned(),
2867 target: "a".to_owned(),
2868 binding: Binding::Global,
2869 visibility: Visibility::Default,
2870 ifunc: false,
2871 }];
2872 let error = write(
2873 &Text::default(),
2874 &Data::default(),
2875 &aliases,
2876 &target(),
2877 Output::default(),
2878 &Info::default(),
2879 )
2880 .expect_err("nothing to point at");
2881 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2882 }
2883
2884 #[test]
2885 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2886 let text = calling("puts");
2887 for triple in [
2888 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2889 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2890 ] {
2891 let error = write(
2892 &text,
2893 &Data::default(),
2894 &[],
2895 &TargetInfo::new(triple),
2896 Output::default(),
2897 &Info::default(),
2898 )
2899 .expect_err("no writer");
2900 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2901 }
2902 }
2903
2904 #[test]
2910 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2911 let mut text = calling("puts");
2912 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2913 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2914 text.bytes.resize(33, 0x90);
2915 let data = Data {
2916 apart: Vec::new(),
2917 exports: Vec::new(),
2918 weak: Vec::new(),
2919 objects: vec![variable("seen", Place::Written), {
2920 let mut quiet = variable("quiet", Place::Zero);
2921 quiet.binding = Binding::Local;
2922 quiet
2923 }],
2924 };
2925 let aliases = [Alias {
2926 name: "second".to_owned(),
2927 target: "f".to_owned(),
2928 binding: Binding::Global,
2929 visibility: Visibility::Default,
2930 ifunc: false,
2931 }];
2932
2933 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2934 assert_eq!(names, ["f", "shared", "seen", "second"]);
2935
2936 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2937 .expect("an object");
2938 let file = object::File::parse(&bytes[..]).expect("a readable object");
2939 let found: Vec<String> = file
2940 .symbols()
2941 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2942 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2943 .collect();
2944 let mut sorted = names.clone();
2945 sorted.sort();
2946 let mut theirs = found;
2947 theirs.sort();
2948 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2949 }
2950
2951 fn windows() -> TargetInfo {
2953 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2954 }
2955
2956 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2958 let file = object::File::parse(bytes).expect("a readable object");
2959 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2960 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2961 }
2962
2963 #[test]
2964 fn a_windows_target_is_written_rather_than_refused() {
2965 let text = calling("puts");
2966 let bytes =
2967 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2968 .expect("an object");
2969 let file = object::File::parse(&bytes[..]).expect("a readable object");
2970 assert_eq!(file.format(), BinaryFormat::Coff);
2971 let section = file.section_by_name(".text").expect("a text section");
2972 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2973 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2974 assert!(names.contains(&"f"), "{names:?}");
2975 assert!(names.contains(&"puts"), "{names:?}");
2976 }
2977
2978 #[test]
2986 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2987 for (after, typ) in [
2988 (0, pe::IMAGE_REL_AMD64_REL32),
2989 (1, pe::IMAGE_REL_AMD64_REL32_1),
2990 (4, pe::IMAGE_REL_AMD64_REL32_4),
2991 (5, pe::IMAGE_REL_AMD64_REL32_5),
2992 ] {
2993 let mut text = calling("puts");
2994 text.relocs[0].addend = -4 - i64::from(after);
2997 text.relocs[0].after = after;
2998 text.bytes.resize(6 + after as usize, 0x90);
2999 text.funcs[0].len = text.bytes.len();
3000 let bytes = write(
3001 &text,
3002 &Data::default(),
3003 &[],
3004 &windows(),
3005 Output::default(),
3006 &Info::default(),
3007 )
3008 .expect("an object");
3009 let file = object::File::parse(&bytes[..]).expect("a readable object");
3010 let section = file.section_by_name(".text").expect("a text section");
3011 let (_, reloc) = section.relocations().next().expect("the relocation");
3012 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
3013 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
3016 }
3017 }
3018
3019 #[test]
3022 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
3023 let mut text = calling("puts");
3024 text.relocs[0].addend = 12;
3025 let bytes =
3026 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3027 .expect("an object");
3028 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
3029 }
3030
3031 #[test]
3032 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
3033 let object = Object {
3034 bytes: vec![0; 8],
3035 size: 8,
3036 align: 8,
3037 relocs: vec![Reloc {
3038 at: 0,
3039 symbol: "y".to_owned(),
3040 kind: Reference::Address { bytes: 8 },
3041 addend: 0,
3042 after: 0,
3043 }],
3044 ..variable("p", Place::Written)
3045 };
3046 let data = Data {
3047 apart: Vec::new(),
3048 exports: Vec::new(),
3049 weak: Vec::new(),
3050 objects: vec![object],
3051 };
3052 let bytes =
3053 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3054 .expect("an object");
3055 let file = object::File::parse(&bytes[..]).expect("a readable object");
3056 let section = file.section_by_name(".data").expect("a data section");
3057 let (_, reloc) = section.relocations().next().expect("the relocation");
3058 let typ = pe::IMAGE_REL_AMD64_ADDR64;
3059 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
3060 }
3061
3062 #[test]
3066 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
3067 let pointer = Object {
3068 bytes: vec![0; 8],
3069 size: 8,
3070 align: 8,
3071 relocs: vec![Reloc {
3072 at: 0,
3073 symbol: "environ".to_owned(),
3074 kind: Reference::Address { bytes: 8 },
3075 addend: 0,
3076 after: 0,
3077 }],
3078 ..variable(".refptr.environ", Place::Pointer)
3079 };
3080 let data = Data { objects: vec![pointer], ..Data::default() };
3081 let bytes =
3082 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3083 .expect("an object");
3084 let file = object::File::parse(&bytes[..]).expect("a readable object");
3085 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
3086 let SectionFlags::Coff { characteristics } = section.flags() else {
3087 panic!("a COFF section has COFF flags");
3088 };
3089 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
3090 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
3092 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
3093 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
3094 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
3095 assert_eq!(comdat.kind(), ComdatKind::Any);
3096 assert_eq!(comdat.name(), Ok(".refptr.environ"));
3097 let (_, reloc) = section.relocations().next().expect("the address it holds");
3098 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
3099 }
3100
3101 #[test]
3105 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
3106 let exports = vec![
3107 Export { name: "offered".to_owned(), kind: Offer::Function },
3108 Export { name: "count".to_owned(), kind: Offer::Variable },
3109 Export { name: "kept".to_owned(), kind: Offer::Hidden },
3110 ];
3111 let data = Data { exports, ..Data::default() };
3112 let bytes =
3113 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3114 .expect("an object");
3115 let file = object::File::parse(&bytes[..]).expect("a readable object");
3116 let section = file.section_by_name(".drectve").expect("the options section");
3117 assert_eq!(
3118 section.data().expect("the options"),
3119 b" -export:offered -export:count,data -exclude-symbols:kept"
3120 );
3121 let SectionFlags::Coff { characteristics } = section.flags() else {
3122 panic!("a COFF section has COFF flags");
3123 };
3124 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
3125 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
3126
3127 let none = write(
3128 &Text::default(),
3129 &Data::default(),
3130 &[],
3131 &windows(),
3132 Output::default(),
3133 &Info::default(),
3134 )
3135 .expect("an object");
3136 let file = object::File::parse(&none[..]).expect("a readable object");
3137 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3138 }
3139
3140 #[test]
3143 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3144 for local in [false, true] {
3145 let data = Data {
3146 apart: Vec::new(),
3147 exports: Vec::new(),
3148 weak: Vec::new(),
3149 objects: vec![variable("p", Place::RelocReadOnly { local })],
3150 };
3151 let bytes = write(
3152 &Text::default(),
3153 &data,
3154 &[],
3155 &windows(),
3156 Output::default(),
3157 &Info::default(),
3158 )
3159 .expect("an object");
3160 let file = object::File::parse(&bytes[..]).expect("a readable object");
3161 assert!(file.section_by_name(".rdata").is_some(), "{local}");
3162 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3163 }
3164 }
3165
3166 #[test]
3169 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3170 let text = calling("puts");
3171 let output = Output { property: Property { features: 3 }, ..Output::default() };
3172 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3173 .expect("an object");
3174 let file = object::File::parse(&bytes[..]).expect("a readable object");
3175 assert!(file.section_by_name(".note.GNU-stack").is_none());
3176 assert!(file.section_by_name(".note.gnu.property").is_none());
3177 }
3178
3179 #[test]
3184 fn what_this_format_cannot_say_is_refused_by_name() {
3185 let ordinary = Text::default();
3186 let empty = Data::default();
3187
3188 let mut thread = Data::default();
3189 thread.objects.push(variable("t", Place::Thread { zero: true }));
3190
3191 let mut gathered = Data::default();
3192 gathered
3193 .objects
3194 .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3195
3196 let mut table = calling("puts");
3197 table.relocs[0].kind = Reference::Got;
3198
3199 let mut room = calling("puts");
3200 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3201
3202 let cases: [(&str, &Text, &Data); 4] = [
3203 ("thread-local", &ordinary, &thread),
3204 ("startup", &ordinary, &gathered),
3205 ("table", &table, &empty),
3206 ("patcher", &room, &empty),
3207 ];
3208 for (what, text, data) in cases {
3209 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3210 .expect_err("something this format cannot write");
3211 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3212 }
3213 }
3214
3215 #[test]
3218 fn a_thread_local_variable_goes_in_the_tls_section() {
3219 let mut thread = Data::default();
3220 thread.objects.push(variable("t", Place::Thread { zero: false }));
3221 let bytes =
3222 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3223 .expect("an object");
3224 let file = object::File::parse(&bytes[..]).expect("a readable object");
3225 assert!(file.section_by_name(".tls$").is_some());
3226 }
3227
3228 #[test]
3232 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3233 let mut text = calling("puts");
3234 text.funcs[0].visibility = Visibility::Hidden;
3235 let bytes =
3236 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3237 .expect("an object");
3238 let file = object::File::parse(&bytes[..]).expect("a readable object");
3239 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3240 assert!(symbol.is_global(), "a name others may use either way");
3241 }
3242
3243 #[test]
3244 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3245 let text = calling("puts");
3246 let data = Data {
3247 apart: Vec::new(),
3248 exports: Vec::new(),
3249 weak: Vec::new(),
3250 objects: vec![variable("shared", Place::Written)],
3251 };
3252 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3253 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3254 }
3255
3256 #[test]
3260 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3261 let text = calling("puts");
3262 for triple in [
3263 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3264 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3265 ] {
3266 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3267 .expect_err("no writer");
3268 assert!(matches!(error, Error::Format { .. }), "{error:?}");
3269 }
3270 }
3271
3272 fn i386() -> TargetInfo {
3274 TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3275 }
3276
3277 #[test]
3284 fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3285 let mut text = calling("puts");
3286 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3287 text.funcs[0].start = 3;
3288 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3289 text.relocs[0].at = 4;
3290 let data = Data {
3291 objects: vec![Object {
3292 name: "p".to_owned(),
3293 bytes: vec![0; 4],
3294 size: 4,
3295 align: 4,
3296 place: Place::Written,
3297 binding: Binding::Global,
3298 visibility: Visibility::Default,
3299 relocs: vec![Reloc {
3300 at: 0,
3301 symbol: "x".to_owned(),
3302 kind: Reference::Address { bytes: 4 },
3303 addend: 12,
3304 after: 0,
3305 }],
3306 }],
3307 ..Data::default()
3308 };
3309 let property = Property { features: Property::IBT | Property::SHSTK };
3310 let output = Output { property, ..Output::default() };
3311 let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3312 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3313 assert_eq!(file.architecture(), Architecture::I386);
3314 assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3315 assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3316
3317 let code = file.section_by_name(".text").expect("a text section");
3319 let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3320 panic!("one relocation in the text")
3321 };
3322 assert_eq!(*at, 4);
3323 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3324 assert!(reloc.has_implicit_addend());
3325 assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3326
3327 let variable = file.section_by_name(".data").expect("a data section");
3329 let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3330 panic!("one relocation in the data")
3331 };
3332 assert_eq!(*at, 0);
3333 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3334 assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3335
3336 let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3338 assert_eq!(record.size(), 4);
3339 assert_eq!(record.align(), 4);
3340 let index = code.index().0;
3341 assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3342 let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3343 panic!("one address in the record")
3344 };
3345 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3346
3347 let note = file.section_by_name(".note.gnu.property").expect("the note");
3349 assert_eq!(note.align(), 4);
3350 let want: Vec<u8> = [
3351 4u32,
3352 12,
3353 5,
3354 u32::from_le_bytes(*b"GNU\0"),
3355 Property::X86_FEATURES,
3356 4,
3357 Property::IBT | Property::SHSTK,
3358 ]
3359 .iter()
3360 .flat_map(|word| word.to_le_bytes())
3361 .collect();
3362 assert_eq!(note.data().expect("the bytes"), &want[..]);
3363 }
3364
3365 #[test]
3368 fn an_i386_address_less_three_gigabytes_wraps_in_its_four_bytes() {
3369 let object = |bytes: u8| Object {
3370 name: "cr3".to_owned(),
3371 bytes: vec![0; usize::from(bytes)],
3372 size: u64::from(bytes),
3373 align: u64::from(bytes),
3374 place: Place::Written,
3375 binding: Binding::Global,
3376 visibility: Visibility::Default,
3377 relocs: vec![Reloc {
3378 at: 0,
3379 symbol: "swapper_pg_dir".to_owned(),
3380 kind: Reference::Address { bytes },
3381 addend: -0xC000_0000,
3382 after: 0,
3383 }],
3384 };
3385 let data = Data { objects: vec![object(4)], ..Data::default() };
3386 let bytes =
3387 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default())
3388 .expect("an object");
3389 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3390 let variable = file.section_by_name(".data").expect("a data section");
3391 assert_eq!(variable.data().expect("the bytes"), &0x4000_0000u32.to_le_bytes());
3392
3393 let data = Data { objects: vec![object(2)], ..Data::default() };
3394 let refused =
3395 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default());
3396 assert!(refused.is_err(), "two bytes cannot hold a name less three gigabytes");
3397 }
3398
3399 #[test]
3402 fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3403 let info = Info {
3404 chunks: vec![Chunk {
3405 name: ".debug_info".to_owned(),
3406 bytes: vec![0; 64],
3407 relocs: vec![Reloc {
3408 at: 8,
3409 symbol: "f".to_owned(),
3410 kind: Reference::Address { bytes: 4 },
3411 addend: 7,
3412 after: 0,
3413 }],
3414 }],
3415 compress: Compress::Zlib,
3416 };
3417 let bytes =
3418 write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3419 .expect("an object");
3420 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3421 let section = file.section_by_name(".debug_info").expect("the debug section");
3422 let packed =
3423 SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3424 assert_ne!(section.flags(), packed);
3425 let data = section.data().expect("the bytes");
3426 assert_eq!(data.len(), 64);
3427 assert_eq!(&data[8..12], &7u32.to_le_bytes());
3428 }
3429
3430 fn i386_windows() -> TargetInfo {
3432 TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3433 }
3434
3435 #[test]
3444 fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3445 let mut text = calling("puts");
3446 text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3447 text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3448 text.relocs.push(Reloc {
3449 at: 7,
3450 symbol: "__imp_GetTickCount".to_owned(),
3451 kind: Reference::Call,
3452 addend: -4,
3453 after: 0,
3454 });
3455 let data = Data {
3456 objects: vec![Object {
3457 name: "p".to_owned(),
3458 bytes: vec![0; 12],
3459 size: 12,
3460 align: 4,
3461 place: Place::Written,
3462 binding: Binding::Global,
3463 visibility: Visibility::Default,
3464 relocs: vec![
3465 Reloc {
3466 at: 0,
3467 symbol: "x".to_owned(),
3468 kind: Reference::Address { bytes: 4 },
3469 addend: 12,
3470 after: 0,
3471 },
3472 Reloc {
3473 at: 4,
3474 symbol: "f".to_owned(),
3475 kind: Reference::Image,
3476 addend: 0,
3477 after: 0,
3478 },
3479 Reloc {
3480 at: 8,
3481 symbol: "x".to_owned(),
3482 kind: Reference::Away,
3483 addend: 0,
3484 after: 0,
3485 },
3486 ],
3487 }],
3488 ..Data::default()
3489 };
3490 let aliases = [Alias {
3491 name: "g".to_owned(),
3492 target: "f".to_owned(),
3493 binding: Binding::Global,
3494 visibility: Visibility::Default,
3495 ifunc: false,
3496 }];
3497 let target = i386_windows();
3498 let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3499 .expect("an object");
3500 let file = object::File::parse(&bytes[..]).expect("a readable object");
3501 assert_eq!(file.format(), BinaryFormat::Coff);
3502 assert_eq!(file.architecture(), Architecture::I386);
3503 assert!(!file.is_64());
3504
3505 let named = |name: &str| file.symbol_by_name(name).is_some();
3506 for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3507 assert!(named(name), "{name}");
3508 }
3509 for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3510 assert!(!named(name), "{name}");
3511 }
3512
3513 let code = file.section_by_name(".text").expect("a text section");
3514 let relocs: Vec<_> = code.relocations().collect();
3515 assert_eq!(relocs.len(), 2);
3516 for (at, reloc) in &relocs {
3517 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3518 let at = *at as usize;
3519 assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3520 }
3521
3522 let variable = file.section_by_name(".data").expect("a data section");
3523 let types: Vec<_> = variable
3524 .relocations()
3525 .map(|(at, reloc)| match reloc.flags() {
3526 RelocationFlags::Coff { typ } => (at, typ),
3527 flags => panic!("{flags:?}"),
3528 })
3529 .collect();
3530 assert_eq!(
3531 types,
3532 [
3533 (0, pe::IMAGE_REL_I386_DIR32),
3534 (4, pe::IMAGE_REL_I386_DIR32NB),
3535 (8, pe::IMAGE_REL_I386_REL32)
3536 ]
3537 );
3538 let image = variable.data().expect("the bytes");
3541 assert_eq!(&image[0..4], &12u32.to_le_bytes());
3542 assert_eq!(&image[8..12], &4u32.to_le_bytes());
3543
3544 let listed = defines(&text, &data, &aliases, &target).expect("a list");
3546 assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3547 }
3548
3549 #[test]
3552 fn an_i386_windows_object_has_no_unwind_table() {
3553 let mut text = calling("puts");
3554 text.unwind.bytes = vec![0; 12];
3555 let bytes = write(
3556 &text,
3557 &Data::default(),
3558 &[],
3559 &i386_windows(),
3560 Output::default(),
3561 &Info::default(),
3562 )
3563 .expect("an object");
3564 let file = object::File::parse(&bytes[..]).expect("a readable object");
3565 assert!(file.section_by_name(".pdata").is_none());
3566 assert!(file.section_by_name(".xdata").is_none());
3567 assert!(file.section_by_name(".eh_frame").is_none());
3568 }
3569
3570 #[test]
3573 fn an_i386_windows_object_says_it_is_safe_for_safeseh() {
3574 let write_for = |target: &TargetInfo| {
3575 write(
3576 &calling("puts"),
3577 &Data::default(),
3578 &[],
3579 target,
3580 Output::default(),
3581 &Info::default(),
3582 )
3583 .expect("an object")
3584 };
3585 let bytes = write_for(&i386_windows());
3586 let file = object::File::parse(&bytes[..]).expect("a readable object");
3587 let feat = file.symbol_by_name("@feat.00").expect("the feature symbol");
3588 let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
3590 let raw = coff.symbol_by_name("@feat.00").expect("the feature symbol");
3591 assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
3592 assert_eq!(feat.section(), object::SymbolSection::Absolute);
3593 assert!(feat.is_local());
3594 let bytes = write_for(&windows());
3595 let file = object::File::parse(&bytes[..]).expect("a readable object");
3596 assert!(file.symbol_by_name("@feat.00").is_none());
3597 }
3598
3599 #[test]
3602 fn i386_windows_has_no_eight_byte_or_table_relocations() {
3603 for kind in [
3604 Reference::Address { bytes: 8 },
3605 Reference::AwayWide,
3606 Reference::Got,
3607 Reference::GotOffset,
3608 Reference::Slot,
3609 Reference::Thread,
3610 ] {
3611 assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3612 }
3613 assert_eq!(
3614 Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3615 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3616 );
3617 assert_eq!(
3618 Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3619 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3620 "an address an instruction holds"
3621 );
3622 }
3623}