1use std::collections::BTreeMap;
34
35use object::write::{
36 Comdat, Mangling, Object as Writer, Relocation, StandardSection, Symbol, SymbolId,
37 SymbolSection,
38};
39use object::{
40 Architecture, BinaryFormat, ComdatKind, Endianness, RelocationFlags, SectionFlags, SectionKind,
41 SymbolFlags, SymbolKind, SymbolScope,
42};
43use rucc_base::hash::{Map, Set};
44use rucc_target::{ObjectFormat, TargetInfo};
45use rucc_tuple::Arch;
46
47use crate::section::{
48 Alias, Apart, Array, Binding, Compress, Data, EXCEPT_TABLE, Export, Holds, Info, Object,
49 Output, Place, Property, Reference, Reloc, Sections, Text, Visibility,
50};
51use crate::{coff, elf};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub(crate) enum Flavour {
61 Elf,
63 Coff,
65 MachO,
68}
69
70impl Flavour {
71 pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
73 match target.object_format {
74 ObjectFormat::Elf => Some(Flavour::Elf),
75 ObjectFormat::Coff => Some(Flavour::Coff),
76 ObjectFormat::MachO | ObjectFormat::Wasm => None,
77 }
78 }
79
80 pub(crate) fn binary(self) -> BinaryFormat {
82 match self {
83 Flavour::Elf => BinaryFormat::Elf,
84 Flavour::Coff => BinaryFormat::Coff,
85 Flavour::MachO => BinaryFormat::MachO,
86 }
87 }
88
89 pub(crate) fn reloc(
95 self,
96 machine: Architecture,
97 reference: Reference,
98 after: u8,
99 ) -> Option<RelocationFlags> {
100 let flags = |r_type| RelocationFlags::Elf { r_type };
101 match (self, machine) {
102 (Flavour::Elf, Architecture::I386) => elf::r_type_i386(reference).map(flags),
103 (Flavour::Elf, Architecture::Aarch64) => elf::r_type_aarch64(reference).map(flags),
104 (Flavour::Elf, _) => elf::r_type(reference).map(flags),
105 (Flavour::Coff, Architecture::Aarch64) => {
106 coff::arm64(reference).map(|typ| RelocationFlags::Coff { typ })
107 }
108 (Flavour::Coff, Architecture::I386) => {
109 coff::i386(reference).map(|typ| RelocationFlags::Coff { typ })
110 }
111 (Flavour::Coff, _) => coff::reloc(reference, after),
112 (Flavour::MachO, _) => crate::macho::reloc(reference, 0).ok(),
113 }
114 }
115
116 pub(crate) fn machine(self, arch: Arch) -> Option<Architecture> {
122 match (self, arch) {
123 (Flavour::Elf | Flavour::Coff, Arch::X86_64) => Some(Architecture::X86_64),
124 (Flavour::Elf | Flavour::Coff, Arch::Aarch64) => Some(Architecture::Aarch64),
125 (Flavour::Elf | Flavour::Coff, Arch::X86) => Some(Architecture::I386),
126 _ => None,
127 }
128 }
129
130 pub(crate) fn spell(self, machine: Architecture, name: &str) -> String {
138 match (self, machine) {
139 (Flavour::Coff, Architecture::I386) => coff::decorate(name),
140 _ => name.to_owned(),
141 }
142 }
143
144 pub(crate) fn see(
150 self,
151 obj: &mut Writer<'_>,
152 id: SymbolId,
153 binding: Binding,
154 visibility: Visibility,
155 ) {
156 match self {
157 Flavour::Elf => elf::see(obj, id, binding, visibility),
158 Flavour::Coff => {}
159 Flavour::MachO => {
162 if binding != Binding::Local && visibility == Visibility::Hidden {
163 obj.symbol_mut(id).scope = SymbolScope::Linkage;
164 }
165 }
166 }
167 }
168
169 fn rel_ro_local(self) -> Option<&'static str> {
173 match self {
174 Flavour::Elf => elf::REL_RO_LOCAL,
175 Flavour::Coff => coff::REL_RO_LOCAL,
176 Flavour::MachO => None,
177 }
178 }
179
180 pub(crate) fn debug(self, kind: Reference, into_debug: bool) -> Reference {
192 match kind {
193 Reference::Address { bytes: 4 } if self == Flavour::Coff && into_debug => {
194 Reference::Section
195 }
196 kind => kind,
197 }
198 }
199
200 fn gathered(self, array: Array) -> Option<SectionFlags> {
201 match self {
202 Flavour::Elf => Some(elf::gathered(array)),
203 Flavour::Coff | Flavour::MachO => None,
204 }
205 }
206
207 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
216 match self {
217 Flavour::Elf => {
218 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
219 }
220 Flavour::Coff => (shape.coff != 0).then_some(SectionFlags::Coff {
221 characteristics: object::pe::SectionFlags(shape.coff),
222 }),
223 Flavour::MachO => Some(SectionFlags::MachO {
224 flags: object::macho::SectionFlags(shape.mach),
225 reserved2: 0,
226 }),
227 }
228 }
229
230 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
246 if self == Flavour::MachO {
247 return match sort {
248 crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
249 crate::source::Sort::File => SymbolKind::File,
250 _ => SymbolKind::Data,
251 };
252 }
253 match sort {
254 crate::source::Sort::Func | crate::source::Sort::Ifunc => SymbolKind::Text,
257 crate::source::Sort::Object => SymbolKind::Data,
258 crate::source::Sort::Thread => SymbolKind::Tls,
259 crate::source::Sort::File => SymbolKind::File,
260 crate::source::Sort::Untyped => match (self, binding) {
261 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
262 _ => SymbolKind::Label,
263 },
264 }
265 }
266
267 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
269 match self {
270 Flavour::Elf => elf::marker(obj),
271 Flavour::Coff => coff::marker(obj),
272 Flavour::MachO => {}
273 }
274 }
275
276 fn property(self, obj: &mut Writer<'_>, property: Property) {
282 if !property.any() {
283 return;
284 }
285 match self {
286 Flavour::Elf => {
287 let note = obj.section_id(StandardSection::GnuProperty);
288 let align = if obj.architecture() == Architecture::I386 { 4 } else { 8 };
289 obj.append_section_data(note, &elf::record(property, align), u64::from(align));
290 }
291 Flavour::Coff | Flavour::MachO => {}
292 }
293 }
294
295 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
299 match self {
300 Flavour::Elf => (elf::FRAMES, None),
301 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
302 Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
303 }
304 }
305
306 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
308 match self {
309 Flavour::Elf => elf::link(bytes, ordered),
310 Flavour::Coff | Flavour::MachO => {
311 debug_assert!(ordered.is_empty(), "a record this format cannot write");
312 }
313 }
314 }
315}
316
317#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum Error {
320 Format {
322 triple: String,
324 },
325 Refused {
327 why: String,
329 },
330}
331
332impl std::fmt::Display for Error {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 match self {
335 Error::Format { triple } => {
336 write!(f, "there is no object writer for {triple} in this compiler yet")
337 }
338 Error::Refused { why } => {
339 write!(f, "the object writer refused what it was given: {why}")
340 }
341 }
342 }
343}
344
345impl std::error::Error for Error {}
346
347pub fn write(
363 text: &Text,
364 data: &Data,
365 aliases: &[Alias],
366 target: &TargetInfo,
367 output: Output,
368 info: &Info,
369) -> Result<Vec<u8>, Error> {
370 let Output { sections, property, ident, .. } = output;
371 let Some((flavour, machine)) = written(target) else {
372 return Err(Error::Format { triple: target.tuple.to_string() });
373 };
374 if flavour == Flavour::Coff {
375 beyond(text, data)?;
376 }
377 let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
378 obj.set_mangling(Mangling::None);
381 let spell = |name: &str| flavour.spell(machine, name).into_bytes();
382 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
384 let whole = obj.section_id(StandardSection::Text);
388 if flavour == Flavour::Elf {
392 obj.section_id(StandardSection::Data);
393 obj.section_id(StandardSection::UninitializedData);
394 }
395 if !sections.functions {
396 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
397 }
398
399 let mut symbols = BTreeMap::new();
403 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
408 let mut ordered: Vec<String> = Vec::new();
411 for func in &text.funcs {
412 let ahead = func.patch.map_or(0, |patch| patch.before);
422 let (section, at) = if sections.functions {
423 let name = format!(".text.{}", func.name).into_bytes();
424 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
425 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
426 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
427 (id, ahead as u64)
428 } else {
429 (whole, func.start as u64)
430 };
431 if let Some(patch) = func.patch {
446 let base = if sections.functions { func.start - ahead } else { 0 };
447 let name = elf::PATCHABLE.as_bytes().to_vec();
448 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
449 obj.section_mut(id).flags = elf::ordered();
450 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
451 let symbol = obj.section_symbol(section);
452 let flags =
453 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(
454 || Error::Refused { why: "no relocation holds an address here".to_owned() },
455 )?;
456 relocate(
457 &mut obj,
458 id,
459 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
460 )?;
461 ordered.push(if sections.functions {
462 format!(".text.{}", func.name)
463 } else {
464 ".text".to_owned()
465 });
466 }
467 let id = obj.add_symbol(Symbol {
468 name: spell(&func.name),
469 value: at,
470 size: func.len as u64,
471 kind: SymbolKind::Text,
472 scope: scope_of(func.binding),
473 weak: func.binding == Binding::Weak,
474 section: SymbolSection::Section(section),
475 flags: SymbolFlags::None,
476 });
477 flavour.see(&mut obj, id, func.binding, func.visibility);
478 symbols.insert(func.name.clone(), id);
479 split.push((section, at));
480 }
481
482 let mut places: Places = BTreeMap::new();
486 for label in &text.labels {
487 let after = text.funcs.partition_point(|func| func.start <= label.at);
488 let Some(index) = after.checked_sub(1) else {
489 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
490 return Err(Error::Refused { why });
491 };
492 let func = &text.funcs[index];
493 let (section, at) = if sections.functions {
494 let base = func.start - func.patch.map_or(0, |patch| patch.before);
497 (split[index].0, (label.at - base) as u64)
498 } else {
499 (whole, label.at as u64)
500 };
501 if flavour == Flavour::Elf {
507 places.insert(label.name.clone(), (section, at));
508 continue;
509 }
510 let id = obj.add_symbol(Symbol {
511 name: label.name.clone().into_bytes(),
512 value: at,
513 size: 0,
516 kind: SymbolKind::Label,
517 scope: SymbolScope::Compilation,
521 weak: false,
522 section: SymbolSection::Section(section),
523 flags: SymbolFlags::None,
524 });
525 symbols.insert(label.name.clone(), id);
526 }
527
528 if !text.mcount.is_empty() {
534 let name = crate::section::MCOUNT_LOC.as_bytes().to_vec();
535 let id = obj.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
536 let flags =
537 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(|| {
538 Error::Refused { why: "no relocation holds an address here".to_owned() }
539 })?;
540 for &call in &text.mcount {
541 let after = text.funcs.partition_point(|func| func.start <= call);
542 let Some(index) = after.checked_sub(1) else {
543 let why = format!("a profiler call at {call} is in front of every function");
544 return Err(Error::Refused { why });
545 };
546 let func = &text.funcs[index];
547 let (section, at) = if sections.functions {
548 let base = func.start - func.patch.map_or(0, |patch| patch.before);
549 (split[index].0, call - base)
550 } else {
551 (whole, call)
552 };
553 let offset =
554 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
555 let symbol = obj.section_symbol(section);
556 relocate(&mut obj, id, Relocation { offset, symbol, addend: at as i64, flags })?;
557 }
558 }
559
560 let mut placed = Vec::with_capacity(data.objects.len());
565 let mut named = Map::default();
569 for object in &data.objects {
570 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
571 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
575 obj.section_symbol(section);
576 }
577 let id = obj.add_symbol(Symbol {
578 name: spell(&object.name),
579 value: if object.place == Place::Merged { object.align } else { offset },
582 size: object.size,
583 kind: match object.place {
588 Place::Thread { .. } => SymbolKind::Tls,
589 _ => SymbolKind::Data,
590 },
591 scope: scope_of(object.binding),
592 weak: object.binding == Binding::Weak,
593 section,
594 flags: SymbolFlags::None,
595 });
596 flavour.see(&mut obj, id, object.binding, object.visibility);
597 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
601 obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
602 }
603 symbols.insert(object.name.clone(), id);
604 placed.push((section.id(), offset));
605 }
606
607 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
610
611 for apart in &data.apart {
615 let (Some(section), offset) = placed[apart.object] else { continue };
616 let value = distance(&obj, &symbols, &places, apart)?;
617 let bytes = usize::from(apart.bytes);
618 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
619 let at = at + apart.at;
620 let image = obj.section_mut(section).data_mut();
621 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
622 }
623
624 let written: BTreeMap<&str, usize> = text
632 .funcs
633 .iter()
634 .map(|func| func.name.as_str())
635 .chain(data.objects.iter().map(|object| object.name.as_str()))
636 .enumerate()
637 .map(|(at, name)| (name, at))
638 .collect();
639 let mut by_target: Vec<&Alias> = aliases.iter().collect();
640 by_target
641 .sort_by_key(|alias| written.get(alias.target.as_str()).copied().unwrap_or(usize::MAX));
642 for alias in by_target {
643 let Some(&id) = symbols.get(&alias.target) else {
644 let why =
645 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
646 return Err(Error::Refused { why });
647 };
648 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
649 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
650 let id = obj.add_symbol(Symbol {
651 name: spell(&alias.name),
652 value,
653 size,
654 kind,
655 scope: scope_of(alias.binding),
656 weak: alias.binding == Binding::Weak,
657 section,
658 flags: SymbolFlags::None,
659 });
660 flavour.see(&mut obj, id, alias.binding, alias.visibility);
661 if alias.ifunc {
662 if flavour != Flavour::Elf {
663 let why = format!("'{}' is an indirect function, which only ELF has", alias.name);
664 return Err(Error::Refused { why });
665 }
666 elf::indirect(&mut obj, id);
667 }
668 symbols.insert(alias.name.clone(), id);
669 }
670
671 let weak: Set<&str> = data.weak.iter().map(String::as_str).collect();
678 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
679 let thread: Set<&str> = relocs()
684 .filter(|reloc| reloc.kind == Reference::Thread)
685 .map(|reloc| reloc.symbol.as_str())
686 .collect();
687 let wanted: Vec<&String> =
688 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
689 for name in wanted {
690 if symbols.contains_key(name) || tables.contains_key(name) || places.contains_key(name) {
691 continue;
692 }
693 let id = obj.add_symbol(Symbol {
694 name: spell(name),
695 value: 0,
696 size: 0,
697 kind: if thread.contains(name.as_str()) {
707 SymbolKind::Tls
708 } else {
709 SymbolKind::Unknown
710 },
711 scope: SymbolScope::Dynamic,
712 weak: weak.contains(name.as_str()),
713 section: SymbolSection::Undefined,
714 flags: SymbolFlags::None,
715 });
716 symbols.insert(name.clone(), id);
717 }
718
719 for reloc in &text.relocs {
720 let (section, at) = if sections.functions {
725 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
726 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
727 let why = format!("a relocation at {} is in front of every function", reloc.at);
728 return Err(Error::Refused { why });
729 };
730 let base = func.start - func.patch.map_or(0, |patch| patch.before);
733 (split[after - 1].0, (reloc.at - base) as u64)
734 } else {
735 (whole, reloc.at as u64)
736 };
737 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
741 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
742 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
743 })?;
744 let symbol = obj.section_symbol(table);
745 let addend = reloc.addend + offset as i64;
746 relocate(&mut obj, section, Relocation { offset: at, symbol, addend, flags })?;
747 continue;
748 }
749 add(&mut obj, section, at, reloc, &symbols, &places, flavour)?;
750 }
751
752 let seh_free = flavour == Flavour::Coff && machine == Architecture::I386;
762 if !text.unwind.bytes.is_empty() && !seh_free {
763 let ((name, align), second) = flavour.tables();
764 let align = if machine == Architecture::I386 { 4 } else { align };
767 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
768 obj.append_section_data(frames, &text.unwind.bytes, align);
769 let mut described = Map::default();
774 if !text.unwind.info.is_empty() {
775 let Some((name, align)) = second else {
776 let why = "an unwind table here is one section and it was given two".to_owned();
777 return Err(Error::Refused { why });
778 };
779 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
780 obj.append_section_data(codes, &text.unwind.info, align);
781 for label in &text.unwind.labels {
782 let id = obj.add_symbol(Symbol {
783 name: label.name.clone().into_bytes(),
784 value: label.at as u64,
785 size: 0,
786 kind: SymbolKind::Label,
787 scope: SymbolScope::Compilation,
788 weak: false,
789 section: SymbolSection::Section(codes),
790 flags: SymbolFlags::None,
791 });
792 described.insert(label.name.clone(), id);
793 }
794 }
795 if !text.unwind.except.is_empty() {
800 let except =
801 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
802 obj.append_section_data(except, &text.unwind.except, 4);
803 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
804 }
805 for reloc in &text.unwind.relocs {
806 let found = described.get(&reloc.symbol).or_else(|| {
807 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
811 if ours { symbols.get(&reloc.symbol) } else { None }
812 });
813 let (symbol, addend) = match found {
814 Some(&id) => (id, reloc.addend),
818 None => {
832 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
833 let Some((section, at)) = found.map(|i| split[i]) else {
834 let why = format!(
835 "'{}' has an unwind record and is not a function here",
836 reloc.symbol
837 );
838 return Err(Error::Refused { why });
839 };
840 (obj.section_symbol(section), reloc.addend + at as i64)
844 }
845 };
846 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
847 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
848 })?;
849 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
850 relocate(&mut obj, frames, record)?;
851 }
852 }
853 let mut named = Map::default();
862 for chunk in &info.chunks {
863 let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
866 info.compress
867 } else {
868 Compress::None
869 };
870 let id = crate::zlib::debug_section(&mut obj, chunk, how);
871 named.insert(chunk.name.as_str(), id);
872 }
873 for chunk in &info.chunks {
874 let section = named[chunk.name.as_str()];
875 for reloc in &chunk.relocs {
876 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
877 Some(&id) => (obj.section_symbol(id), reloc.addend),
880 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
885 Some(which) => {
886 let (section, at) = split[which];
887 (obj.section_symbol(section), reloc.addend + at as i64)
888 }
889 None => {
895 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
896 let Some(which) = found else {
897 let why = format!(
898 "'{}' is named by the debug information and is not defined here",
899 reloc.symbol
900 );
901 return Err(Error::Refused { why });
902 };
903 match placed[which] {
904 (Some(section), at) => {
905 (obj.section_symbol(section), reloc.addend + at as i64)
906 }
907 (None, _) => (symbols[&reloc.symbol], reloc.addend),
908 }
909 }
910 },
911 };
912 let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
913 let flags = flavour
914 .reloc(machine, kind, reloc.after)
915 .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
916 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
917 relocate(&mut obj, section, record)?;
918 }
919 }
920 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
921 let Some(section) = section else { continue };
922 for reloc in &object.relocs {
923 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, &places, flavour)?;
924 }
925 }
926
927 if !data.exports.is_empty() {
931 let options: String = data.exports.iter().map(Export::option).collect();
932 let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
933 obj.append_section_data(id, options.as_bytes(), 1);
934 }
935
936 flavour.property(&mut obj, property);
940
941 if let Some(ident) = ident.filter(|_| flavour == Flavour::Elf) {
943 let id = obj.add_section(Vec::new(), b".comment".to_vec(), SectionKind::OtherString);
944 let bytes = [&[0][..], ident.as_bytes(), &[0]].concat();
945 obj.append_section_data(id, &bytes, 1);
946 }
947
948 flavour.marker(&mut obj);
951
952 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
953 flavour.finish(&mut bytes, &ordered);
954 Ok(bytes)
955}
956
957type Places = BTreeMap<String, (object::write::SectionId, u64)>;
960
961fn distance(
969 obj: &Writer<'_>,
970 symbols: &BTreeMap<String, SymbolId>,
971 places: &Places,
972 apart: &Apart,
973) -> Result<i64, Error> {
974 let find = |name: &str| match (symbols.get(name), places.get(name)) {
975 (Some(&id), _) => Ok((obj.symbol(id).section, obj.symbol(id).value)),
976 (None, Some(&(section, at))) => Ok((SymbolSection::Section(section), at)),
977 (None, None) => {
978 Err(Error::Refused { why: format!("'{name}' is measured from and is not here") })
979 }
980 };
981 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
982 if to.0 != from.0 {
983 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
984 return Err(Error::Refused { why });
985 }
986 let value = (to.1 as i64).wrapping_sub(from.1 as i64).wrapping_add(apart.addend);
987 let bits = u32::from(apart.bytes) * 8;
988 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
989 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
990 return Err(Error::Refused { why });
991 }
992 Ok(value)
993}
994
995fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
1008 let why = |why: String| Err(Error::Refused { why });
1009 if text.funcs.iter().any(|func| func.patch.is_some()) {
1010 return why("a record of where a patcher's room is has no section flags here".to_owned());
1011 }
1012 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
1013 if matches!(
1014 reloc.kind,
1015 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
1016 ) {
1017 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
1018 }
1019 }
1020 for object in &data.objects {
1021 if matches!(object.place, Place::Thread { zero: true }) {
1022 return why(format!(
1023 "'{}' is zeroed thread-local storage, which is not here",
1024 object.name
1025 ));
1026 }
1027 let Place::Named(name, _) = &object.place else { continue };
1028 if Array::of(name).is_some() {
1029 return why(format!("'{name}' is not a list the startup code here gathers"));
1030 }
1031 }
1032 Ok(())
1033}
1034
1035pub fn defines(
1058 text: &Text,
1059 data: &Data,
1060 aliases: &[Alias],
1061 target: &TargetInfo,
1062) -> Result<Vec<String>, Error> {
1063 let Some((flavour, machine)) = written(target) else {
1064 return Err(Error::Format { triple: target.tuple.to_string() });
1065 };
1066 let spell = |name: &String| flavour.spell(machine, name);
1067 let names = text
1068 .funcs
1069 .iter()
1070 .filter(|func| func.binding != Binding::Local)
1071 .map(|func| spell(&func.name))
1072 .chain(
1073 data.objects
1074 .iter()
1075 .filter(|object| object.binding != Binding::Local)
1076 .map(|object| spell(&object.name)),
1077 )
1078 .chain(
1079 aliases
1080 .iter()
1081 .filter(|alias| alias.binding != Binding::Local)
1082 .map(|alias| spell(&alias.name)),
1083 )
1084 .collect();
1085 Ok(names)
1086}
1087
1088fn put(
1095 obj: &mut Writer<'_>,
1096 object: &Object,
1097 named: &mut Map<String, object::write::SectionId>,
1098 sections: Sections,
1099 flavour: Flavour,
1100) -> (SymbolSection, u64) {
1101 if sections.data {
1107 if let Some(name) = object.place.split(&object.name) {
1108 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
1109 let offset = if carries_no_bytes(&object.place) {
1110 obj.append_section_bss(section, object.size, object.align)
1111 } else {
1112 obj.append_section_data(section, &object.bytes, object.align)
1113 };
1114 return (SymbolSection::Section(section), offset);
1115 }
1116 }
1117 let section = match &object.place {
1118 Place::Written => obj.section_id(StandardSection::Data),
1119 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
1120 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
1126 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
1127 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
1128 },
1129 Place::Zero => obj.section_id(StandardSection::UninitializedData),
1130 Place::Strings { align } => {
1133 made(obj, named, &Place::strings(*align), SectionKind::ReadOnlyString)
1134 }
1135 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
1136 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
1137 Place::Merged => return (SymbolSection::Common, 0),
1138 Place::Named(name, _) => {
1145 let section = made(obj, named, name, kind_of(&object.place));
1146 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
1147 obj.section_mut(section).flags = flags;
1148 }
1149 section
1150 }
1151 Place::Pointer => {
1155 let name = format!(".rdata${}", object.name);
1156 made(obj, named, &name, SectionKind::ReadOnlyData)
1157 }
1158 };
1159 let offset = if carries_no_bytes(&object.place) {
1160 obj.append_section_bss(section, object.size, object.align)
1161 } else {
1162 obj.append_section_data(section, &object.bytes, object.align)
1163 };
1164 (SymbolSection::Section(section), offset)
1165}
1166
1167fn tables(
1180 obj: &mut Writer<'_>,
1181 text: &Text,
1182 split: &[(object::write::SectionId, u64)],
1183 named: &mut Map<String, object::write::SectionId>,
1184 sections: Sections,
1185 flavour: Flavour,
1186) -> Result<Map<String, (object::write::SectionId, u64)>, Error> {
1187 let mut placed = Map::default();
1188 if text.tables.is_empty() {
1189 return Ok(placed);
1190 }
1191 if flavour != Flavour::Elf {
1192 let why = "a jump table outside the code is written on ELF only".to_owned();
1193 return Err(Error::Refused { why });
1194 }
1195 let machine = obj.architecture();
1196 let flags = flavour.reloc(machine, Reference::Away, 0).ok_or_else(|| Error::Refused {
1197 why: "no relocation is a distance from where it is written".to_owned(),
1198 })?;
1199 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
1201 for table in &text.tables {
1202 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1203 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1204 })?;
1205 let section = if sections.data {
1206 let name = format!(".rodata.{}", func.name);
1207 made(obj, named, &name, SectionKind::ReadOnlyData)
1208 } else {
1209 obj.section_id(StandardSection::ReadOnlyData)
1210 };
1211 let (width, flags) = if table.absolute {
1214 let reference = Reference::Address { bytes: pointer };
1215 let wide = flavour.reloc(machine, reference, 0).ok_or_else(|| Error::Refused {
1216 why: format!("no relocation is an address in {pointer} bytes"),
1217 })?;
1218 (usize::from(pointer), wide)
1219 } else {
1220 (4, flags)
1221 };
1222 let offset =
1223 obj.append_section_data(section, &vec![0; width * table.cells.len()], width as u64);
1224 placed.insert(table.name.clone(), (section, offset));
1225 let (code, at) = split[table.func];
1226 let symbol = obj.section_symbol(code);
1227 for (index, &cell) in table.cells.iter().enumerate() {
1228 let place = (width * index) as u64;
1229 let addend = at as i64 + cell as i64 + if table.absolute { 0 } else { place as i64 };
1230 let record = Relocation { offset: offset + place, symbol, addend, flags };
1231 relocate(obj, section, record)?;
1232 }
1233 }
1234 Ok(placed)
1235}
1236
1237fn carries_no_bytes(place: &Place) -> bool {
1243 matches!(place, Place::Zero | Place::Thread { zero: true } | Place::Named(_, Holds::Zero))
1244}
1245
1246fn made(
1254 obj: &mut Writer<'_>,
1255 named: &mut Map<String, object::write::SectionId>,
1256 name: &str,
1257 kind: SectionKind,
1258) -> object::write::SectionId {
1259 if let Some(section) = named.get(name) {
1260 return *section;
1261 }
1262 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1263 named.insert(name.to_owned(), section);
1264 section
1265}
1266
1267fn kind_of(place: &Place) -> SectionKind {
1279 match place {
1280 Place::ReadOnly | Place::Pointer | Place::Named(_, Holds::ReadOnly) => {
1281 SectionKind::ReadOnlyData
1282 }
1283 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1284 Place::Strings { .. } => SectionKind::ReadOnlyString,
1285 Place::Zero | Place::Named(_, Holds::Zero) => SectionKind::UninitializedData,
1286 Place::Thread { zero: false } => SectionKind::Tls,
1287 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1288 Place::Written | Place::Merged | Place::Named(_, Holds::Written) => SectionKind::Data,
1289 }
1290}
1291
1292fn add(
1299 obj: &mut Writer<'_>,
1300 section: object::write::SectionId,
1301 at: u64,
1302 reloc: &Reloc,
1303 symbols: &BTreeMap<String, SymbolId>,
1304 places: &Places,
1305 flavour: Flavour,
1306) -> Result<(), Error> {
1307 let flags = flavour
1308 .reloc(obj.architecture(), reloc.kind, reloc.after)
1309 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1310 let (symbol, addend) = match places.get(&reloc.symbol) {
1312 Some(&(held, offset)) => (obj.section_symbol(held), reloc.addend + offset as i64),
1313 None => (symbols[&reloc.symbol], reloc.addend),
1314 };
1315 relocate(obj, section, Relocation { offset: at, symbol, addend, flags })
1316}
1317
1318pub(crate) fn relocate(
1337 obj: &mut Writer<'_>,
1338 section: object::write::SectionId,
1339 mut relocation: Relocation,
1340) -> Result<(), Error> {
1341 if let (Architecture::I386, RelocationFlags::Elf { r_type }) =
1342 (obj.architecture(), relocation.flags)
1343 {
1344 let Some(width) = elf::width_i386(r_type) else {
1345 let why = format!("relocation type {} has no width this writer knows", r_type.0);
1346 return Err(Error::Refused { why });
1347 };
1348 let addend = relocation.addend;
1349 let bits = 8 * width as u32;
1350 let least = if width == 4 { -(1i64 << bits) } else { -(1i64 << (bits - 1)) };
1356 if addend < least || addend >= 1i64 << bits {
1357 let why =
1358 format!("{addend} added to a name, and there are {width} bytes to keep it in");
1359 return Err(Error::Refused { why });
1360 }
1361 let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1362 let data = obj.section_mut(section).data_mut();
1363 let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1364 let why = format!("a relocation at {at} is past the end of its section");
1365 return Err(Error::Refused { why });
1366 };
1367 place.copy_from_slice(&addend.to_le_bytes()[..width]);
1368 relocation.addend = 0;
1369 }
1370 obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1371}
1372
1373fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1379 let flavour = Flavour::of(target)?;
1380 let machine = flavour.machine(target.tuple.arch())?;
1381 (machine != Architecture::Aarch64).then_some((flavour, machine))
1382}
1383
1384pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1397 match binding {
1398 Binding::Local => SymbolScope::Compilation,
1399 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1400 }
1401}
1402
1403#[cfg(test)]
1404mod tests {
1405 use super::*;
1406
1407 use object::read::elf::Sym as _;
1408 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1409 use object::{elf, pe};
1410 use rucc_target::{Arch, Env, Os, Triple};
1411
1412 use crate::elf::PATCHABLE;
1413 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1414
1415 fn target() -> TargetInfo {
1417 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1418 }
1419
1420 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1425 Extent {
1426 name,
1427 start,
1428 len,
1429 align: crate::FUNC_ALIGN,
1430 binding,
1431 visibility: Visibility::Default,
1432 patch: None,
1433 landings: Vec::new(),
1434 }
1435 }
1436
1437 fn calling(name: &str) -> Text {
1439 Text {
1440 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1441 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1442 relocs: vec![Reloc {
1443 at: 1,
1444 symbol: name.to_owned(),
1445 kind: Reference::Call,
1446 addend: -4,
1447 after: 0,
1448 }],
1449 ..Text::default()
1450 }
1451 }
1452
1453 #[test]
1454 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1455 let text = calling("puts");
1456 let bytes =
1457 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1458 .expect("an object");
1459 let file = object::File::parse(&bytes[..]).expect("a readable object");
1460 let section = file.section_by_name(".text").expect("a text section");
1461 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1462 }
1463
1464 #[test]
1465 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1466 let mut text = calling("puts");
1467 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1468 text.bytes.resize(17, 0x90);
1469 let bytes =
1470 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1471 .expect("an object");
1472 let file = object::File::parse(&bytes[..]).expect("a readable object");
1473 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1474 assert_eq!(g.address(), 16);
1475 assert_eq!(g.size(), 1);
1476 assert_eq!(g.kind(), SymbolKind::Text);
1477 assert!(g.is_global(), "nothing said otherwise about this one");
1478 }
1479
1480 #[test]
1481 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1482 let mut text = calling("puts");
1483 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1484 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1485 text.bytes.resize(33, 0x90);
1486 let bytes =
1487 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1488 .expect("an object");
1489 let file = object::File::parse(&bytes[..]).expect("a readable object");
1490 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1491 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1494 assert!(!hidden.is_weak());
1495 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1496 assert!(shared.is_weak(), "a weak function has to be able to lose");
1497 assert!(shared.is_global());
1498 }
1499
1500 #[test]
1517 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1518 let mut text = calling("puts");
1519 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1520 text.funcs[0].start = 3;
1521 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1522 text.relocs[0].at = 4;
1523 let bytes =
1524 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1525 .expect("an object");
1526 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1527 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1528 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1529 assert_eq!(section.align(), 8);
1530 let header = section.elf_section_header();
1531 assert_eq!(
1532 header.sh_flags.get(Endianness::Little),
1533 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1534 );
1535 let index = file.section_by_name(".text").expect("a text section").index().0;
1538 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1539 assert_ne!(index, 0);
1540
1541 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1543 panic!("one address in the record")
1544 };
1545 assert_eq!(*at, 0);
1546 assert_eq!(reloc.addend(), 0);
1547 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1548 }
1549
1550 #[test]
1552 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1553 let text = calling("puts");
1554 let bytes =
1555 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1556 .expect("an object");
1557 let file = object::File::parse(&bytes[..]).expect("a readable object");
1558 assert!(file.section_by_name(PATCHABLE).is_none());
1559 }
1560
1561 #[test]
1567 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1568 let mut text = calling("puts");
1569 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1570 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1571 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1572 text.bytes.resize(17, 0x90);
1573 let output =
1574 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1575 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1576 .expect("an object");
1577 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1578 let links: Vec<usize> = file
1579 .sections()
1580 .filter(|section| section.name() == Ok(PATCHABLE))
1581 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1582 .collect();
1583 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1584 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1585 }
1586
1587 #[test]
1588 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1589 let mut text = calling("puts");
1590 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1591 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1592 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1593 text.bytes.resize(49, 0x90);
1594 let bytes =
1595 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1596 .expect("an object");
1597 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1598 let visibility = |name: &str| {
1599 file.symbols()
1600 .find(|s| s.name() == Ok(name))
1601 .expect("the function")
1602 .elf_symbol()
1603 .st_visibility()
1604 };
1605 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1607 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1608 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1611 }
1612
1613 #[test]
1623 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1624 let mut text = calling("puts");
1625 for (index, (name, seen)) in
1626 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1627 {
1628 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1629 func.visibility = seen;
1630 text.funcs.push(func);
1631 }
1632 text.bytes.resize(49, 0x90);
1633 let mut data = Data::default();
1634 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1635 let mut object = variable(name, Place::Written);
1636 object.visibility = seen;
1637 data.objects.push(object);
1638 }
1639 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1640 .expect("an object");
1641 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1642 let visibility = |name: &str| {
1643 file.symbols()
1644 .find(|s| s.name() == Ok(name))
1645 .expect("the symbol")
1646 .elf_symbol()
1647 .st_visibility()
1648 };
1649 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1650 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1651 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1652 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1653 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1656 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1657 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1658 }
1659
1660 #[test]
1661 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1662 let bytes = write(
1663 &calling("puts"),
1664 &Data::default(),
1665 &[],
1666 &target(),
1667 Output::default(),
1668 &Info::default(),
1669 )
1670 .expect("an object");
1671 let file = object::File::parse(&bytes[..]).expect("a readable object");
1672 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1673 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1674 }
1675
1676 #[test]
1677 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1678 for (reference, wanted) in [
1679 (Reference::Call, elf::R_X86_64_PLT32),
1680 (Reference::Data, elf::R_X86_64_PC32),
1681 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1682 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1683 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1684 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1685 ] {
1686 let mut text = calling("puts");
1687 text.relocs[0].kind = reference;
1688 let bytes =
1689 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1690 .expect("an object");
1691 let file = object::File::parse(&bytes[..]).expect("a readable object");
1692 let section = file.section_by_name(".text").expect("a text section");
1693 let (offset, reloc) = section.relocations().next().expect("one relocation");
1694 assert_eq!(offset, 1);
1695 assert_eq!(reloc.addend(), -4);
1696 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1697 }
1698 }
1699
1700 #[test]
1703 fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1704 for (reference, wanted) in [
1705 (Reference::AwayWide, elf::R_X86_64_PC64),
1706 (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1707 (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1708 ] {
1709 assert_eq!(crate::elf::r_type(reference), Some(wanted));
1710 }
1711 assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1712 }
1713
1714 #[test]
1715 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1716 let mut text = calling("puts");
1717 text.relocs.push(Reloc {
1718 at: 1,
1719 symbol: "puts".to_owned(),
1720 kind: Reference::Call,
1721 addend: -4,
1722 after: 0,
1723 });
1724 let bytes =
1725 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1726 .expect("an object");
1727 let file = object::File::parse(&bytes[..]).expect("a readable object");
1728 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1729 }
1730
1731 #[test]
1732 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1733 let text = calling("f");
1734 let bytes =
1735 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1736 .expect("an object");
1737 let file = object::File::parse(&bytes[..]).expect("a readable object");
1738 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1739 let f = found.next().expect("the function");
1740 assert!(!f.is_undefined(), "the file defines it");
1741 assert!(found.next().is_none(), "and defines it once");
1742 }
1743
1744 #[test]
1745 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1746 let bytes = write(
1747 &calling("puts"),
1748 &Data::default(),
1749 &[],
1750 &target(),
1751 Output::default(),
1752 &Info::default(),
1753 )
1754 .expect("an object");
1755 let file = object::File::parse(&bytes[..]).expect("a readable object");
1756 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1757 assert!(note.data().expect("no bytes").is_empty());
1758 }
1759
1760 #[test]
1767 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1768 let property = Property { features: Property::IBT | Property::SHSTK };
1769 let output = Output { property, ..Output::default() };
1770 let bytes =
1771 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1772 .expect("an object");
1773 let file = object::File::parse(&bytes[..]).expect("a readable object");
1774 let note = file.section_by_name(".note.gnu.property").expect("the note");
1775 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1776 let want: Vec<u8> = [
1777 4u32,
1778 16,
1779 5,
1780 u32::from_le_bytes(*b"GNU\0"),
1781 Property::X86_FEATURES,
1782 4,
1783 Property::IBT | Property::SHSTK,
1784 0,
1785 ]
1786 .iter()
1787 .flat_map(|word| word.to_le_bytes())
1788 .collect();
1789 assert_eq!(note.data().expect("the bytes"), &want[..]);
1790 }
1791
1792 #[test]
1798 fn a_file_built_to_have_nothing_checked_says_nothing() {
1799 let bytes = write(
1800 &calling("puts"),
1801 &Data::default(),
1802 &[],
1803 &target(),
1804 Output::default(),
1805 &Info::default(),
1806 )
1807 .expect("an object");
1808 let file = object::File::parse(&bytes[..]).expect("a readable object");
1809 assert!(file.section_by_name(".note.gnu.property").is_none());
1810 }
1811
1812 #[test]
1821 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1822 let mut text = calling("puts");
1823 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1824 text.bytes.resize(17, 0x90);
1825 text.unwind.bytes = vec![0; 64];
1828 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1829 text.unwind.relocs.push(Reloc {
1830 at,
1831 symbol: name.to_owned(),
1832 kind: Reference::Address { bytes: 8 },
1833 addend: 0,
1834 after: 0,
1835 });
1836 }
1837 let bytes =
1838 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1839 .expect("an object");
1840 let file = object::File::parse(&bytes[..]).expect("a readable object");
1841 let mut found = points_at(&file);
1842 found.sort_unstable();
1843 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1844 }
1845
1846 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1849 let frames = file.section_by_name(".eh_frame").expect("the table");
1850 frames
1851 .relocations()
1852 .map(|(offset, reloc)| {
1853 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1854 panic!("a record points at something that is not a symbol");
1855 };
1856 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1857 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1858 let section = symbol.section_index().expect("a section symbol is in one");
1859 let name = file.section_by_index(section).expect("a readable section");
1860 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1861 })
1862 .collect()
1863 }
1864
1865 #[test]
1878 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1879 let mut text = two();
1880 text.unwind.bytes = vec![0; 64];
1881 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1882 text.unwind.relocs.push(Reloc {
1883 at,
1884 symbol: name.to_owned(),
1885 kind: Reference::Data,
1886 addend: 0,
1887 after: 0,
1888 });
1889 }
1890 let bytes =
1891 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1892 .expect("an object");
1893 let file = object::File::parse(&bytes[..]).expect("a readable object");
1894 let mut whole = points_at(&file);
1895 whole.sort_unstable();
1896 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1897
1898 let sections =
1899 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1900 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1901 .expect("an object");
1902 let file = object::File::parse(&bytes[..]).expect("a readable object");
1903 let mut split = points_at(&file);
1904 split.sort_unstable();
1905 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1906 }
1907
1908 #[test]
1915 fn a_record_about_something_this_file_does_not_define_is_refused() {
1916 let mut text = calling("puts");
1917 text.unwind.bytes = vec![0; 64];
1918 text.unwind.relocs.push(Reloc {
1919 at: 32,
1920 symbol: "puts".to_owned(),
1921 kind: Reference::Data,
1922 addend: 0,
1923 after: 0,
1924 });
1925 let why =
1926 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1927 .expect_err("a record about a name from somewhere else");
1928 assert!(why.to_string().contains("puts"), "{why}");
1929 }
1930
1931 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1933 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1934 let index = symbol.section_index().expect("a section to be defined in");
1935 let section = file.section_by_index(index).expect("a readable section");
1936 section.name().expect("a named section").to_owned()
1937 }
1938
1939 fn two() -> Text {
1941 let mut text = calling("puts");
1942 text.bytes.resize(16, 0x90);
1945 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1946 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1947 text.relocs.push(Reloc {
1948 at: 17,
1949 symbol: "puts".to_owned(),
1950 kind: Reference::Call,
1951 addend: -4,
1952 after: 0,
1953 });
1954 text
1955 }
1956
1957 #[test]
1964 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1965 let sections =
1966 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1967 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1968 .expect("an object");
1969 let file = object::File::parse(&bytes[..]).expect("a readable object");
1970 assert_eq!(lives_in(&file, "f"), ".text.f");
1971 assert_eq!(lives_in(&file, "g"), ".text.g");
1972 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1973 for name in ["f", "g"] {
1976 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1977 assert_eq!(symbol.address(), 0, "{name}");
1978 assert_eq!(symbol.size(), 6, "{name}");
1979 }
1980 let section = file.section_by_name(".text.g").expect("the second function");
1981 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1982 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1985 }
1986
1987 #[test]
1993 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1994 let sections =
1995 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1996 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1997 .expect("an object");
1998 let file = object::File::parse(&bytes[..]).expect("a readable object");
1999 for name in [".text.f", ".text.g"] {
2000 let section = file.section_by_name(name).expect("a function");
2001 let (offset, _) = section.relocations().next().expect("the call in it");
2002 assert_eq!(offset, 1, "{name}");
2005 assert_eq!(section.relocations().count(), 1, "{name}");
2006 }
2007 }
2008
2009 fn switching() -> Text {
2011 let mut text = two();
2012 let name = ".Lg_j0".to_owned();
2013 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
2014 text
2015 }
2016
2017 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
2019 let section = file.section_by_name(section).expect("the table's section");
2020 section
2021 .relocations()
2022 .map(|(offset, reloc)| {
2023 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
2024 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2025 panic!("a cell against something that is not a symbol");
2026 };
2027 let symbol = file.symbol_by_index(index).expect("a symbol");
2028 assert_eq!(symbol.kind(), SymbolKind::Section);
2029 let at = symbol.section_index().expect("a section symbol is in one");
2030 let name = file.section_by_index(at).expect("a section").name().expect("a name");
2031 (offset, name.to_owned(), reloc.addend())
2032 })
2033 .collect()
2034 }
2035
2036 #[test]
2037 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
2038 let mut text = switching();
2041 text.relocs[1].symbol = ".Lg_j0".to_owned();
2042 text.relocs[1].kind = Reference::Data;
2043 let bytes =
2044 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2045 .expect("an object");
2046 let file = object::File::parse(&bytes[..]).expect("a readable object");
2047 let rodata = file.section_by_name(".rodata").expect("the table's section");
2048 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2049 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2050 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2051 let (at, reloc) = file
2052 .section_by_name(".text")
2053 .expect("the code")
2054 .relocations()
2055 .find(|(at, _)| *at == 17)
2056 .expect("the reference to the table");
2057 assert_eq!((at, reloc.addend()), (17, -4));
2058 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2059 panic!("a reference against something that is not a symbol");
2060 };
2061 let symbol = file.symbol_by_index(index).expect("a symbol");
2062 assert_eq!(symbol.section_index(), Some(rodata.index()));
2063 assert_eq!(symbol.kind(), SymbolKind::Section);
2064 assert_eq!(
2067 cells(&file, ".rodata"),
2068 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2069 );
2070 }
2071
2072 #[test]
2073 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2074 let sections =
2075 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2076 let bytes =
2077 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2078 .expect("an object");
2079 let file = object::File::parse(&bytes[..]).expect("a readable object");
2080 assert_eq!(
2082 cells(&file, ".rodata.g"),
2083 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2084 );
2085 }
2086
2087 #[test]
2088 fn a_jump_table_outside_the_code_is_refused_on_windows() {
2089 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2090 let written = write(
2091 &switching(),
2092 &Data::default(),
2093 &[],
2094 &target,
2095 Output::default(),
2096 &Info::default(),
2097 );
2098 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2099 }
2100
2101 #[test]
2104 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2105 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2106 let reloc = |at, symbol: &str, bytes| Reloc {
2107 at,
2108 symbol: symbol.to_owned(),
2109 kind: Reference::Address { bytes },
2110 addend: 0,
2111 after: 0,
2112 };
2113 let info = Info {
2114 chunks: vec![
2115 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2116 Chunk {
2117 name: ".debug_info".to_owned(),
2118 bytes: vec![0; 12],
2119 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2120 },
2121 ],
2122 ..Info::default()
2123 };
2124 let bytes =
2125 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2126 .expect("object");
2127 let file = object::File::parse(&bytes[..]).expect("a readable object");
2128 let section = file.section_by_name(".debug_info").expect("the debug section");
2129 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2130 assert_eq!(
2131 kinds,
2132 [
2133 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2134 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2135 ]
2136 );
2137 }
2138
2139 fn variable(name: &str, place: Place) -> Object {
2141 Object {
2142 name: name.to_owned(),
2143 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2144 size: 4,
2145 align: 4,
2146 place,
2147 binding: Binding::Global,
2148 visibility: Visibility::Default,
2149 relocs: Vec::new(),
2150 }
2151 }
2152
2153 fn measured() -> (Text, Data) {
2155 let mut text = calling("puts");
2156 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2157 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2158 let mut table = variable("table", Place::ReadOnly);
2159 table.bytes = vec![0; 8];
2160 table.size = 8;
2161 let apart = |at, to: &str, from: &str| Apart {
2162 object: 0,
2163 at,
2164 to: to.to_owned(),
2165 from: from.to_owned(),
2166 addend: 0,
2167 bytes: 4,
2168 };
2169 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2170 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2171 }
2172
2173 #[test]
2174 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2175 let (text, data) = measured();
2176 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2177 .expect("an object");
2178 let file = object::File::parse(&bytes[..]).expect("a readable object");
2179 let section = file.section_by_name(".rodata").expect("a read only section");
2180 assert_eq!(section.relocations().count(), 0);
2181 let image = section.data().expect("the image");
2182 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2183 }
2184
2185 #[test]
2189 fn a_label_an_image_holds_is_not_in_the_symbol_table() {
2190 let (text, mut data) = measured();
2191 data.apart.clear();
2192 data.objects[0].relocs.push(Reloc {
2193 at: 0,
2194 symbol: ".L1".to_owned(),
2195 kind: Reference::Address { bytes: 8 },
2196 addend: 0,
2197 after: 0,
2198 });
2199 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2200 .expect("an object");
2201 let file = object::File::parse(&bytes[..]).expect("a readable object");
2202 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2203 assert!(names.iter().all(|name| !name.starts_with(".L")), "{names:?}");
2204 let section = file.section_by_name(".rodata").expect("a read only section");
2205 let relocs: Vec<_> = section.relocations().collect();
2206 assert_eq!(relocs.len(), 1);
2207 let (_, reloc) = &relocs[0];
2208 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2209 panic!("a relocation against a symbol, not {reloc:?}");
2210 };
2211 let symbol = file.symbol_by_index(index).expect("a symbol");
2212 assert_eq!(symbol.kind(), SymbolKind::Section);
2213 assert_eq!(reloc.addend(), 5);
2214 }
2215
2216 #[test]
2217 fn a_distance_between_labels_in_two_sections_is_refused() {
2218 let (mut text, data) = measured();
2221 text.bytes.resize(22, 0x90);
2222 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2223 text.labels[1].at = 17;
2224 let output =
2225 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2226 let refused = write(&text, &data, &[], &target(), output, &Info::default());
2227 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2228 }
2229
2230 fn holding(object: Object) -> Vec<u8> {
2232 let data = Data {
2233 apart: Vec::new(),
2234 exports: Vec::new(),
2235 weak: Vec::new(),
2236 objects: vec![object],
2237 };
2238 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2239 .expect("an object")
2240 }
2241
2242 #[test]
2243 fn what_a_variable_is_decides_which_section_it_goes_in() {
2244 for (place, wanted) in [
2245 (Place::Written, ".data"),
2246 (Place::ReadOnly, ".rodata"),
2247 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2248 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2249 (Place::Zero, ".bss"),
2250 (Place::Thread { zero: false }, ".tdata"),
2251 (Place::Thread { zero: true }, ".tbss"),
2252 (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2253 ] {
2254 let bytes = holding(variable("x", place.clone()));
2255 let file = object::File::parse(&bytes[..]).expect("a readable object");
2256 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2257 assert_eq!(section.size(), 4, "{place:?}");
2258 let carried = section.data().expect("the bytes").len();
2261 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2262 }
2263 }
2264
2265 #[test]
2271 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2272 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2273 let bytes = holding(variable("counter", place.clone()));
2274 let file = object::File::parse(&bytes[..]).expect("a readable object");
2275 let symbol = file
2276 .symbols()
2277 .find(|symbol| symbol.name() == Ok("counter"))
2278 .unwrap_or_else(|| panic!("{place:?}"));
2279 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2280 }
2281 }
2282
2283 #[test]
2289 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2290 for (name, wanted) in [
2291 (".init_array", elf::SHT_INIT_ARRAY),
2292 (".init_array.00101", elf::SHT_INIT_ARRAY),
2293 (".fini_array", elf::SHT_FINI_ARRAY),
2294 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2295 (".init_arrays", elf::SHT_PROGBITS),
2296 ] {
2297 let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2298 let file = object::File::parse(&bytes[..]).expect("a readable object");
2299 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2300 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2301 panic!("{name} is not an elf section");
2302 };
2303 assert_eq!(sh_type, wanted, "{name}");
2304 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2305 }
2306 }
2307
2308 #[test]
2312 fn a_named_section_carries_the_flags_of_what_is_in_it() {
2313 for (name, holds, kind, flags) in [
2314 (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2315 (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2316 (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2317 ] {
2318 let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2319 let file = object::File::parse(&bytes[..]).expect("a readable object");
2320 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2321 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2322 panic!("{name} is not an elf section");
2323 };
2324 assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2325 assert_eq!(section.size(), 4, "{name}");
2326 }
2327 }
2328
2329 #[test]
2335 fn two_variables_in_one_named_section_share_it() {
2336 let objects = vec![
2337 variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2338 variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2339 ];
2340 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2341 let bytes =
2342 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2343 .expect("an object");
2344 let file = object::File::parse(&bytes[..]).expect("a readable object");
2345 let named: Vec<_> =
2346 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2347 assert_eq!(named.len(), 1);
2348 assert_eq!(named[0].size(), 8);
2349 }
2350
2351 #[test]
2355 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2356 let sections =
2357 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2358 for (place, wanted) in [
2359 (Place::Written, ".data.x"),
2360 (Place::ReadOnly, ".rodata.x"),
2361 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2362 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2363 (Place::Zero, ".bss.x"),
2364 (Place::Thread { zero: false }, ".tdata.x"),
2365 (Place::Thread { zero: true }, ".tbss.x"),
2366 ] {
2367 let data = Data {
2368 apart: Vec::new(),
2369 exports: Vec::new(),
2370 weak: Vec::new(),
2371 objects: vec![variable("x", place.clone())],
2372 };
2373 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2374 .expect("object");
2375 let file = object::File::parse(&bytes[..]).expect("a readable object");
2376 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2377 let section = file.section_by_name(wanted).expect("the section it named");
2378 assert_eq!(section.size(), 4, "{place:?}");
2379 let carried = section.data().expect("the bytes").len();
2382 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2383 }
2384 }
2385
2386 #[test]
2390 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2391 let sections =
2392 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2393 let named = Place::Named(".init_array".to_owned(), Holds::Written);
2394 let objects = vec![variable("m", Place::Merged), variable("n", named)];
2395 let bytes = write(
2396 &Text::default(),
2397 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2398 &[],
2399 &target(),
2400 sections,
2401 &Info::default(),
2402 )
2403 .expect("object");
2404 let file = object::File::parse(&bytes[..]).expect("a readable object");
2405 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2406 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2407 assert_eq!(lives_in(&file, "n"), ".init_array");
2408 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2409 }
2410
2411 #[test]
2415 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2416 let sections =
2417 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2418 let pointer = Object {
2419 bytes: vec![0; 8],
2420 size: 8,
2421 align: 8,
2422 relocs: vec![Reloc {
2423 at: 0,
2424 symbol: "y".to_owned(),
2425 kind: Reference::Address { bytes: 8 },
2426 addend: 0,
2427 after: 0,
2428 }],
2429 ..variable("p", Place::Written)
2430 };
2431 let objects = vec![variable("first", Place::Written), pointer];
2432 let bytes = write(
2433 &Text::default(),
2434 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2435 &[],
2436 &target(),
2437 sections,
2438 &Info::default(),
2439 )
2440 .expect("object");
2441 let file = object::File::parse(&bytes[..]).expect("a readable object");
2442 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2443 let (offset, reloc) = section.relocations().next().expect("one relocation");
2444 assert_eq!(offset, 0);
2447 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2448 }
2449
2450 #[test]
2458 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2459 let place = Place::RelocReadOnly { local: true };
2460 let data = Data {
2461 apart: Vec::new(),
2462 exports: Vec::new(),
2463 weak: Vec::new(),
2464 objects: vec![variable("first", place.clone()), variable("second", place)],
2465 };
2466 let bytes =
2467 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2468 .expect("an object");
2469 let file = object::File::parse(&bytes[..]).expect("a readable object");
2470 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2471 assert_eq!(named, 1, "one section holding both, not one each");
2472 }
2473
2474 #[test]
2475 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2476 let mut data = Data {
2477 apart: Vec::new(),
2478 exports: Vec::new(),
2479 weak: Vec::new(),
2480 objects: vec![variable("first", Place::Written)],
2481 };
2482 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2483 let bytes =
2484 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2485 .expect("an object");
2486 let file = object::File::parse(&bytes[..]).expect("a readable object");
2487 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2488 assert_eq!(second.kind(), SymbolKind::Data);
2489 assert_eq!(second.size(), 4);
2490 assert_eq!(second.address(), 16);
2494 }
2495
2496 #[test]
2497 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2498 for (binding, global, weak) in [
2499 (Binding::Global, true, false),
2500 (Binding::Local, false, false),
2501 (Binding::Weak, true, true),
2502 ] {
2503 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2504 let file = object::File::parse(&bytes[..]).expect("a readable object");
2505 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2506 assert_eq!(x.is_global(), global, "{binding:?}");
2507 assert_eq!(x.is_weak(), weak, "{binding:?}");
2508 }
2509 }
2510
2511 #[test]
2512 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2513 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2514 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2515 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2516 assert!(x.is_common(), "the linker merges every definition of this name into one");
2517 assert_eq!(x.size(), 4);
2518 assert_eq!(x.address(), 0);
2522 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2523 }
2524
2525 #[test]
2526 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2527 let object = Object {
2528 bytes: vec![0; 8],
2529 size: 8,
2530 align: 8,
2531 relocs: vec![Reloc {
2532 at: 0,
2533 symbol: "y".to_owned(),
2534 kind: Reference::Address { bytes: 8 },
2535 addend: 16,
2536 after: 0,
2537 }],
2538 ..variable("p", Place::Written)
2539 };
2540 let bytes = holding(object);
2541 let file = object::File::parse(&bytes[..]).expect("a readable object");
2542 let section = file.section_by_name(".data").expect("a data section");
2543 let (offset, reloc) = section.relocations().next().expect("one relocation");
2544 assert_eq!(offset, 0);
2545 assert_eq!(reloc.addend(), 16);
2546 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2547 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2548 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2549 }
2550
2551 #[test]
2558 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2559 let mut text = Text::default();
2560 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2561 text.bytes.resize(8, 0x90);
2562 text.relocs.push(Reloc {
2563 at: 1,
2564 symbol: "hook".to_owned(),
2565 kind: Reference::Call,
2566 addend: -4,
2567 after: 0,
2568 });
2569 let data = Data {
2570 apart: Vec::new(),
2571 exports: Vec::new(),
2572 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2573 objects: vec![],
2574 };
2575 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2576 .expect("an object");
2577 let file = object::File::parse(&bytes[..]).expect("a readable object");
2578
2579 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2580 assert!(hook.is_undefined(), "nothing here defines it");
2581 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2582
2583 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2587 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2588 }
2589
2590 #[test]
2605 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2606 let mut text = Text::default();
2607 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2608 text.bytes.resize(16, 0x90);
2609 text.relocs.push(Reloc {
2610 at: 3,
2611 symbol: "flags".to_owned(),
2612 kind: Reference::Thread,
2613 addend: -4,
2614 after: 0,
2615 });
2616 text.relocs.push(Reloc {
2619 at: 10,
2620 symbol: "shared".to_owned(),
2621 kind: Reference::Got,
2622 addend: -4,
2623 after: 0,
2624 });
2625 let data =
2626 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2627 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2628 .expect("an object");
2629 let file = object::File::parse(&bytes[..]).expect("a readable object");
2630
2631 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2632 assert!(flags.is_undefined(), "nothing here defines it");
2633 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2634
2635 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2636 assert!(shared.is_undefined(), "nothing here defines this one either");
2637 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2638 }
2639
2640 #[test]
2642 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2643 let mut data = Data {
2644 apart: Vec::new(),
2645 exports: Vec::new(),
2646 weak: Vec::new(),
2647 objects: vec![variable("first", Place::Written)],
2648 };
2649 data.objects.push(Object {
2650 bytes: vec![0; 16],
2651 size: 16,
2652 align: 8,
2653 relocs: vec![Reloc {
2654 at: 8,
2655 symbol: "y".to_owned(),
2656 kind: Reference::Address { bytes: 8 },
2657 addend: 0,
2658 after: 0,
2659 }],
2660 ..variable("second", Place::Written)
2661 });
2662 let bytes =
2663 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2664 .expect("an object");
2665 let file = object::File::parse(&bytes[..]).expect("a readable object");
2666 let section = file.section_by_name(".data").expect("a data section");
2667 let (offset, _) = section.relocations().next().expect("one relocation");
2668 assert_eq!(offset, 16);
2671 }
2672
2673 #[test]
2674 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2675 let data = Data {
2676 apart: Vec::new(),
2677 exports: Vec::new(),
2678 weak: Vec::new(),
2679 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2680 };
2681 let aliases = [Alias {
2682 name: "b".to_owned(),
2683 target: "a".to_owned(),
2684 binding: Binding::Global,
2685 visibility: Visibility::Default,
2686 ifunc: false,
2687 }];
2688 let bytes = write(
2689 &Text::default(),
2690 &data,
2691 &aliases,
2692 &target(),
2693 Output::default(),
2694 &Info::default(),
2695 )
2696 .expect("an object");
2697 let file = object::File::parse(&bytes[..]).expect("a readable object");
2698 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2699 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2700 assert_eq!(b.address(), a.address(), "the same place");
2701 assert_eq!(b.size(), a.size());
2702 assert_eq!(b.section_index(), a.section_index());
2703 assert!(a.is_local(), "the target was written `static`");
2706 assert!(b.is_global(), "and the name given to it was not");
2707 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2709 }
2710
2711 #[test]
2712 fn second_names_are_written_in_the_order_of_what_they_name() {
2713 let data = Data {
2714 apart: Vec::new(),
2715 exports: Vec::new(),
2716 weak: Vec::new(),
2717 objects: vec![variable("a", Place::Written), variable("b", Place::Written)],
2718 };
2719 let alias = |name: &str, target: &str| Alias {
2720 name: name.to_owned(),
2721 target: target.to_owned(),
2722 binding: Binding::Global,
2723 visibility: Visibility::Default,
2724 ifunc: false,
2725 };
2726 let aliases = [alias("for_b", "b"), alias("for_a", "a")];
2727 let bytes = write(
2728 &Text::default(),
2729 &data,
2730 &aliases,
2731 &target(),
2732 Output::default(),
2733 &Info::default(),
2734 )
2735 .expect("an object");
2736 let file = object::File::parse(&bytes[..]).expect("a readable object");
2737 let names: Vec<_> = file
2738 .symbols()
2739 .filter_map(|s| s.name().ok())
2740 .filter(|name| name.starts_with("for_"))
2741 .collect();
2742 assert_eq!(names, ["for_a", "for_b"]);
2743 }
2744
2745 #[test]
2746 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2747 let text = calling("puts");
2748 let aliases = [Alias {
2749 name: "g".to_owned(),
2750 target: "f".to_owned(),
2751 binding: Binding::Weak,
2752 visibility: Visibility::Default,
2753 ifunc: false,
2754 }];
2755 let bytes = write(
2756 &text,
2757 &Data::default(),
2758 &aliases,
2759 &target(),
2760 Output::default(),
2761 &Info::default(),
2762 )
2763 .expect("an object");
2764 let file = object::File::parse(&bytes[..]).expect("a readable object");
2765 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2766 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2767 assert_eq!(g.address(), f.address());
2768 assert_eq!(g.size(), f.size());
2769 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2770 assert!(g.is_weak(), "so that a program may define the name itself instead");
2771 }
2772
2773 #[test]
2777 fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2778 let text = calling("puts");
2779 for (binding, bind) in [
2780 (Binding::Global, elf::STB_GLOBAL),
2781 (Binding::Weak, elf::STB_WEAK),
2782 (Binding::Local, elf::STB_LOCAL),
2783 ] {
2784 let aliases = [Alias {
2785 name: "g".to_owned(),
2786 target: "f".to_owned(),
2787 binding,
2788 visibility: Visibility::Default,
2789 ifunc: true,
2790 }];
2791 let bytes = write(
2792 &text,
2793 &Data::default(),
2794 &aliases,
2795 &target(),
2796 Output::default(),
2797 &Info::default(),
2798 )
2799 .expect("an object");
2800 let file = object::File::parse(&bytes[..]).expect("a readable object");
2801 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2802 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2803 assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2804 let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2805 panic!("an ELF symbol");
2806 };
2807 assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2808 let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2809 let os_abi = elf.elf_header().e_ident.os_abi;
2810 assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2811 }
2812 }
2813
2814 #[test]
2817 fn an_ifunc_is_refused_on_a_format_without_the_type() {
2818 let aliases = [Alias {
2819 name: "g".to_owned(),
2820 target: "f".to_owned(),
2821 binding: Binding::Global,
2822 visibility: Visibility::Default,
2823 ifunc: true,
2824 }];
2825 let error = write(
2826 &calling("puts"),
2827 &Data::default(),
2828 &aliases,
2829 &windows(),
2830 Output::default(),
2831 &Info::default(),
2832 )
2833 .expect_err("no ifunc on COFF");
2834 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2835 }
2836
2837 #[test]
2840 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2841 let aliases = [Alias {
2842 name: "b".to_owned(),
2843 target: "a".to_owned(),
2844 binding: Binding::Global,
2845 visibility: Visibility::Default,
2846 ifunc: false,
2847 }];
2848 let error = write(
2849 &Text::default(),
2850 &Data::default(),
2851 &aliases,
2852 &target(),
2853 Output::default(),
2854 &Info::default(),
2855 )
2856 .expect_err("nothing to point at");
2857 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2858 }
2859
2860 #[test]
2861 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2862 let text = calling("puts");
2863 for triple in [
2864 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2865 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2866 ] {
2867 let error = write(
2868 &text,
2869 &Data::default(),
2870 &[],
2871 &TargetInfo::new(triple),
2872 Output::default(),
2873 &Info::default(),
2874 )
2875 .expect_err("no writer");
2876 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2877 }
2878 }
2879
2880 #[test]
2886 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2887 let mut text = calling("puts");
2888 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2889 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2890 text.bytes.resize(33, 0x90);
2891 let data = Data {
2892 apart: Vec::new(),
2893 exports: Vec::new(),
2894 weak: Vec::new(),
2895 objects: vec![variable("seen", Place::Written), {
2896 let mut quiet = variable("quiet", Place::Zero);
2897 quiet.binding = Binding::Local;
2898 quiet
2899 }],
2900 };
2901 let aliases = [Alias {
2902 name: "second".to_owned(),
2903 target: "f".to_owned(),
2904 binding: Binding::Global,
2905 visibility: Visibility::Default,
2906 ifunc: false,
2907 }];
2908
2909 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2910 assert_eq!(names, ["f", "shared", "seen", "second"]);
2911
2912 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2913 .expect("an object");
2914 let file = object::File::parse(&bytes[..]).expect("a readable object");
2915 let found: Vec<String> = file
2916 .symbols()
2917 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2918 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2919 .collect();
2920 let mut sorted = names.clone();
2921 sorted.sort();
2922 let mut theirs = found;
2923 theirs.sort();
2924 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2925 }
2926
2927 fn windows() -> TargetInfo {
2929 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2930 }
2931
2932 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2934 let file = object::File::parse(bytes).expect("a readable object");
2935 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2936 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2937 }
2938
2939 #[test]
2940 fn a_windows_target_is_written_rather_than_refused() {
2941 let text = calling("puts");
2942 let bytes =
2943 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2944 .expect("an object");
2945 let file = object::File::parse(&bytes[..]).expect("a readable object");
2946 assert_eq!(file.format(), BinaryFormat::Coff);
2947 let section = file.section_by_name(".text").expect("a text section");
2948 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2949 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2950 assert!(names.contains(&"f"), "{names:?}");
2951 assert!(names.contains(&"puts"), "{names:?}");
2952 }
2953
2954 #[test]
2962 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2963 for (after, typ) in [
2964 (0, pe::IMAGE_REL_AMD64_REL32),
2965 (1, pe::IMAGE_REL_AMD64_REL32_1),
2966 (4, pe::IMAGE_REL_AMD64_REL32_4),
2967 (5, pe::IMAGE_REL_AMD64_REL32_5),
2968 ] {
2969 let mut text = calling("puts");
2970 text.relocs[0].addend = -4 - i64::from(after);
2973 text.relocs[0].after = after;
2974 text.bytes.resize(6 + after as usize, 0x90);
2975 text.funcs[0].len = text.bytes.len();
2976 let bytes = write(
2977 &text,
2978 &Data::default(),
2979 &[],
2980 &windows(),
2981 Output::default(),
2982 &Info::default(),
2983 )
2984 .expect("an object");
2985 let file = object::File::parse(&bytes[..]).expect("a readable object");
2986 let section = file.section_by_name(".text").expect("a text section");
2987 let (_, reloc) = section.relocations().next().expect("the relocation");
2988 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2989 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2992 }
2993 }
2994
2995 #[test]
2998 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2999 let mut text = calling("puts");
3000 text.relocs[0].addend = 12;
3001 let bytes =
3002 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3003 .expect("an object");
3004 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
3005 }
3006
3007 #[test]
3008 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
3009 let object = Object {
3010 bytes: vec![0; 8],
3011 size: 8,
3012 align: 8,
3013 relocs: vec![Reloc {
3014 at: 0,
3015 symbol: "y".to_owned(),
3016 kind: Reference::Address { bytes: 8 },
3017 addend: 0,
3018 after: 0,
3019 }],
3020 ..variable("p", Place::Written)
3021 };
3022 let data = Data {
3023 apart: Vec::new(),
3024 exports: Vec::new(),
3025 weak: Vec::new(),
3026 objects: vec![object],
3027 };
3028 let bytes =
3029 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3030 .expect("an object");
3031 let file = object::File::parse(&bytes[..]).expect("a readable object");
3032 let section = file.section_by_name(".data").expect("a data section");
3033 let (_, reloc) = section.relocations().next().expect("the relocation");
3034 let typ = pe::IMAGE_REL_AMD64_ADDR64;
3035 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
3036 }
3037
3038 #[test]
3042 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
3043 let pointer = Object {
3044 bytes: vec![0; 8],
3045 size: 8,
3046 align: 8,
3047 relocs: vec![Reloc {
3048 at: 0,
3049 symbol: "environ".to_owned(),
3050 kind: Reference::Address { bytes: 8 },
3051 addend: 0,
3052 after: 0,
3053 }],
3054 ..variable(".refptr.environ", Place::Pointer)
3055 };
3056 let data = Data { objects: vec![pointer], ..Data::default() };
3057 let bytes =
3058 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3059 .expect("an object");
3060 let file = object::File::parse(&bytes[..]).expect("a readable object");
3061 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
3062 let SectionFlags::Coff { characteristics } = section.flags() else {
3063 panic!("a COFF section has COFF flags");
3064 };
3065 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
3066 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
3068 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
3069 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
3070 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
3071 assert_eq!(comdat.kind(), ComdatKind::Any);
3072 assert_eq!(comdat.name(), Ok(".refptr.environ"));
3073 let (_, reloc) = section.relocations().next().expect("the address it holds");
3074 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
3075 }
3076
3077 #[test]
3081 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
3082 let exports = vec![
3083 Export { name: "offered".to_owned(), kind: Offer::Function },
3084 Export { name: "count".to_owned(), kind: Offer::Variable },
3085 Export { name: "kept".to_owned(), kind: Offer::Hidden },
3086 ];
3087 let data = Data { exports, ..Data::default() };
3088 let bytes =
3089 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
3090 .expect("an object");
3091 let file = object::File::parse(&bytes[..]).expect("a readable object");
3092 let section = file.section_by_name(".drectve").expect("the options section");
3093 assert_eq!(
3094 section.data().expect("the options"),
3095 b" -export:offered -export:count,data -exclude-symbols:kept"
3096 );
3097 let SectionFlags::Coff { characteristics } = section.flags() else {
3098 panic!("a COFF section has COFF flags");
3099 };
3100 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
3101 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
3102
3103 let none = write(
3104 &Text::default(),
3105 &Data::default(),
3106 &[],
3107 &windows(),
3108 Output::default(),
3109 &Info::default(),
3110 )
3111 .expect("an object");
3112 let file = object::File::parse(&none[..]).expect("a readable object");
3113 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3114 }
3115
3116 #[test]
3119 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3120 for local in [false, true] {
3121 let data = Data {
3122 apart: Vec::new(),
3123 exports: Vec::new(),
3124 weak: Vec::new(),
3125 objects: vec![variable("p", Place::RelocReadOnly { local })],
3126 };
3127 let bytes = write(
3128 &Text::default(),
3129 &data,
3130 &[],
3131 &windows(),
3132 Output::default(),
3133 &Info::default(),
3134 )
3135 .expect("an object");
3136 let file = object::File::parse(&bytes[..]).expect("a readable object");
3137 assert!(file.section_by_name(".rdata").is_some(), "{local}");
3138 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3139 }
3140 }
3141
3142 #[test]
3145 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3146 let text = calling("puts");
3147 let output = Output { property: Property { features: 3 }, ..Output::default() };
3148 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3149 .expect("an object");
3150 let file = object::File::parse(&bytes[..]).expect("a readable object");
3151 assert!(file.section_by_name(".note.GNU-stack").is_none());
3152 assert!(file.section_by_name(".note.gnu.property").is_none());
3153 }
3154
3155 #[test]
3160 fn what_this_format_cannot_say_is_refused_by_name() {
3161 let ordinary = Text::default();
3162 let empty = Data::default();
3163
3164 let mut thread = Data::default();
3165 thread.objects.push(variable("t", Place::Thread { zero: true }));
3166
3167 let mut gathered = Data::default();
3168 gathered
3169 .objects
3170 .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3171
3172 let mut table = calling("puts");
3173 table.relocs[0].kind = Reference::Got;
3174
3175 let mut room = calling("puts");
3176 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3177
3178 let cases: [(&str, &Text, &Data); 4] = [
3179 ("thread-local", &ordinary, &thread),
3180 ("startup", &ordinary, &gathered),
3181 ("table", &table, &empty),
3182 ("patcher", &room, &empty),
3183 ];
3184 for (what, text, data) in cases {
3185 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3186 .expect_err("something this format cannot write");
3187 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3188 }
3189 }
3190
3191 #[test]
3194 fn a_thread_local_variable_goes_in_the_tls_section() {
3195 let mut thread = Data::default();
3196 thread.objects.push(variable("t", Place::Thread { zero: false }));
3197 let bytes =
3198 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3199 .expect("an object");
3200 let file = object::File::parse(&bytes[..]).expect("a readable object");
3201 assert!(file.section_by_name(".tls$").is_some());
3202 }
3203
3204 #[test]
3208 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3209 let mut text = calling("puts");
3210 text.funcs[0].visibility = Visibility::Hidden;
3211 let bytes =
3212 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3213 .expect("an object");
3214 let file = object::File::parse(&bytes[..]).expect("a readable object");
3215 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3216 assert!(symbol.is_global(), "a name others may use either way");
3217 }
3218
3219 #[test]
3220 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3221 let text = calling("puts");
3222 let data = Data {
3223 apart: Vec::new(),
3224 exports: Vec::new(),
3225 weak: Vec::new(),
3226 objects: vec![variable("shared", Place::Written)],
3227 };
3228 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3229 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3230 }
3231
3232 #[test]
3236 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3237 let text = calling("puts");
3238 for triple in [
3239 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3240 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3241 ] {
3242 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3243 .expect_err("no writer");
3244 assert!(matches!(error, Error::Format { .. }), "{error:?}");
3245 }
3246 }
3247
3248 fn i386() -> TargetInfo {
3250 TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3251 }
3252
3253 #[test]
3260 fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3261 let mut text = calling("puts");
3262 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3263 text.funcs[0].start = 3;
3264 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3265 text.relocs[0].at = 4;
3266 let data = Data {
3267 objects: vec![Object {
3268 name: "p".to_owned(),
3269 bytes: vec![0; 4],
3270 size: 4,
3271 align: 4,
3272 place: Place::Written,
3273 binding: Binding::Global,
3274 visibility: Visibility::Default,
3275 relocs: vec![Reloc {
3276 at: 0,
3277 symbol: "x".to_owned(),
3278 kind: Reference::Address { bytes: 4 },
3279 addend: 12,
3280 after: 0,
3281 }],
3282 }],
3283 ..Data::default()
3284 };
3285 let property = Property { features: Property::IBT | Property::SHSTK };
3286 let output = Output { property, ..Output::default() };
3287 let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3288 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3289 assert_eq!(file.architecture(), Architecture::I386);
3290 assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3291 assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3292
3293 let code = file.section_by_name(".text").expect("a text section");
3295 let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3296 panic!("one relocation in the text")
3297 };
3298 assert_eq!(*at, 4);
3299 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3300 assert!(reloc.has_implicit_addend());
3301 assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3302
3303 let variable = file.section_by_name(".data").expect("a data section");
3305 let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3306 panic!("one relocation in the data")
3307 };
3308 assert_eq!(*at, 0);
3309 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3310 assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3311
3312 let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3314 assert_eq!(record.size(), 4);
3315 assert_eq!(record.align(), 4);
3316 let index = code.index().0;
3317 assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3318 let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3319 panic!("one address in the record")
3320 };
3321 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3322
3323 let note = file.section_by_name(".note.gnu.property").expect("the note");
3325 assert_eq!(note.align(), 4);
3326 let want: Vec<u8> = [
3327 4u32,
3328 12,
3329 5,
3330 u32::from_le_bytes(*b"GNU\0"),
3331 Property::X86_FEATURES,
3332 4,
3333 Property::IBT | Property::SHSTK,
3334 ]
3335 .iter()
3336 .flat_map(|word| word.to_le_bytes())
3337 .collect();
3338 assert_eq!(note.data().expect("the bytes"), &want[..]);
3339 }
3340
3341 #[test]
3344 fn an_i386_address_less_three_gigabytes_wraps_in_its_four_bytes() {
3345 let object = |bytes: u8| Object {
3346 name: "cr3".to_owned(),
3347 bytes: vec![0; usize::from(bytes)],
3348 size: u64::from(bytes),
3349 align: u64::from(bytes),
3350 place: Place::Written,
3351 binding: Binding::Global,
3352 visibility: Visibility::Default,
3353 relocs: vec![Reloc {
3354 at: 0,
3355 symbol: "swapper_pg_dir".to_owned(),
3356 kind: Reference::Address { bytes },
3357 addend: -0xC000_0000,
3358 after: 0,
3359 }],
3360 };
3361 let data = Data { objects: vec![object(4)], ..Data::default() };
3362 let bytes =
3363 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default())
3364 .expect("an object");
3365 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3366 let variable = file.section_by_name(".data").expect("a data section");
3367 assert_eq!(variable.data().expect("the bytes"), &0x4000_0000u32.to_le_bytes());
3368
3369 let data = Data { objects: vec![object(2)], ..Data::default() };
3370 let refused =
3371 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default());
3372 assert!(refused.is_err(), "two bytes cannot hold a name less three gigabytes");
3373 }
3374
3375 #[test]
3378 fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3379 let info = Info {
3380 chunks: vec![Chunk {
3381 name: ".debug_info".to_owned(),
3382 bytes: vec![0; 64],
3383 relocs: vec![Reloc {
3384 at: 8,
3385 symbol: "f".to_owned(),
3386 kind: Reference::Address { bytes: 4 },
3387 addend: 7,
3388 after: 0,
3389 }],
3390 }],
3391 compress: Compress::Zlib,
3392 };
3393 let bytes =
3394 write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3395 .expect("an object");
3396 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3397 let section = file.section_by_name(".debug_info").expect("the debug section");
3398 let packed =
3399 SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3400 assert_ne!(section.flags(), packed);
3401 let data = section.data().expect("the bytes");
3402 assert_eq!(data.len(), 64);
3403 assert_eq!(&data[8..12], &7u32.to_le_bytes());
3404 }
3405
3406 fn i386_windows() -> TargetInfo {
3408 TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3409 }
3410
3411 #[test]
3420 fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3421 let mut text = calling("puts");
3422 text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3423 text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3424 text.relocs.push(Reloc {
3425 at: 7,
3426 symbol: "__imp_GetTickCount".to_owned(),
3427 kind: Reference::Call,
3428 addend: -4,
3429 after: 0,
3430 });
3431 let data = Data {
3432 objects: vec![Object {
3433 name: "p".to_owned(),
3434 bytes: vec![0; 12],
3435 size: 12,
3436 align: 4,
3437 place: Place::Written,
3438 binding: Binding::Global,
3439 visibility: Visibility::Default,
3440 relocs: vec![
3441 Reloc {
3442 at: 0,
3443 symbol: "x".to_owned(),
3444 kind: Reference::Address { bytes: 4 },
3445 addend: 12,
3446 after: 0,
3447 },
3448 Reloc {
3449 at: 4,
3450 symbol: "f".to_owned(),
3451 kind: Reference::Image,
3452 addend: 0,
3453 after: 0,
3454 },
3455 Reloc {
3456 at: 8,
3457 symbol: "x".to_owned(),
3458 kind: Reference::Away,
3459 addend: 0,
3460 after: 0,
3461 },
3462 ],
3463 }],
3464 ..Data::default()
3465 };
3466 let aliases = [Alias {
3467 name: "g".to_owned(),
3468 target: "f".to_owned(),
3469 binding: Binding::Global,
3470 visibility: Visibility::Default,
3471 ifunc: false,
3472 }];
3473 let target = i386_windows();
3474 let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3475 .expect("an object");
3476 let file = object::File::parse(&bytes[..]).expect("a readable object");
3477 assert_eq!(file.format(), BinaryFormat::Coff);
3478 assert_eq!(file.architecture(), Architecture::I386);
3479 assert!(!file.is_64());
3480
3481 let named = |name: &str| file.symbol_by_name(name).is_some();
3482 for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3483 assert!(named(name), "{name}");
3484 }
3485 for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3486 assert!(!named(name), "{name}");
3487 }
3488
3489 let code = file.section_by_name(".text").expect("a text section");
3490 let relocs: Vec<_> = code.relocations().collect();
3491 assert_eq!(relocs.len(), 2);
3492 for (at, reloc) in &relocs {
3493 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3494 let at = *at as usize;
3495 assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3496 }
3497
3498 let variable = file.section_by_name(".data").expect("a data section");
3499 let types: Vec<_> = variable
3500 .relocations()
3501 .map(|(at, reloc)| match reloc.flags() {
3502 RelocationFlags::Coff { typ } => (at, typ),
3503 flags => panic!("{flags:?}"),
3504 })
3505 .collect();
3506 assert_eq!(
3507 types,
3508 [
3509 (0, pe::IMAGE_REL_I386_DIR32),
3510 (4, pe::IMAGE_REL_I386_DIR32NB),
3511 (8, pe::IMAGE_REL_I386_REL32)
3512 ]
3513 );
3514 let image = variable.data().expect("the bytes");
3517 assert_eq!(&image[0..4], &12u32.to_le_bytes());
3518 assert_eq!(&image[8..12], &4u32.to_le_bytes());
3519
3520 let listed = defines(&text, &data, &aliases, &target).expect("a list");
3522 assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3523 }
3524
3525 #[test]
3528 fn an_i386_windows_object_has_no_unwind_table() {
3529 let mut text = calling("puts");
3530 text.unwind.bytes = vec![0; 12];
3531 let bytes = write(
3532 &text,
3533 &Data::default(),
3534 &[],
3535 &i386_windows(),
3536 Output::default(),
3537 &Info::default(),
3538 )
3539 .expect("an object");
3540 let file = object::File::parse(&bytes[..]).expect("a readable object");
3541 assert!(file.section_by_name(".pdata").is_none());
3542 assert!(file.section_by_name(".xdata").is_none());
3543 assert!(file.section_by_name(".eh_frame").is_none());
3544 }
3545
3546 #[test]
3549 fn an_i386_windows_object_says_it_is_safe_for_safeseh() {
3550 let write_for = |target: &TargetInfo| {
3551 write(
3552 &calling("puts"),
3553 &Data::default(),
3554 &[],
3555 target,
3556 Output::default(),
3557 &Info::default(),
3558 )
3559 .expect("an object")
3560 };
3561 let bytes = write_for(&i386_windows());
3562 let file = object::File::parse(&bytes[..]).expect("a readable object");
3563 let feat = file.symbol_by_name("@feat.00").expect("the feature symbol");
3564 let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
3566 let raw = coff.symbol_by_name("@feat.00").expect("the feature symbol");
3567 assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
3568 assert_eq!(feat.section(), object::SymbolSection::Absolute);
3569 assert!(feat.is_local());
3570 let bytes = write_for(&windows());
3571 let file = object::File::parse(&bytes[..]).expect("a readable object");
3572 assert!(file.symbol_by_name("@feat.00").is_none());
3573 }
3574
3575 #[test]
3578 fn i386_windows_has_no_eight_byte_or_table_relocations() {
3579 for kind in [
3580 Reference::Address { bytes: 8 },
3581 Reference::AwayWide,
3582 Reference::Got,
3583 Reference::GotOffset,
3584 Reference::Slot,
3585 Reference::Thread,
3586 ] {
3587 assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3588 }
3589 assert_eq!(
3590 Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3591 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3592 );
3593 assert_eq!(
3594 Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3595 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3596 "an address an instruction holds"
3597 );
3598 }
3599}