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 for label in &text.labels {
486 let after = text.funcs.partition_point(|func| func.start <= label.at);
487 let Some(index) = after.checked_sub(1) else {
488 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
489 return Err(Error::Refused { why });
490 };
491 let func = &text.funcs[index];
492 let (section, at) = if sections.functions {
493 let base = func.start - func.patch.map_or(0, |patch| patch.before);
496 (split[index].0, (label.at - base) as u64)
497 } else {
498 (whole, label.at as u64)
499 };
500 let id = obj.add_symbol(Symbol {
501 name: label.name.clone().into_bytes(),
502 value: at,
503 size: 0,
506 kind: SymbolKind::Label,
507 scope: SymbolScope::Compilation,
511 weak: false,
512 section: SymbolSection::Section(section),
513 flags: SymbolFlags::None,
514 });
515 symbols.insert(label.name.clone(), id);
516 }
517
518 if !text.mcount.is_empty() {
524 let name = crate::section::MCOUNT_LOC.as_bytes().to_vec();
525 let id = obj.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
526 let flags =
527 flavour.reloc(machine, Reference::Address { bytes: pointer }, 0).ok_or_else(|| {
528 Error::Refused { why: "no relocation holds an address here".to_owned() }
529 })?;
530 for &call in &text.mcount {
531 let after = text.funcs.partition_point(|func| func.start <= call);
532 let Some(index) = after.checked_sub(1) else {
533 let why = format!("a profiler call at {call} is in front of every function");
534 return Err(Error::Refused { why });
535 };
536 let func = &text.funcs[index];
537 let (section, at) = if sections.functions {
538 let base = func.start - func.patch.map_or(0, |patch| patch.before);
539 (split[index].0, call - base)
540 } else {
541 (whole, call)
542 };
543 let offset =
544 obj.append_section_data(id, &vec![0; usize::from(pointer)], u64::from(pointer));
545 let symbol = obj.section_symbol(section);
546 relocate(&mut obj, id, Relocation { offset, symbol, addend: at as i64, flags })?;
547 }
548 }
549
550 let mut placed = Vec::with_capacity(data.objects.len());
555 let mut named = Map::default();
559 for object in &data.objects {
560 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
561 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
565 obj.section_symbol(section);
566 }
567 let id = obj.add_symbol(Symbol {
568 name: spell(&object.name),
569 value: if object.place == Place::Merged { object.align } else { offset },
572 size: object.size,
573 kind: match object.place {
578 Place::Thread { .. } => SymbolKind::Tls,
579 _ => SymbolKind::Data,
580 },
581 scope: scope_of(object.binding),
582 weak: object.binding == Binding::Weak,
583 section,
584 flags: SymbolFlags::None,
585 });
586 flavour.see(&mut obj, id, object.binding, object.visibility);
587 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
591 obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
592 }
593 symbols.insert(object.name.clone(), id);
594 placed.push((section.id(), offset));
595 }
596
597 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
600
601 for apart in &data.apart {
605 let (Some(section), offset) = placed[apart.object] else { continue };
606 let value = distance(&obj, &symbols, apart)?;
607 let bytes = usize::from(apart.bytes);
608 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
609 let at = at + apart.at;
610 let image = obj.section_mut(section).data_mut();
611 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
612 }
613
614 for alias in aliases {
620 let Some(&id) = symbols.get(&alias.target) else {
621 let why =
622 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
623 return Err(Error::Refused { why });
624 };
625 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
626 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
627 let id = obj.add_symbol(Symbol {
628 name: spell(&alias.name),
629 value,
630 size,
631 kind,
632 scope: scope_of(alias.binding),
633 weak: alias.binding == Binding::Weak,
634 section,
635 flags: SymbolFlags::None,
636 });
637 flavour.see(&mut obj, id, alias.binding, alias.visibility);
638 if alias.ifunc {
639 if flavour != Flavour::Elf {
640 let why = format!("'{}' is an indirect function, which only ELF has", alias.name);
641 return Err(Error::Refused { why });
642 }
643 elf::indirect(&mut obj, id);
644 }
645 symbols.insert(alias.name.clone(), id);
646 }
647
648 let weak: Set<&str> = data.weak.iter().map(String::as_str).collect();
655 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
656 let thread: Set<&str> = relocs()
661 .filter(|reloc| reloc.kind == Reference::Thread)
662 .map(|reloc| reloc.symbol.as_str())
663 .collect();
664 let wanted: Vec<&String> =
665 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
666 for name in wanted {
667 if symbols.contains_key(name) || tables.contains_key(name) {
668 continue;
669 }
670 let id = obj.add_symbol(Symbol {
671 name: spell(name),
672 value: 0,
673 size: 0,
674 kind: if thread.contains(name.as_str()) {
684 SymbolKind::Tls
685 } else {
686 SymbolKind::Unknown
687 },
688 scope: SymbolScope::Dynamic,
689 weak: weak.contains(name.as_str()),
690 section: SymbolSection::Undefined,
691 flags: SymbolFlags::None,
692 });
693 symbols.insert(name.clone(), id);
694 }
695
696 for reloc in &text.relocs {
697 let (section, at) = if sections.functions {
702 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
703 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
704 let why = format!("a relocation at {} is in front of every function", reloc.at);
705 return Err(Error::Refused { why });
706 };
707 let base = func.start - func.patch.map_or(0, |patch| patch.before);
710 (split[after - 1].0, (reloc.at - base) as u64)
711 } else {
712 (whole, reloc.at as u64)
713 };
714 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
718 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
719 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
720 })?;
721 let symbol = obj.section_symbol(table);
722 let addend = reloc.addend + offset as i64;
723 relocate(&mut obj, section, Relocation { offset: at, symbol, addend, flags })?;
724 continue;
725 }
726 add(&mut obj, section, at, reloc, &symbols, flavour)?;
727 }
728
729 let seh_free = flavour == Flavour::Coff && machine == Architecture::I386;
739 if !text.unwind.bytes.is_empty() && !seh_free {
740 let ((name, align), second) = flavour.tables();
741 let align = if machine == Architecture::I386 { 4 } else { align };
744 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
745 obj.append_section_data(frames, &text.unwind.bytes, align);
746 let mut described = Map::default();
751 if !text.unwind.info.is_empty() {
752 let Some((name, align)) = second else {
753 let why = "an unwind table here is one section and it was given two".to_owned();
754 return Err(Error::Refused { why });
755 };
756 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
757 obj.append_section_data(codes, &text.unwind.info, align);
758 for label in &text.unwind.labels {
759 let id = obj.add_symbol(Symbol {
760 name: label.name.clone().into_bytes(),
761 value: label.at as u64,
762 size: 0,
763 kind: SymbolKind::Label,
764 scope: SymbolScope::Compilation,
765 weak: false,
766 section: SymbolSection::Section(codes),
767 flags: SymbolFlags::None,
768 });
769 described.insert(label.name.clone(), id);
770 }
771 }
772 if !text.unwind.except.is_empty() {
777 let except =
778 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
779 obj.append_section_data(except, &text.unwind.except, 4);
780 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
781 }
782 for reloc in &text.unwind.relocs {
783 let found = described.get(&reloc.symbol).or_else(|| {
784 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
788 if ours { symbols.get(&reloc.symbol) } else { None }
789 });
790 let (symbol, addend) = match found {
791 Some(&id) => (id, reloc.addend),
795 None => {
809 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
810 let Some((section, at)) = found.map(|i| split[i]) else {
811 let why = format!(
812 "'{}' has an unwind record and is not a function here",
813 reloc.symbol
814 );
815 return Err(Error::Refused { why });
816 };
817 (obj.section_symbol(section), reloc.addend + at as i64)
821 }
822 };
823 let flags = flavour.reloc(machine, reloc.kind, reloc.after).ok_or_else(|| {
824 Error::Refused { why: format!("no relocation is {:?}", reloc.kind) }
825 })?;
826 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
827 relocate(&mut obj, frames, record)?;
828 }
829 }
830 let mut named = Map::default();
839 for chunk in &info.chunks {
840 let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
843 info.compress
844 } else {
845 Compress::None
846 };
847 let id = crate::zlib::debug_section(&mut obj, chunk, how);
848 named.insert(chunk.name.as_str(), id);
849 }
850 for chunk in &info.chunks {
851 let section = named[chunk.name.as_str()];
852 for reloc in &chunk.relocs {
853 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
854 Some(&id) => (obj.section_symbol(id), reloc.addend),
857 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
862 Some(which) => {
863 let (section, at) = split[which];
864 (obj.section_symbol(section), reloc.addend + at as i64)
865 }
866 None => {
872 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
873 let Some(which) = found else {
874 let why = format!(
875 "'{}' is named by the debug information and is not defined here",
876 reloc.symbol
877 );
878 return Err(Error::Refused { why });
879 };
880 match placed[which] {
881 (Some(section), at) => {
882 (obj.section_symbol(section), reloc.addend + at as i64)
883 }
884 (None, _) => (symbols[&reloc.symbol], reloc.addend),
885 }
886 }
887 },
888 };
889 let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
890 let flags = flavour
891 .reloc(machine, kind, reloc.after)
892 .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
893 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
894 relocate(&mut obj, section, record)?;
895 }
896 }
897 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
898 let Some(section) = section else { continue };
899 for reloc in &object.relocs {
900 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
901 }
902 }
903
904 if !data.exports.is_empty() {
908 let options: String = data.exports.iter().map(Export::option).collect();
909 let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
910 obj.append_section_data(id, options.as_bytes(), 1);
911 }
912
913 flavour.property(&mut obj, property);
917
918 if let Some(ident) = ident.filter(|_| flavour == Flavour::Elf) {
920 let id = obj.add_section(Vec::new(), b".comment".to_vec(), SectionKind::OtherString);
921 let bytes = [&[0][..], ident.as_bytes(), &[0]].concat();
922 obj.append_section_data(id, &bytes, 1);
923 }
924
925 flavour.marker(&mut obj);
928
929 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
930 flavour.finish(&mut bytes, &ordered);
931 Ok(bytes)
932}
933
934fn distance(
941 obj: &Writer<'_>,
942 symbols: &BTreeMap<String, SymbolId>,
943 apart: &Apart,
944) -> Result<i64, Error> {
945 let find = |name: &str| match symbols.get(name) {
946 Some(&id) => Ok(obj.symbol(id)),
947 None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
948 };
949 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
950 if to.section != from.section {
951 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
952 return Err(Error::Refused { why });
953 }
954 let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
955 let bits = u32::from(apart.bytes) * 8;
956 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
957 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
958 return Err(Error::Refused { why });
959 }
960 Ok(value)
961}
962
963fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
976 let why = |why: String| Err(Error::Refused { why });
977 if text.funcs.iter().any(|func| func.patch.is_some()) {
978 return why("a record of where a patcher's room is has no section flags here".to_owned());
979 }
980 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
981 if matches!(
982 reloc.kind,
983 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
984 ) {
985 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
986 }
987 }
988 for object in &data.objects {
989 if matches!(object.place, Place::Thread { zero: true }) {
990 return why(format!(
991 "'{}' is zeroed thread-local storage, which is not here",
992 object.name
993 ));
994 }
995 let Place::Named(name, _) = &object.place else { continue };
996 if Array::of(name).is_some() {
997 return why(format!("'{name}' is not a list the startup code here gathers"));
998 }
999 }
1000 Ok(())
1001}
1002
1003pub fn defines(
1026 text: &Text,
1027 data: &Data,
1028 aliases: &[Alias],
1029 target: &TargetInfo,
1030) -> Result<Vec<String>, Error> {
1031 let Some((flavour, machine)) = written(target) else {
1032 return Err(Error::Format { triple: target.tuple.to_string() });
1033 };
1034 let spell = |name: &String| flavour.spell(machine, name);
1035 let names = text
1036 .funcs
1037 .iter()
1038 .filter(|func| func.binding != Binding::Local)
1039 .map(|func| spell(&func.name))
1040 .chain(
1041 data.objects
1042 .iter()
1043 .filter(|object| object.binding != Binding::Local)
1044 .map(|object| spell(&object.name)),
1045 )
1046 .chain(
1047 aliases
1048 .iter()
1049 .filter(|alias| alias.binding != Binding::Local)
1050 .map(|alias| spell(&alias.name)),
1051 )
1052 .collect();
1053 Ok(names)
1054}
1055
1056fn put(
1063 obj: &mut Writer<'_>,
1064 object: &Object,
1065 named: &mut Map<String, object::write::SectionId>,
1066 sections: Sections,
1067 flavour: Flavour,
1068) -> (SymbolSection, u64) {
1069 if sections.data {
1075 if let Some(name) = object.place.split(&object.name) {
1076 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
1077 let offset = if carries_no_bytes(&object.place) {
1078 obj.append_section_bss(section, object.size, object.align)
1079 } else {
1080 obj.append_section_data(section, &object.bytes, object.align)
1081 };
1082 return (SymbolSection::Section(section), offset);
1083 }
1084 }
1085 let section = match &object.place {
1086 Place::Written => obj.section_id(StandardSection::Data),
1087 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
1088 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
1094 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
1095 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
1096 },
1097 Place::Zero => obj.section_id(StandardSection::UninitializedData),
1098 Place::Strings { align } => {
1101 made(obj, named, &Place::strings(*align), SectionKind::ReadOnlyString)
1102 }
1103 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
1104 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
1105 Place::Merged => return (SymbolSection::Common, 0),
1106 Place::Named(name, _) => {
1113 let section = made(obj, named, name, kind_of(&object.place));
1114 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
1115 obj.section_mut(section).flags = flags;
1116 }
1117 section
1118 }
1119 Place::Pointer => {
1123 let name = format!(".rdata${}", object.name);
1124 made(obj, named, &name, SectionKind::ReadOnlyData)
1125 }
1126 };
1127 let offset = if carries_no_bytes(&object.place) {
1128 obj.append_section_bss(section, object.size, object.align)
1129 } else {
1130 obj.append_section_data(section, &object.bytes, object.align)
1131 };
1132 (SymbolSection::Section(section), offset)
1133}
1134
1135fn tables(
1148 obj: &mut Writer<'_>,
1149 text: &Text,
1150 split: &[(object::write::SectionId, u64)],
1151 named: &mut Map<String, object::write::SectionId>,
1152 sections: Sections,
1153 flavour: Flavour,
1154) -> Result<Map<String, (object::write::SectionId, u64)>, Error> {
1155 let mut placed = Map::default();
1156 if text.tables.is_empty() {
1157 return Ok(placed);
1158 }
1159 if flavour != Flavour::Elf {
1160 let why = "a jump table outside the code is written on ELF only".to_owned();
1161 return Err(Error::Refused { why });
1162 }
1163 let machine = obj.architecture();
1164 let flags = flavour.reloc(machine, Reference::Away, 0).ok_or_else(|| Error::Refused {
1165 why: "no relocation is a distance from where it is written".to_owned(),
1166 })?;
1167 let pointer = if machine == Architecture::I386 { 4u8 } else { 8 };
1169 for table in &text.tables {
1170 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1171 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1172 })?;
1173 let section = if sections.data {
1174 let name = format!(".rodata.{}", func.name);
1175 made(obj, named, &name, SectionKind::ReadOnlyData)
1176 } else {
1177 obj.section_id(StandardSection::ReadOnlyData)
1178 };
1179 let (width, flags) = if table.absolute {
1182 let reference = Reference::Address { bytes: pointer };
1183 let wide = flavour.reloc(machine, reference, 0).ok_or_else(|| Error::Refused {
1184 why: format!("no relocation is an address in {pointer} bytes"),
1185 })?;
1186 (usize::from(pointer), wide)
1187 } else {
1188 (4, flags)
1189 };
1190 let offset =
1191 obj.append_section_data(section, &vec![0; width * table.cells.len()], width as u64);
1192 placed.insert(table.name.clone(), (section, offset));
1193 let (code, at) = split[table.func];
1194 let symbol = obj.section_symbol(code);
1195 for (index, &cell) in table.cells.iter().enumerate() {
1196 let place = (width * index) as u64;
1197 let addend = at as i64 + cell as i64 + if table.absolute { 0 } else { place as i64 };
1198 let record = Relocation { offset: offset + place, symbol, addend, flags };
1199 relocate(obj, section, record)?;
1200 }
1201 }
1202 Ok(placed)
1203}
1204
1205fn carries_no_bytes(place: &Place) -> bool {
1211 matches!(place, Place::Zero | Place::Thread { zero: true } | Place::Named(_, Holds::Zero))
1212}
1213
1214fn made(
1222 obj: &mut Writer<'_>,
1223 named: &mut Map<String, object::write::SectionId>,
1224 name: &str,
1225 kind: SectionKind,
1226) -> object::write::SectionId {
1227 if let Some(section) = named.get(name) {
1228 return *section;
1229 }
1230 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1231 named.insert(name.to_owned(), section);
1232 section
1233}
1234
1235fn kind_of(place: &Place) -> SectionKind {
1247 match place {
1248 Place::ReadOnly | Place::Pointer | Place::Named(_, Holds::ReadOnly) => {
1249 SectionKind::ReadOnlyData
1250 }
1251 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1252 Place::Strings { .. } => SectionKind::ReadOnlyString,
1253 Place::Zero | Place::Named(_, Holds::Zero) => SectionKind::UninitializedData,
1254 Place::Thread { zero: false } => SectionKind::Tls,
1255 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1256 Place::Written | Place::Merged | Place::Named(_, Holds::Written) => SectionKind::Data,
1257 }
1258}
1259
1260fn add(
1267 obj: &mut Writer<'_>,
1268 section: object::write::SectionId,
1269 at: u64,
1270 reloc: &Reloc,
1271 symbols: &BTreeMap<String, SymbolId>,
1272 flavour: Flavour,
1273) -> Result<(), Error> {
1274 let flags = flavour
1275 .reloc(obj.architecture(), reloc.kind, reloc.after)
1276 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1277 relocate(
1278 obj,
1279 section,
1280 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1281 )
1282}
1283
1284pub(crate) fn relocate(
1303 obj: &mut Writer<'_>,
1304 section: object::write::SectionId,
1305 mut relocation: Relocation,
1306) -> Result<(), Error> {
1307 if let (Architecture::I386, RelocationFlags::Elf { r_type }) =
1308 (obj.architecture(), relocation.flags)
1309 {
1310 let Some(width) = elf::width_i386(r_type) else {
1311 let why = format!("relocation type {} has no width this writer knows", r_type.0);
1312 return Err(Error::Refused { why });
1313 };
1314 let addend = relocation.addend;
1315 let bits = 8 * width as u32;
1316 let least = if width == 4 { -(1i64 << bits) } else { -(1i64 << (bits - 1)) };
1322 if addend < least || addend >= 1i64 << bits {
1323 let why =
1324 format!("{addend} added to a name, and there are {width} bytes to keep it in");
1325 return Err(Error::Refused { why });
1326 }
1327 let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1328 let data = obj.section_mut(section).data_mut();
1329 let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1330 let why = format!("a relocation at {at} is past the end of its section");
1331 return Err(Error::Refused { why });
1332 };
1333 place.copy_from_slice(&addend.to_le_bytes()[..width]);
1334 relocation.addend = 0;
1335 }
1336 obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1337}
1338
1339fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1345 let flavour = Flavour::of(target)?;
1346 let machine = flavour.machine(target.tuple.arch())?;
1347 (machine != Architecture::Aarch64).then_some((flavour, machine))
1348}
1349
1350pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1363 match binding {
1364 Binding::Local => SymbolScope::Compilation,
1365 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1366 }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371 use super::*;
1372
1373 use object::read::elf::Sym as _;
1374 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1375 use object::{elf, pe};
1376 use rucc_target::{Arch, Env, Os, Triple};
1377
1378 use crate::elf::PATCHABLE;
1379 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1380
1381 fn target() -> TargetInfo {
1383 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1384 }
1385
1386 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1391 Extent {
1392 name,
1393 start,
1394 len,
1395 align: crate::FUNC_ALIGN,
1396 binding,
1397 visibility: Visibility::Default,
1398 patch: None,
1399 landings: Vec::new(),
1400 }
1401 }
1402
1403 fn calling(name: &str) -> Text {
1405 Text {
1406 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1407 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1408 relocs: vec![Reloc {
1409 at: 1,
1410 symbol: name.to_owned(),
1411 kind: Reference::Call,
1412 addend: -4,
1413 after: 0,
1414 }],
1415 ..Text::default()
1416 }
1417 }
1418
1419 #[test]
1420 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1421 let text = calling("puts");
1422 let bytes =
1423 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1424 .expect("an object");
1425 let file = object::File::parse(&bytes[..]).expect("a readable object");
1426 let section = file.section_by_name(".text").expect("a text section");
1427 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1428 }
1429
1430 #[test]
1431 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1432 let mut text = calling("puts");
1433 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1434 text.bytes.resize(17, 0x90);
1435 let bytes =
1436 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1437 .expect("an object");
1438 let file = object::File::parse(&bytes[..]).expect("a readable object");
1439 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1440 assert_eq!(g.address(), 16);
1441 assert_eq!(g.size(), 1);
1442 assert_eq!(g.kind(), SymbolKind::Text);
1443 assert!(g.is_global(), "nothing said otherwise about this one");
1444 }
1445
1446 #[test]
1447 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1448 let mut text = calling("puts");
1449 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1450 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1451 text.bytes.resize(33, 0x90);
1452 let bytes =
1453 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1454 .expect("an object");
1455 let file = object::File::parse(&bytes[..]).expect("a readable object");
1456 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1457 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1460 assert!(!hidden.is_weak());
1461 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1462 assert!(shared.is_weak(), "a weak function has to be able to lose");
1463 assert!(shared.is_global());
1464 }
1465
1466 #[test]
1483 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1484 let mut text = calling("puts");
1485 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1486 text.funcs[0].start = 3;
1487 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1488 text.relocs[0].at = 4;
1489 let bytes =
1490 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1491 .expect("an object");
1492 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1493 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1494 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1495 assert_eq!(section.align(), 8);
1496 let header = section.elf_section_header();
1497 assert_eq!(
1498 header.sh_flags.get(Endianness::Little),
1499 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1500 );
1501 let index = file.section_by_name(".text").expect("a text section").index().0;
1504 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1505 assert_ne!(index, 0);
1506
1507 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1509 panic!("one address in the record")
1510 };
1511 assert_eq!(*at, 0);
1512 assert_eq!(reloc.addend(), 0);
1513 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1514 }
1515
1516 #[test]
1518 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1519 let text = calling("puts");
1520 let bytes =
1521 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1522 .expect("an object");
1523 let file = object::File::parse(&bytes[..]).expect("a readable object");
1524 assert!(file.section_by_name(PATCHABLE).is_none());
1525 }
1526
1527 #[test]
1533 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1534 let mut text = calling("puts");
1535 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1536 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1537 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1538 text.bytes.resize(17, 0x90);
1539 let output =
1540 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1541 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1542 .expect("an object");
1543 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1544 let links: Vec<usize> = file
1545 .sections()
1546 .filter(|section| section.name() == Ok(PATCHABLE))
1547 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1548 .collect();
1549 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1550 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1551 }
1552
1553 #[test]
1554 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1555 let mut text = calling("puts");
1556 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1557 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1558 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1559 text.bytes.resize(49, 0x90);
1560 let bytes =
1561 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1562 .expect("an object");
1563 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1564 let visibility = |name: &str| {
1565 file.symbols()
1566 .find(|s| s.name() == Ok(name))
1567 .expect("the function")
1568 .elf_symbol()
1569 .st_visibility()
1570 };
1571 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1573 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1574 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1577 }
1578
1579 #[test]
1589 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1590 let mut text = calling("puts");
1591 for (index, (name, seen)) in
1592 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1593 {
1594 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1595 func.visibility = seen;
1596 text.funcs.push(func);
1597 }
1598 text.bytes.resize(49, 0x90);
1599 let mut data = Data::default();
1600 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1601 let mut object = variable(name, Place::Written);
1602 object.visibility = seen;
1603 data.objects.push(object);
1604 }
1605 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1606 .expect("an object");
1607 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1608 let visibility = |name: &str| {
1609 file.symbols()
1610 .find(|s| s.name() == Ok(name))
1611 .expect("the symbol")
1612 .elf_symbol()
1613 .st_visibility()
1614 };
1615 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1616 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1617 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1618 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1619 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1622 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1623 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1624 }
1625
1626 #[test]
1627 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1628 let bytes = write(
1629 &calling("puts"),
1630 &Data::default(),
1631 &[],
1632 &target(),
1633 Output::default(),
1634 &Info::default(),
1635 )
1636 .expect("an object");
1637 let file = object::File::parse(&bytes[..]).expect("a readable object");
1638 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1639 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1640 }
1641
1642 #[test]
1643 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1644 for (reference, wanted) in [
1645 (Reference::Call, elf::R_X86_64_PLT32),
1646 (Reference::Data, elf::R_X86_64_PC32),
1647 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1648 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1649 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1650 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1651 ] {
1652 let mut text = calling("puts");
1653 text.relocs[0].kind = reference;
1654 let bytes =
1655 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1656 .expect("an object");
1657 let file = object::File::parse(&bytes[..]).expect("a readable object");
1658 let section = file.section_by_name(".text").expect("a text section");
1659 let (offset, reloc) = section.relocations().next().expect("one relocation");
1660 assert_eq!(offset, 1);
1661 assert_eq!(reloc.addend(), -4);
1662 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1663 }
1664 }
1665
1666 #[test]
1669 fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1670 for (reference, wanted) in [
1671 (Reference::AwayWide, elf::R_X86_64_PC64),
1672 (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1673 (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1674 ] {
1675 assert_eq!(crate::elf::r_type(reference), Some(wanted));
1676 }
1677 assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1678 }
1679
1680 #[test]
1681 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1682 let mut text = calling("puts");
1683 text.relocs.push(Reloc {
1684 at: 1,
1685 symbol: "puts".to_owned(),
1686 kind: Reference::Call,
1687 addend: -4,
1688 after: 0,
1689 });
1690 let bytes =
1691 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1692 .expect("an object");
1693 let file = object::File::parse(&bytes[..]).expect("a readable object");
1694 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1695 }
1696
1697 #[test]
1698 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1699 let text = calling("f");
1700 let bytes =
1701 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1702 .expect("an object");
1703 let file = object::File::parse(&bytes[..]).expect("a readable object");
1704 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1705 let f = found.next().expect("the function");
1706 assert!(!f.is_undefined(), "the file defines it");
1707 assert!(found.next().is_none(), "and defines it once");
1708 }
1709
1710 #[test]
1711 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1712 let bytes = write(
1713 &calling("puts"),
1714 &Data::default(),
1715 &[],
1716 &target(),
1717 Output::default(),
1718 &Info::default(),
1719 )
1720 .expect("an object");
1721 let file = object::File::parse(&bytes[..]).expect("a readable object");
1722 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1723 assert!(note.data().expect("no bytes").is_empty());
1724 }
1725
1726 #[test]
1733 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1734 let property = Property { features: Property::IBT | Property::SHSTK };
1735 let output = Output { property, ..Output::default() };
1736 let bytes =
1737 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1738 .expect("an object");
1739 let file = object::File::parse(&bytes[..]).expect("a readable object");
1740 let note = file.section_by_name(".note.gnu.property").expect("the note");
1741 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1742 let want: Vec<u8> = [
1743 4u32,
1744 16,
1745 5,
1746 u32::from_le_bytes(*b"GNU\0"),
1747 Property::X86_FEATURES,
1748 4,
1749 Property::IBT | Property::SHSTK,
1750 0,
1751 ]
1752 .iter()
1753 .flat_map(|word| word.to_le_bytes())
1754 .collect();
1755 assert_eq!(note.data().expect("the bytes"), &want[..]);
1756 }
1757
1758 #[test]
1764 fn a_file_built_to_have_nothing_checked_says_nothing() {
1765 let bytes = write(
1766 &calling("puts"),
1767 &Data::default(),
1768 &[],
1769 &target(),
1770 Output::default(),
1771 &Info::default(),
1772 )
1773 .expect("an object");
1774 let file = object::File::parse(&bytes[..]).expect("a readable object");
1775 assert!(file.section_by_name(".note.gnu.property").is_none());
1776 }
1777
1778 #[test]
1787 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1788 let mut text = calling("puts");
1789 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1790 text.bytes.resize(17, 0x90);
1791 text.unwind.bytes = vec![0; 64];
1794 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1795 text.unwind.relocs.push(Reloc {
1796 at,
1797 symbol: name.to_owned(),
1798 kind: Reference::Address { bytes: 8 },
1799 addend: 0,
1800 after: 0,
1801 });
1802 }
1803 let bytes =
1804 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1805 .expect("an object");
1806 let file = object::File::parse(&bytes[..]).expect("a readable object");
1807 let mut found = points_at(&file);
1808 found.sort_unstable();
1809 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1810 }
1811
1812 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1815 let frames = file.section_by_name(".eh_frame").expect("the table");
1816 frames
1817 .relocations()
1818 .map(|(offset, reloc)| {
1819 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1820 panic!("a record points at something that is not a symbol");
1821 };
1822 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1823 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1824 let section = symbol.section_index().expect("a section symbol is in one");
1825 let name = file.section_by_index(section).expect("a readable section");
1826 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1827 })
1828 .collect()
1829 }
1830
1831 #[test]
1844 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1845 let mut text = two();
1846 text.unwind.bytes = vec![0; 64];
1847 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1848 text.unwind.relocs.push(Reloc {
1849 at,
1850 symbol: name.to_owned(),
1851 kind: Reference::Data,
1852 addend: 0,
1853 after: 0,
1854 });
1855 }
1856 let bytes =
1857 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1858 .expect("an object");
1859 let file = object::File::parse(&bytes[..]).expect("a readable object");
1860 let mut whole = points_at(&file);
1861 whole.sort_unstable();
1862 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1863
1864 let sections =
1865 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1866 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1867 .expect("an object");
1868 let file = object::File::parse(&bytes[..]).expect("a readable object");
1869 let mut split = points_at(&file);
1870 split.sort_unstable();
1871 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1872 }
1873
1874 #[test]
1881 fn a_record_about_something_this_file_does_not_define_is_refused() {
1882 let mut text = calling("puts");
1883 text.unwind.bytes = vec![0; 64];
1884 text.unwind.relocs.push(Reloc {
1885 at: 32,
1886 symbol: "puts".to_owned(),
1887 kind: Reference::Data,
1888 addend: 0,
1889 after: 0,
1890 });
1891 let why =
1892 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1893 .expect_err("a record about a name from somewhere else");
1894 assert!(why.to_string().contains("puts"), "{why}");
1895 }
1896
1897 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1899 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1900 let index = symbol.section_index().expect("a section to be defined in");
1901 let section = file.section_by_index(index).expect("a readable section");
1902 section.name().expect("a named section").to_owned()
1903 }
1904
1905 fn two() -> Text {
1907 let mut text = calling("puts");
1908 text.bytes.resize(16, 0x90);
1911 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1912 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1913 text.relocs.push(Reloc {
1914 at: 17,
1915 symbol: "puts".to_owned(),
1916 kind: Reference::Call,
1917 addend: -4,
1918 after: 0,
1919 });
1920 text
1921 }
1922
1923 #[test]
1930 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1931 let sections =
1932 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1933 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1934 .expect("an object");
1935 let file = object::File::parse(&bytes[..]).expect("a readable object");
1936 assert_eq!(lives_in(&file, "f"), ".text.f");
1937 assert_eq!(lives_in(&file, "g"), ".text.g");
1938 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1939 for name in ["f", "g"] {
1942 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1943 assert_eq!(symbol.address(), 0, "{name}");
1944 assert_eq!(symbol.size(), 6, "{name}");
1945 }
1946 let section = file.section_by_name(".text.g").expect("the second function");
1947 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1948 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1951 }
1952
1953 #[test]
1959 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1960 let sections =
1961 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1962 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1963 .expect("an object");
1964 let file = object::File::parse(&bytes[..]).expect("a readable object");
1965 for name in [".text.f", ".text.g"] {
1966 let section = file.section_by_name(name).expect("a function");
1967 let (offset, _) = section.relocations().next().expect("the call in it");
1968 assert_eq!(offset, 1, "{name}");
1971 assert_eq!(section.relocations().count(), 1, "{name}");
1972 }
1973 }
1974
1975 fn switching() -> Text {
1977 let mut text = two();
1978 let name = ".Lg_j0".to_owned();
1979 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
1980 text
1981 }
1982
1983 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1985 let section = file.section_by_name(section).expect("the table's section");
1986 section
1987 .relocations()
1988 .map(|(offset, reloc)| {
1989 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1990 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1991 panic!("a cell against something that is not a symbol");
1992 };
1993 let symbol = file.symbol_by_index(index).expect("a symbol");
1994 assert_eq!(symbol.kind(), SymbolKind::Section);
1995 let at = symbol.section_index().expect("a section symbol is in one");
1996 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1997 (offset, name.to_owned(), reloc.addend())
1998 })
1999 .collect()
2000 }
2001
2002 #[test]
2003 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
2004 let mut text = switching();
2007 text.relocs[1].symbol = ".Lg_j0".to_owned();
2008 text.relocs[1].kind = Reference::Data;
2009 let bytes =
2010 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2011 .expect("an object");
2012 let file = object::File::parse(&bytes[..]).expect("a readable object");
2013 let rodata = file.section_by_name(".rodata").expect("the table's section");
2014 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2015 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2016 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2017 let (at, reloc) = file
2018 .section_by_name(".text")
2019 .expect("the code")
2020 .relocations()
2021 .find(|(at, _)| *at == 17)
2022 .expect("the reference to the table");
2023 assert_eq!((at, reloc.addend()), (17, -4));
2024 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2025 panic!("a reference against something that is not a symbol");
2026 };
2027 let symbol = file.symbol_by_index(index).expect("a symbol");
2028 assert_eq!(symbol.section_index(), Some(rodata.index()));
2029 assert_eq!(symbol.kind(), SymbolKind::Section);
2030 assert_eq!(
2033 cells(&file, ".rodata"),
2034 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2035 );
2036 }
2037
2038 #[test]
2039 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2040 let sections =
2041 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2042 let bytes =
2043 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2044 .expect("an object");
2045 let file = object::File::parse(&bytes[..]).expect("a readable object");
2046 assert_eq!(
2048 cells(&file, ".rodata.g"),
2049 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2050 );
2051 }
2052
2053 #[test]
2054 fn a_jump_table_outside_the_code_is_refused_on_windows() {
2055 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2056 let written = write(
2057 &switching(),
2058 &Data::default(),
2059 &[],
2060 &target,
2061 Output::default(),
2062 &Info::default(),
2063 );
2064 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2065 }
2066
2067 #[test]
2070 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2071 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2072 let reloc = |at, symbol: &str, bytes| Reloc {
2073 at,
2074 symbol: symbol.to_owned(),
2075 kind: Reference::Address { bytes },
2076 addend: 0,
2077 after: 0,
2078 };
2079 let info = Info {
2080 chunks: vec![
2081 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2082 Chunk {
2083 name: ".debug_info".to_owned(),
2084 bytes: vec![0; 12],
2085 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2086 },
2087 ],
2088 ..Info::default()
2089 };
2090 let bytes =
2091 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2092 .expect("object");
2093 let file = object::File::parse(&bytes[..]).expect("a readable object");
2094 let section = file.section_by_name(".debug_info").expect("the debug section");
2095 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2096 assert_eq!(
2097 kinds,
2098 [
2099 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2100 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2101 ]
2102 );
2103 }
2104
2105 fn variable(name: &str, place: Place) -> Object {
2107 Object {
2108 name: name.to_owned(),
2109 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2110 size: 4,
2111 align: 4,
2112 place,
2113 binding: Binding::Global,
2114 visibility: Visibility::Default,
2115 relocs: Vec::new(),
2116 }
2117 }
2118
2119 fn measured() -> (Text, Data) {
2121 let mut text = calling("puts");
2122 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2123 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2124 let mut table = variable("table", Place::ReadOnly);
2125 table.bytes = vec![0; 8];
2126 table.size = 8;
2127 let apart = |at, to: &str, from: &str| Apart {
2128 object: 0,
2129 at,
2130 to: to.to_owned(),
2131 from: from.to_owned(),
2132 addend: 0,
2133 bytes: 4,
2134 };
2135 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2136 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2137 }
2138
2139 #[test]
2140 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2141 let (text, data) = measured();
2142 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2143 .expect("an object");
2144 let file = object::File::parse(&bytes[..]).expect("a readable object");
2145 let section = file.section_by_name(".rodata").expect("a read only section");
2146 assert_eq!(section.relocations().count(), 0);
2147 let image = section.data().expect("the image");
2148 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2149 }
2150
2151 #[test]
2152 fn a_distance_between_labels_in_two_sections_is_refused() {
2153 let (mut text, data) = measured();
2156 text.bytes.resize(22, 0x90);
2157 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2158 text.labels[1].at = 17;
2159 let output =
2160 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2161 let refused = write(&text, &data, &[], &target(), output, &Info::default());
2162 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2163 }
2164
2165 fn holding(object: Object) -> Vec<u8> {
2167 let data = Data {
2168 apart: Vec::new(),
2169 exports: Vec::new(),
2170 weak: Vec::new(),
2171 objects: vec![object],
2172 };
2173 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2174 .expect("an object")
2175 }
2176
2177 #[test]
2178 fn what_a_variable_is_decides_which_section_it_goes_in() {
2179 for (place, wanted) in [
2180 (Place::Written, ".data"),
2181 (Place::ReadOnly, ".rodata"),
2182 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2183 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2184 (Place::Zero, ".bss"),
2185 (Place::Thread { zero: false }, ".tdata"),
2186 (Place::Thread { zero: true }, ".tbss"),
2187 (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2188 ] {
2189 let bytes = holding(variable("x", place.clone()));
2190 let file = object::File::parse(&bytes[..]).expect("a readable object");
2191 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2192 assert_eq!(section.size(), 4, "{place:?}");
2193 let carried = section.data().expect("the bytes").len();
2196 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2197 }
2198 }
2199
2200 #[test]
2206 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2207 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2208 let bytes = holding(variable("counter", place.clone()));
2209 let file = object::File::parse(&bytes[..]).expect("a readable object");
2210 let symbol = file
2211 .symbols()
2212 .find(|symbol| symbol.name() == Ok("counter"))
2213 .unwrap_or_else(|| panic!("{place:?}"));
2214 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2215 }
2216 }
2217
2218 #[test]
2224 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2225 for (name, wanted) in [
2226 (".init_array", elf::SHT_INIT_ARRAY),
2227 (".init_array.00101", elf::SHT_INIT_ARRAY),
2228 (".fini_array", elf::SHT_FINI_ARRAY),
2229 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2230 (".init_arrays", elf::SHT_PROGBITS),
2231 ] {
2232 let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2233 let file = object::File::parse(&bytes[..]).expect("a readable object");
2234 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2235 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2236 panic!("{name} is not an elf section");
2237 };
2238 assert_eq!(sh_type, wanted, "{name}");
2239 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2240 }
2241 }
2242
2243 #[test]
2247 fn a_named_section_carries_the_flags_of_what_is_in_it() {
2248 for (name, holds, kind, flags) in [
2249 (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2250 (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2251 (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2252 ] {
2253 let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2254 let file = object::File::parse(&bytes[..]).expect("a readable object");
2255 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2256 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2257 panic!("{name} is not an elf section");
2258 };
2259 assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2260 assert_eq!(section.size(), 4, "{name}");
2261 }
2262 }
2263
2264 #[test]
2270 fn two_variables_in_one_named_section_share_it() {
2271 let objects = vec![
2272 variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2273 variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2274 ];
2275 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2276 let bytes =
2277 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2278 .expect("an object");
2279 let file = object::File::parse(&bytes[..]).expect("a readable object");
2280 let named: Vec<_> =
2281 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2282 assert_eq!(named.len(), 1);
2283 assert_eq!(named[0].size(), 8);
2284 }
2285
2286 #[test]
2290 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2291 let sections =
2292 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2293 for (place, wanted) in [
2294 (Place::Written, ".data.x"),
2295 (Place::ReadOnly, ".rodata.x"),
2296 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2297 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2298 (Place::Zero, ".bss.x"),
2299 (Place::Thread { zero: false }, ".tdata.x"),
2300 (Place::Thread { zero: true }, ".tbss.x"),
2301 ] {
2302 let data = Data {
2303 apart: Vec::new(),
2304 exports: Vec::new(),
2305 weak: Vec::new(),
2306 objects: vec![variable("x", place.clone())],
2307 };
2308 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2309 .expect("object");
2310 let file = object::File::parse(&bytes[..]).expect("a readable object");
2311 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2312 let section = file.section_by_name(wanted).expect("the section it named");
2313 assert_eq!(section.size(), 4, "{place:?}");
2314 let carried = section.data().expect("the bytes").len();
2317 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2318 }
2319 }
2320
2321 #[test]
2325 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2326 let sections =
2327 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2328 let named = Place::Named(".init_array".to_owned(), Holds::Written);
2329 let objects = vec![variable("m", Place::Merged), variable("n", named)];
2330 let bytes = write(
2331 &Text::default(),
2332 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2333 &[],
2334 &target(),
2335 sections,
2336 &Info::default(),
2337 )
2338 .expect("object");
2339 let file = object::File::parse(&bytes[..]).expect("a readable object");
2340 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2341 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2342 assert_eq!(lives_in(&file, "n"), ".init_array");
2343 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2344 }
2345
2346 #[test]
2350 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2351 let sections =
2352 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2353 let pointer = Object {
2354 bytes: vec![0; 8],
2355 size: 8,
2356 align: 8,
2357 relocs: vec![Reloc {
2358 at: 0,
2359 symbol: "y".to_owned(),
2360 kind: Reference::Address { bytes: 8 },
2361 addend: 0,
2362 after: 0,
2363 }],
2364 ..variable("p", Place::Written)
2365 };
2366 let objects = vec![variable("first", Place::Written), pointer];
2367 let bytes = write(
2368 &Text::default(),
2369 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2370 &[],
2371 &target(),
2372 sections,
2373 &Info::default(),
2374 )
2375 .expect("object");
2376 let file = object::File::parse(&bytes[..]).expect("a readable object");
2377 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2378 let (offset, reloc) = section.relocations().next().expect("one relocation");
2379 assert_eq!(offset, 0);
2382 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2383 }
2384
2385 #[test]
2393 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2394 let place = Place::RelocReadOnly { local: true };
2395 let data = Data {
2396 apart: Vec::new(),
2397 exports: Vec::new(),
2398 weak: Vec::new(),
2399 objects: vec![variable("first", place.clone()), variable("second", place)],
2400 };
2401 let bytes =
2402 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2403 .expect("an object");
2404 let file = object::File::parse(&bytes[..]).expect("a readable object");
2405 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2406 assert_eq!(named, 1, "one section holding both, not one each");
2407 }
2408
2409 #[test]
2410 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2411 let mut data = Data {
2412 apart: Vec::new(),
2413 exports: Vec::new(),
2414 weak: Vec::new(),
2415 objects: vec![variable("first", Place::Written)],
2416 };
2417 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2418 let bytes =
2419 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2420 .expect("an object");
2421 let file = object::File::parse(&bytes[..]).expect("a readable object");
2422 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2423 assert_eq!(second.kind(), SymbolKind::Data);
2424 assert_eq!(second.size(), 4);
2425 assert_eq!(second.address(), 16);
2429 }
2430
2431 #[test]
2432 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2433 for (binding, global, weak) in [
2434 (Binding::Global, true, false),
2435 (Binding::Local, false, false),
2436 (Binding::Weak, true, true),
2437 ] {
2438 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2439 let file = object::File::parse(&bytes[..]).expect("a readable object");
2440 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2441 assert_eq!(x.is_global(), global, "{binding:?}");
2442 assert_eq!(x.is_weak(), weak, "{binding:?}");
2443 }
2444 }
2445
2446 #[test]
2447 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2448 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2449 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2450 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2451 assert!(x.is_common(), "the linker merges every definition of this name into one");
2452 assert_eq!(x.size(), 4);
2453 assert_eq!(x.address(), 0);
2457 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2458 }
2459
2460 #[test]
2461 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2462 let object = Object {
2463 bytes: vec![0; 8],
2464 size: 8,
2465 align: 8,
2466 relocs: vec![Reloc {
2467 at: 0,
2468 symbol: "y".to_owned(),
2469 kind: Reference::Address { bytes: 8 },
2470 addend: 16,
2471 after: 0,
2472 }],
2473 ..variable("p", Place::Written)
2474 };
2475 let bytes = holding(object);
2476 let file = object::File::parse(&bytes[..]).expect("a readable object");
2477 let section = file.section_by_name(".data").expect("a data section");
2478 let (offset, reloc) = section.relocations().next().expect("one relocation");
2479 assert_eq!(offset, 0);
2480 assert_eq!(reloc.addend(), 16);
2481 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2482 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2483 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2484 }
2485
2486 #[test]
2493 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2494 let mut text = Text::default();
2495 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2496 text.bytes.resize(8, 0x90);
2497 text.relocs.push(Reloc {
2498 at: 1,
2499 symbol: "hook".to_owned(),
2500 kind: Reference::Call,
2501 addend: -4,
2502 after: 0,
2503 });
2504 let data = Data {
2505 apart: Vec::new(),
2506 exports: Vec::new(),
2507 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2508 objects: vec![],
2509 };
2510 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2511 .expect("an object");
2512 let file = object::File::parse(&bytes[..]).expect("a readable object");
2513
2514 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2515 assert!(hook.is_undefined(), "nothing here defines it");
2516 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2517
2518 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2522 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2523 }
2524
2525 #[test]
2540 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2541 let mut text = Text::default();
2542 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2543 text.bytes.resize(16, 0x90);
2544 text.relocs.push(Reloc {
2545 at: 3,
2546 symbol: "flags".to_owned(),
2547 kind: Reference::Thread,
2548 addend: -4,
2549 after: 0,
2550 });
2551 text.relocs.push(Reloc {
2554 at: 10,
2555 symbol: "shared".to_owned(),
2556 kind: Reference::Got,
2557 addend: -4,
2558 after: 0,
2559 });
2560 let data =
2561 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2562 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2563 .expect("an object");
2564 let file = object::File::parse(&bytes[..]).expect("a readable object");
2565
2566 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2567 assert!(flags.is_undefined(), "nothing here defines it");
2568 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2569
2570 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2571 assert!(shared.is_undefined(), "nothing here defines this one either");
2572 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2573 }
2574
2575 #[test]
2577 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2578 let mut data = Data {
2579 apart: Vec::new(),
2580 exports: Vec::new(),
2581 weak: Vec::new(),
2582 objects: vec![variable("first", Place::Written)],
2583 };
2584 data.objects.push(Object {
2585 bytes: vec![0; 16],
2586 size: 16,
2587 align: 8,
2588 relocs: vec![Reloc {
2589 at: 8,
2590 symbol: "y".to_owned(),
2591 kind: Reference::Address { bytes: 8 },
2592 addend: 0,
2593 after: 0,
2594 }],
2595 ..variable("second", Place::Written)
2596 });
2597 let bytes =
2598 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2599 .expect("an object");
2600 let file = object::File::parse(&bytes[..]).expect("a readable object");
2601 let section = file.section_by_name(".data").expect("a data section");
2602 let (offset, _) = section.relocations().next().expect("one relocation");
2603 assert_eq!(offset, 16);
2606 }
2607
2608 #[test]
2609 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2610 let data = Data {
2611 apart: Vec::new(),
2612 exports: Vec::new(),
2613 weak: Vec::new(),
2614 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2615 };
2616 let aliases = [Alias {
2617 name: "b".to_owned(),
2618 target: "a".to_owned(),
2619 binding: Binding::Global,
2620 visibility: Visibility::Default,
2621 ifunc: false,
2622 }];
2623 let bytes = write(
2624 &Text::default(),
2625 &data,
2626 &aliases,
2627 &target(),
2628 Output::default(),
2629 &Info::default(),
2630 )
2631 .expect("an object");
2632 let file = object::File::parse(&bytes[..]).expect("a readable object");
2633 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2634 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2635 assert_eq!(b.address(), a.address(), "the same place");
2636 assert_eq!(b.size(), a.size());
2637 assert_eq!(b.section_index(), a.section_index());
2638 assert!(a.is_local(), "the target was written `static`");
2641 assert!(b.is_global(), "and the name given to it was not");
2642 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2644 }
2645
2646 #[test]
2647 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2648 let text = calling("puts");
2649 let aliases = [Alias {
2650 name: "g".to_owned(),
2651 target: "f".to_owned(),
2652 binding: Binding::Weak,
2653 visibility: Visibility::Default,
2654 ifunc: false,
2655 }];
2656 let bytes = write(
2657 &text,
2658 &Data::default(),
2659 &aliases,
2660 &target(),
2661 Output::default(),
2662 &Info::default(),
2663 )
2664 .expect("an object");
2665 let file = object::File::parse(&bytes[..]).expect("a readable object");
2666 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2667 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2668 assert_eq!(g.address(), f.address());
2669 assert_eq!(g.size(), f.size());
2670 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2671 assert!(g.is_weak(), "so that a program may define the name itself instead");
2672 }
2673
2674 #[test]
2678 fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2679 let text = calling("puts");
2680 for (binding, bind) in [
2681 (Binding::Global, elf::STB_GLOBAL),
2682 (Binding::Weak, elf::STB_WEAK),
2683 (Binding::Local, elf::STB_LOCAL),
2684 ] {
2685 let aliases = [Alias {
2686 name: "g".to_owned(),
2687 target: "f".to_owned(),
2688 binding,
2689 visibility: Visibility::Default,
2690 ifunc: true,
2691 }];
2692 let bytes = write(
2693 &text,
2694 &Data::default(),
2695 &aliases,
2696 &target(),
2697 Output::default(),
2698 &Info::default(),
2699 )
2700 .expect("an object");
2701 let file = object::File::parse(&bytes[..]).expect("a readable object");
2702 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2703 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2704 assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2705 let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2706 panic!("an ELF symbol");
2707 };
2708 assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2709 let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2710 let os_abi = elf.elf_header().e_ident.os_abi;
2711 assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2712 }
2713 }
2714
2715 #[test]
2718 fn an_ifunc_is_refused_on_a_format_without_the_type() {
2719 let aliases = [Alias {
2720 name: "g".to_owned(),
2721 target: "f".to_owned(),
2722 binding: Binding::Global,
2723 visibility: Visibility::Default,
2724 ifunc: true,
2725 }];
2726 let error = write(
2727 &calling("puts"),
2728 &Data::default(),
2729 &aliases,
2730 &windows(),
2731 Output::default(),
2732 &Info::default(),
2733 )
2734 .expect_err("no ifunc on COFF");
2735 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2736 }
2737
2738 #[test]
2741 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2742 let aliases = [Alias {
2743 name: "b".to_owned(),
2744 target: "a".to_owned(),
2745 binding: Binding::Global,
2746 visibility: Visibility::Default,
2747 ifunc: false,
2748 }];
2749 let error = write(
2750 &Text::default(),
2751 &Data::default(),
2752 &aliases,
2753 &target(),
2754 Output::default(),
2755 &Info::default(),
2756 )
2757 .expect_err("nothing to point at");
2758 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2759 }
2760
2761 #[test]
2762 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2763 let text = calling("puts");
2764 for triple in [
2765 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2766 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2767 ] {
2768 let error = write(
2769 &text,
2770 &Data::default(),
2771 &[],
2772 &TargetInfo::new(triple),
2773 Output::default(),
2774 &Info::default(),
2775 )
2776 .expect_err("no writer");
2777 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2778 }
2779 }
2780
2781 #[test]
2787 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2788 let mut text = calling("puts");
2789 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2790 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2791 text.bytes.resize(33, 0x90);
2792 let data = Data {
2793 apart: Vec::new(),
2794 exports: Vec::new(),
2795 weak: Vec::new(),
2796 objects: vec![variable("seen", Place::Written), {
2797 let mut quiet = variable("quiet", Place::Zero);
2798 quiet.binding = Binding::Local;
2799 quiet
2800 }],
2801 };
2802 let aliases = [Alias {
2803 name: "second".to_owned(),
2804 target: "f".to_owned(),
2805 binding: Binding::Global,
2806 visibility: Visibility::Default,
2807 ifunc: false,
2808 }];
2809
2810 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2811 assert_eq!(names, ["f", "shared", "seen", "second"]);
2812
2813 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2814 .expect("an object");
2815 let file = object::File::parse(&bytes[..]).expect("a readable object");
2816 let found: Vec<String> = file
2817 .symbols()
2818 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2819 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2820 .collect();
2821 let mut sorted = names.clone();
2822 sorted.sort();
2823 let mut theirs = found;
2824 theirs.sort();
2825 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2826 }
2827
2828 fn windows() -> TargetInfo {
2830 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2831 }
2832
2833 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2835 let file = object::File::parse(bytes).expect("a readable object");
2836 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2837 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2838 }
2839
2840 #[test]
2841 fn a_windows_target_is_written_rather_than_refused() {
2842 let text = calling("puts");
2843 let bytes =
2844 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2845 .expect("an object");
2846 let file = object::File::parse(&bytes[..]).expect("a readable object");
2847 assert_eq!(file.format(), BinaryFormat::Coff);
2848 let section = file.section_by_name(".text").expect("a text section");
2849 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2850 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2851 assert!(names.contains(&"f"), "{names:?}");
2852 assert!(names.contains(&"puts"), "{names:?}");
2853 }
2854
2855 #[test]
2863 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2864 for (after, typ) in [
2865 (0, pe::IMAGE_REL_AMD64_REL32),
2866 (1, pe::IMAGE_REL_AMD64_REL32_1),
2867 (4, pe::IMAGE_REL_AMD64_REL32_4),
2868 (5, pe::IMAGE_REL_AMD64_REL32_5),
2869 ] {
2870 let mut text = calling("puts");
2871 text.relocs[0].addend = -4 - i64::from(after);
2874 text.relocs[0].after = after;
2875 text.bytes.resize(6 + after as usize, 0x90);
2876 text.funcs[0].len = text.bytes.len();
2877 let bytes = write(
2878 &text,
2879 &Data::default(),
2880 &[],
2881 &windows(),
2882 Output::default(),
2883 &Info::default(),
2884 )
2885 .expect("an object");
2886 let file = object::File::parse(&bytes[..]).expect("a readable object");
2887 let section = file.section_by_name(".text").expect("a text section");
2888 let (_, reloc) = section.relocations().next().expect("the relocation");
2889 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2890 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2893 }
2894 }
2895
2896 #[test]
2899 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2900 let mut text = calling("puts");
2901 text.relocs[0].addend = 12;
2902 let bytes =
2903 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2904 .expect("an object");
2905 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2906 }
2907
2908 #[test]
2909 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2910 let object = Object {
2911 bytes: vec![0; 8],
2912 size: 8,
2913 align: 8,
2914 relocs: vec![Reloc {
2915 at: 0,
2916 symbol: "y".to_owned(),
2917 kind: Reference::Address { bytes: 8 },
2918 addend: 0,
2919 after: 0,
2920 }],
2921 ..variable("p", Place::Written)
2922 };
2923 let data = Data {
2924 apart: Vec::new(),
2925 exports: Vec::new(),
2926 weak: Vec::new(),
2927 objects: vec![object],
2928 };
2929 let bytes =
2930 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2931 .expect("an object");
2932 let file = object::File::parse(&bytes[..]).expect("a readable object");
2933 let section = file.section_by_name(".data").expect("a data section");
2934 let (_, reloc) = section.relocations().next().expect("the relocation");
2935 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2936 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2937 }
2938
2939 #[test]
2943 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
2944 let pointer = Object {
2945 bytes: vec![0; 8],
2946 size: 8,
2947 align: 8,
2948 relocs: vec![Reloc {
2949 at: 0,
2950 symbol: "environ".to_owned(),
2951 kind: Reference::Address { bytes: 8 },
2952 addend: 0,
2953 after: 0,
2954 }],
2955 ..variable(".refptr.environ", Place::Pointer)
2956 };
2957 let data = Data { objects: vec![pointer], ..Data::default() };
2958 let bytes =
2959 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2960 .expect("an object");
2961 let file = object::File::parse(&bytes[..]).expect("a readable object");
2962 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
2963 let SectionFlags::Coff { characteristics } = section.flags() else {
2964 panic!("a COFF section has COFF flags");
2965 };
2966 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
2967 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
2969 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
2970 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
2971 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
2972 assert_eq!(comdat.kind(), ComdatKind::Any);
2973 assert_eq!(comdat.name(), Ok(".refptr.environ"));
2974 let (_, reloc) = section.relocations().next().expect("the address it holds");
2975 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
2976 }
2977
2978 #[test]
2982 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
2983 let exports = vec![
2984 Export { name: "offered".to_owned(), kind: Offer::Function },
2985 Export { name: "count".to_owned(), kind: Offer::Variable },
2986 Export { name: "kept".to_owned(), kind: Offer::Hidden },
2987 ];
2988 let data = Data { exports, ..Data::default() };
2989 let bytes =
2990 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2991 .expect("an object");
2992 let file = object::File::parse(&bytes[..]).expect("a readable object");
2993 let section = file.section_by_name(".drectve").expect("the options section");
2994 assert_eq!(
2995 section.data().expect("the options"),
2996 b" -export:offered -export:count,data -exclude-symbols:kept"
2997 );
2998 let SectionFlags::Coff { characteristics } = section.flags() else {
2999 panic!("a COFF section has COFF flags");
3000 };
3001 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
3002 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
3003
3004 let none = write(
3005 &Text::default(),
3006 &Data::default(),
3007 &[],
3008 &windows(),
3009 Output::default(),
3010 &Info::default(),
3011 )
3012 .expect("an object");
3013 let file = object::File::parse(&none[..]).expect("a readable object");
3014 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3015 }
3016
3017 #[test]
3020 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3021 for local in [false, true] {
3022 let data = Data {
3023 apart: Vec::new(),
3024 exports: Vec::new(),
3025 weak: Vec::new(),
3026 objects: vec![variable("p", Place::RelocReadOnly { local })],
3027 };
3028 let bytes = write(
3029 &Text::default(),
3030 &data,
3031 &[],
3032 &windows(),
3033 Output::default(),
3034 &Info::default(),
3035 )
3036 .expect("an object");
3037 let file = object::File::parse(&bytes[..]).expect("a readable object");
3038 assert!(file.section_by_name(".rdata").is_some(), "{local}");
3039 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3040 }
3041 }
3042
3043 #[test]
3046 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3047 let text = calling("puts");
3048 let output = Output { property: Property { features: 3 }, ..Output::default() };
3049 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3050 .expect("an object");
3051 let file = object::File::parse(&bytes[..]).expect("a readable object");
3052 assert!(file.section_by_name(".note.GNU-stack").is_none());
3053 assert!(file.section_by_name(".note.gnu.property").is_none());
3054 }
3055
3056 #[test]
3061 fn what_this_format_cannot_say_is_refused_by_name() {
3062 let ordinary = Text::default();
3063 let empty = Data::default();
3064
3065 let mut thread = Data::default();
3066 thread.objects.push(variable("t", Place::Thread { zero: true }));
3067
3068 let mut gathered = Data::default();
3069 gathered
3070 .objects
3071 .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3072
3073 let mut table = calling("puts");
3074 table.relocs[0].kind = Reference::Got;
3075
3076 let mut room = calling("puts");
3077 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3078
3079 let cases: [(&str, &Text, &Data); 4] = [
3080 ("thread-local", &ordinary, &thread),
3081 ("startup", &ordinary, &gathered),
3082 ("table", &table, &empty),
3083 ("patcher", &room, &empty),
3084 ];
3085 for (what, text, data) in cases {
3086 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3087 .expect_err("something this format cannot write");
3088 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3089 }
3090 }
3091
3092 #[test]
3095 fn a_thread_local_variable_goes_in_the_tls_section() {
3096 let mut thread = Data::default();
3097 thread.objects.push(variable("t", Place::Thread { zero: false }));
3098 let bytes =
3099 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3100 .expect("an object");
3101 let file = object::File::parse(&bytes[..]).expect("a readable object");
3102 assert!(file.section_by_name(".tls$").is_some());
3103 }
3104
3105 #[test]
3109 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3110 let mut text = calling("puts");
3111 text.funcs[0].visibility = Visibility::Hidden;
3112 let bytes =
3113 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3114 .expect("an object");
3115 let file = object::File::parse(&bytes[..]).expect("a readable object");
3116 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3117 assert!(symbol.is_global(), "a name others may use either way");
3118 }
3119
3120 #[test]
3121 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3122 let text = calling("puts");
3123 let data = Data {
3124 apart: Vec::new(),
3125 exports: Vec::new(),
3126 weak: Vec::new(),
3127 objects: vec![variable("shared", Place::Written)],
3128 };
3129 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3130 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3131 }
3132
3133 #[test]
3137 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3138 let text = calling("puts");
3139 for triple in [
3140 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3141 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3142 ] {
3143 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3144 .expect_err("no writer");
3145 assert!(matches!(error, Error::Format { .. }), "{error:?}");
3146 }
3147 }
3148
3149 fn i386() -> TargetInfo {
3151 TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3152 }
3153
3154 #[test]
3161 fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3162 let mut text = calling("puts");
3163 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3164 text.funcs[0].start = 3;
3165 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3166 text.relocs[0].at = 4;
3167 let data = Data {
3168 objects: vec![Object {
3169 name: "p".to_owned(),
3170 bytes: vec![0; 4],
3171 size: 4,
3172 align: 4,
3173 place: Place::Written,
3174 binding: Binding::Global,
3175 visibility: Visibility::Default,
3176 relocs: vec![Reloc {
3177 at: 0,
3178 symbol: "x".to_owned(),
3179 kind: Reference::Address { bytes: 4 },
3180 addend: 12,
3181 after: 0,
3182 }],
3183 }],
3184 ..Data::default()
3185 };
3186 let property = Property { features: Property::IBT | Property::SHSTK };
3187 let output = Output { property, ..Output::default() };
3188 let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3189 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3190 assert_eq!(file.architecture(), Architecture::I386);
3191 assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3192 assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3193
3194 let code = file.section_by_name(".text").expect("a text section");
3196 let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3197 panic!("one relocation in the text")
3198 };
3199 assert_eq!(*at, 4);
3200 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3201 assert!(reloc.has_implicit_addend());
3202 assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3203
3204 let variable = file.section_by_name(".data").expect("a data section");
3206 let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3207 panic!("one relocation in the data")
3208 };
3209 assert_eq!(*at, 0);
3210 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3211 assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3212
3213 let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3215 assert_eq!(record.size(), 4);
3216 assert_eq!(record.align(), 4);
3217 let index = code.index().0;
3218 assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3219 let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3220 panic!("one address in the record")
3221 };
3222 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3223
3224 let note = file.section_by_name(".note.gnu.property").expect("the note");
3226 assert_eq!(note.align(), 4);
3227 let want: Vec<u8> = [
3228 4u32,
3229 12,
3230 5,
3231 u32::from_le_bytes(*b"GNU\0"),
3232 Property::X86_FEATURES,
3233 4,
3234 Property::IBT | Property::SHSTK,
3235 ]
3236 .iter()
3237 .flat_map(|word| word.to_le_bytes())
3238 .collect();
3239 assert_eq!(note.data().expect("the bytes"), &want[..]);
3240 }
3241
3242 #[test]
3245 fn an_i386_address_less_three_gigabytes_wraps_in_its_four_bytes() {
3246 let object = |bytes: u8| Object {
3247 name: "cr3".to_owned(),
3248 bytes: vec![0; usize::from(bytes)],
3249 size: u64::from(bytes),
3250 align: u64::from(bytes),
3251 place: Place::Written,
3252 binding: Binding::Global,
3253 visibility: Visibility::Default,
3254 relocs: vec![Reloc {
3255 at: 0,
3256 symbol: "swapper_pg_dir".to_owned(),
3257 kind: Reference::Address { bytes },
3258 addend: -0xC000_0000,
3259 after: 0,
3260 }],
3261 };
3262 let data = Data { objects: vec![object(4)], ..Data::default() };
3263 let bytes =
3264 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default())
3265 .expect("an object");
3266 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3267 let variable = file.section_by_name(".data").expect("a data section");
3268 assert_eq!(variable.data().expect("the bytes"), &0x4000_0000u32.to_le_bytes());
3269
3270 let data = Data { objects: vec![object(2)], ..Data::default() };
3271 let refused =
3272 write(&Text::default(), &data, &[], &i386(), Output::default(), &Info::default());
3273 assert!(refused.is_err(), "two bytes cannot hold a name less three gigabytes");
3274 }
3275
3276 #[test]
3279 fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3280 let info = Info {
3281 chunks: vec![Chunk {
3282 name: ".debug_info".to_owned(),
3283 bytes: vec![0; 64],
3284 relocs: vec![Reloc {
3285 at: 8,
3286 symbol: "f".to_owned(),
3287 kind: Reference::Address { bytes: 4 },
3288 addend: 7,
3289 after: 0,
3290 }],
3291 }],
3292 compress: Compress::Zlib,
3293 };
3294 let bytes =
3295 write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3296 .expect("an object");
3297 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3298 let section = file.section_by_name(".debug_info").expect("the debug section");
3299 let packed =
3300 SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3301 assert_ne!(section.flags(), packed);
3302 let data = section.data().expect("the bytes");
3303 assert_eq!(data.len(), 64);
3304 assert_eq!(&data[8..12], &7u32.to_le_bytes());
3305 }
3306
3307 fn i386_windows() -> TargetInfo {
3309 TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3310 }
3311
3312 #[test]
3321 fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3322 let mut text = calling("puts");
3323 text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3324 text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3325 text.relocs.push(Reloc {
3326 at: 7,
3327 symbol: "__imp_GetTickCount".to_owned(),
3328 kind: Reference::Call,
3329 addend: -4,
3330 after: 0,
3331 });
3332 let data = Data {
3333 objects: vec![Object {
3334 name: "p".to_owned(),
3335 bytes: vec![0; 12],
3336 size: 12,
3337 align: 4,
3338 place: Place::Written,
3339 binding: Binding::Global,
3340 visibility: Visibility::Default,
3341 relocs: vec![
3342 Reloc {
3343 at: 0,
3344 symbol: "x".to_owned(),
3345 kind: Reference::Address { bytes: 4 },
3346 addend: 12,
3347 after: 0,
3348 },
3349 Reloc {
3350 at: 4,
3351 symbol: "f".to_owned(),
3352 kind: Reference::Image,
3353 addend: 0,
3354 after: 0,
3355 },
3356 Reloc {
3357 at: 8,
3358 symbol: "x".to_owned(),
3359 kind: Reference::Away,
3360 addend: 0,
3361 after: 0,
3362 },
3363 ],
3364 }],
3365 ..Data::default()
3366 };
3367 let aliases = [Alias {
3368 name: "g".to_owned(),
3369 target: "f".to_owned(),
3370 binding: Binding::Global,
3371 visibility: Visibility::Default,
3372 ifunc: false,
3373 }];
3374 let target = i386_windows();
3375 let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3376 .expect("an object");
3377 let file = object::File::parse(&bytes[..]).expect("a readable object");
3378 assert_eq!(file.format(), BinaryFormat::Coff);
3379 assert_eq!(file.architecture(), Architecture::I386);
3380 assert!(!file.is_64());
3381
3382 let named = |name: &str| file.symbol_by_name(name).is_some();
3383 for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3384 assert!(named(name), "{name}");
3385 }
3386 for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3387 assert!(!named(name), "{name}");
3388 }
3389
3390 let code = file.section_by_name(".text").expect("a text section");
3391 let relocs: Vec<_> = code.relocations().collect();
3392 assert_eq!(relocs.len(), 2);
3393 for (at, reloc) in &relocs {
3394 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3395 let at = *at as usize;
3396 assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3397 }
3398
3399 let variable = file.section_by_name(".data").expect("a data section");
3400 let types: Vec<_> = variable
3401 .relocations()
3402 .map(|(at, reloc)| match reloc.flags() {
3403 RelocationFlags::Coff { typ } => (at, typ),
3404 flags => panic!("{flags:?}"),
3405 })
3406 .collect();
3407 assert_eq!(
3408 types,
3409 [
3410 (0, pe::IMAGE_REL_I386_DIR32),
3411 (4, pe::IMAGE_REL_I386_DIR32NB),
3412 (8, pe::IMAGE_REL_I386_REL32)
3413 ]
3414 );
3415 let image = variable.data().expect("the bytes");
3418 assert_eq!(&image[0..4], &12u32.to_le_bytes());
3419 assert_eq!(&image[8..12], &4u32.to_le_bytes());
3420
3421 let listed = defines(&text, &data, &aliases, &target).expect("a list");
3423 assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3424 }
3425
3426 #[test]
3429 fn an_i386_windows_object_has_no_unwind_table() {
3430 let mut text = calling("puts");
3431 text.unwind.bytes = vec![0; 12];
3432 let bytes = write(
3433 &text,
3434 &Data::default(),
3435 &[],
3436 &i386_windows(),
3437 Output::default(),
3438 &Info::default(),
3439 )
3440 .expect("an object");
3441 let file = object::File::parse(&bytes[..]).expect("a readable object");
3442 assert!(file.section_by_name(".pdata").is_none());
3443 assert!(file.section_by_name(".xdata").is_none());
3444 assert!(file.section_by_name(".eh_frame").is_none());
3445 }
3446
3447 #[test]
3450 fn an_i386_windows_object_says_it_is_safe_for_safeseh() {
3451 let write_for = |target: &TargetInfo| {
3452 write(
3453 &calling("puts"),
3454 &Data::default(),
3455 &[],
3456 target,
3457 Output::default(),
3458 &Info::default(),
3459 )
3460 .expect("an object")
3461 };
3462 let bytes = write_for(&i386_windows());
3463 let file = object::File::parse(&bytes[..]).expect("a readable object");
3464 let feat = file.symbol_by_name("@feat.00").expect("the feature symbol");
3465 let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
3467 let raw = coff.symbol_by_name("@feat.00").expect("the feature symbol");
3468 assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
3469 assert_eq!(feat.section(), object::SymbolSection::Absolute);
3470 assert!(feat.is_local());
3471 let bytes = write_for(&windows());
3472 let file = object::File::parse(&bytes[..]).expect("a readable object");
3473 assert!(file.symbol_by_name("@feat.00").is_none());
3474 }
3475
3476 #[test]
3479 fn i386_windows_has_no_eight_byte_or_table_relocations() {
3480 for kind in [
3481 Reference::Address { bytes: 8 },
3482 Reference::AwayWide,
3483 Reference::Got,
3484 Reference::GotOffset,
3485 Reference::Slot,
3486 Reference::Thread,
3487 ] {
3488 assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3489 }
3490 assert_eq!(
3491 Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3492 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3493 );
3494 assert_eq!(
3495 Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3496 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3497 "an address an instruction holds"
3498 );
3499 }
3500}