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 if addend < -(1i64 << (bits - 1)) || addend >= 1i64 << bits {
1317 let why =
1318 format!("{addend} added to a name, and there are {width} bytes to keep it in");
1319 return Err(Error::Refused { why });
1320 }
1321 let at = usize::try_from(relocation.offset).unwrap_or(usize::MAX);
1322 let data = obj.section_mut(section).data_mut();
1323 let Some(place) = data.get_mut(at..).and_then(|rest| rest.get_mut(..width)) else {
1324 let why = format!("a relocation at {at} is past the end of its section");
1325 return Err(Error::Refused { why });
1326 };
1327 place.copy_from_slice(&addend.to_le_bytes()[..width]);
1328 relocation.addend = 0;
1329 }
1330 obj.add_relocation(section, relocation).map_err(|why| Error::Refused { why: why.to_string() })
1331}
1332
1333fn written(target: &TargetInfo) -> Option<(Flavour, Architecture)> {
1339 let flavour = Flavour::of(target)?;
1340 let machine = flavour.machine(target.tuple.arch())?;
1341 (machine != Architecture::Aarch64).then_some((flavour, machine))
1342}
1343
1344pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1357 match binding {
1358 Binding::Local => SymbolScope::Compilation,
1359 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1360 }
1361}
1362
1363#[cfg(test)]
1364mod tests {
1365 use super::*;
1366
1367 use object::read::elf::Sym as _;
1368 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1369 use object::{elf, pe};
1370 use rucc_target::{Arch, Env, Os, Triple};
1371
1372 use crate::elf::PATCHABLE;
1373 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1374
1375 fn target() -> TargetInfo {
1377 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1378 }
1379
1380 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1385 Extent {
1386 name,
1387 start,
1388 len,
1389 align: crate::FUNC_ALIGN,
1390 binding,
1391 visibility: Visibility::Default,
1392 patch: None,
1393 landings: Vec::new(),
1394 }
1395 }
1396
1397 fn calling(name: &str) -> Text {
1399 Text {
1400 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1401 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1402 relocs: vec![Reloc {
1403 at: 1,
1404 symbol: name.to_owned(),
1405 kind: Reference::Call,
1406 addend: -4,
1407 after: 0,
1408 }],
1409 ..Text::default()
1410 }
1411 }
1412
1413 #[test]
1414 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1415 let text = calling("puts");
1416 let bytes =
1417 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1418 .expect("an object");
1419 let file = object::File::parse(&bytes[..]).expect("a readable object");
1420 let section = file.section_by_name(".text").expect("a text section");
1421 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1422 }
1423
1424 #[test]
1425 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1426 let mut text = calling("puts");
1427 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1428 text.bytes.resize(17, 0x90);
1429 let bytes =
1430 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1431 .expect("an object");
1432 let file = object::File::parse(&bytes[..]).expect("a readable object");
1433 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1434 assert_eq!(g.address(), 16);
1435 assert_eq!(g.size(), 1);
1436 assert_eq!(g.kind(), SymbolKind::Text);
1437 assert!(g.is_global(), "nothing said otherwise about this one");
1438 }
1439
1440 #[test]
1441 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1442 let mut text = calling("puts");
1443 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1444 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1445 text.bytes.resize(33, 0x90);
1446 let bytes =
1447 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1448 .expect("an object");
1449 let file = object::File::parse(&bytes[..]).expect("a readable object");
1450 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1451 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1454 assert!(!hidden.is_weak());
1455 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1456 assert!(shared.is_weak(), "a weak function has to be able to lose");
1457 assert!(shared.is_global());
1458 }
1459
1460 #[test]
1477 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1478 let mut text = calling("puts");
1479 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1480 text.funcs[0].start = 3;
1481 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1482 text.relocs[0].at = 4;
1483 let bytes =
1484 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1485 .expect("an object");
1486 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1487 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1488 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1489 assert_eq!(section.align(), 8);
1490 let header = section.elf_section_header();
1491 assert_eq!(
1492 header.sh_flags.get(Endianness::Little),
1493 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1494 );
1495 let index = file.section_by_name(".text").expect("a text section").index().0;
1498 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1499 assert_ne!(index, 0);
1500
1501 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1503 panic!("one address in the record")
1504 };
1505 assert_eq!(*at, 0);
1506 assert_eq!(reloc.addend(), 0);
1507 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1508 }
1509
1510 #[test]
1512 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1513 let text = calling("puts");
1514 let bytes =
1515 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1516 .expect("an object");
1517 let file = object::File::parse(&bytes[..]).expect("a readable object");
1518 assert!(file.section_by_name(PATCHABLE).is_none());
1519 }
1520
1521 #[test]
1527 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1528 let mut text = calling("puts");
1529 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1530 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1531 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1532 text.bytes.resize(17, 0x90);
1533 let output =
1534 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1535 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1536 .expect("an object");
1537 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1538 let links: Vec<usize> = file
1539 .sections()
1540 .filter(|section| section.name() == Ok(PATCHABLE))
1541 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1542 .collect();
1543 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1544 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1545 }
1546
1547 #[test]
1548 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1549 let mut text = calling("puts");
1550 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1551 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1552 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1553 text.bytes.resize(49, 0x90);
1554 let bytes =
1555 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1556 .expect("an object");
1557 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1558 let visibility = |name: &str| {
1559 file.symbols()
1560 .find(|s| s.name() == Ok(name))
1561 .expect("the function")
1562 .elf_symbol()
1563 .st_visibility()
1564 };
1565 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1567 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1568 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1571 }
1572
1573 #[test]
1583 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1584 let mut text = calling("puts");
1585 for (index, (name, seen)) in
1586 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1587 {
1588 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1589 func.visibility = seen;
1590 text.funcs.push(func);
1591 }
1592 text.bytes.resize(49, 0x90);
1593 let mut data = Data::default();
1594 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1595 let mut object = variable(name, Place::Written);
1596 object.visibility = seen;
1597 data.objects.push(object);
1598 }
1599 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1600 .expect("an object");
1601 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1602 let visibility = |name: &str| {
1603 file.symbols()
1604 .find(|s| s.name() == Ok(name))
1605 .expect("the symbol")
1606 .elf_symbol()
1607 .st_visibility()
1608 };
1609 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1610 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1611 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1612 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1613 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1616 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1617 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1618 }
1619
1620 #[test]
1621 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1622 let bytes = write(
1623 &calling("puts"),
1624 &Data::default(),
1625 &[],
1626 &target(),
1627 Output::default(),
1628 &Info::default(),
1629 )
1630 .expect("an object");
1631 let file = object::File::parse(&bytes[..]).expect("a readable object");
1632 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1633 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1634 }
1635
1636 #[test]
1637 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1638 for (reference, wanted) in [
1639 (Reference::Call, elf::R_X86_64_PLT32),
1640 (Reference::Data, elf::R_X86_64_PC32),
1641 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1642 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1643 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1644 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1645 ] {
1646 let mut text = calling("puts");
1647 text.relocs[0].kind = reference;
1648 let bytes =
1649 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1650 .expect("an object");
1651 let file = object::File::parse(&bytes[..]).expect("a readable object");
1652 let section = file.section_by_name(".text").expect("a text section");
1653 let (offset, reloc) = section.relocations().next().expect("one relocation");
1654 assert_eq!(offset, 1);
1655 assert_eq!(reloc.addend(), -4);
1656 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1657 }
1658 }
1659
1660 #[test]
1663 fn a_wide_distance_and_a_narrow_address_have_relocations_of_their_own() {
1664 for (reference, wanted) in [
1665 (Reference::AwayWide, elf::R_X86_64_PC64),
1666 (Reference::Address { bytes: 2 }, elf::R_X86_64_16),
1667 (Reference::Address { bytes: 1 }, elf::R_X86_64_8),
1668 ] {
1669 assert_eq!(crate::elf::r_type(reference), Some(wanted));
1670 }
1671 assert_eq!(crate::elf::r_type_aarch64(Reference::AwayWide), Some(elf::R_AARCH64_PREL64));
1672 }
1673
1674 #[test]
1675 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1676 let mut text = calling("puts");
1677 text.relocs.push(Reloc {
1678 at: 1,
1679 symbol: "puts".to_owned(),
1680 kind: Reference::Call,
1681 addend: -4,
1682 after: 0,
1683 });
1684 let bytes =
1685 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1686 .expect("an object");
1687 let file = object::File::parse(&bytes[..]).expect("a readable object");
1688 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1689 }
1690
1691 #[test]
1692 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1693 let text = calling("f");
1694 let bytes =
1695 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1696 .expect("an object");
1697 let file = object::File::parse(&bytes[..]).expect("a readable object");
1698 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1699 let f = found.next().expect("the function");
1700 assert!(!f.is_undefined(), "the file defines it");
1701 assert!(found.next().is_none(), "and defines it once");
1702 }
1703
1704 #[test]
1705 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1706 let bytes = write(
1707 &calling("puts"),
1708 &Data::default(),
1709 &[],
1710 &target(),
1711 Output::default(),
1712 &Info::default(),
1713 )
1714 .expect("an object");
1715 let file = object::File::parse(&bytes[..]).expect("a readable object");
1716 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1717 assert!(note.data().expect("no bytes").is_empty());
1718 }
1719
1720 #[test]
1727 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1728 let property = Property { features: Property::IBT | Property::SHSTK };
1729 let output = Output { property, ..Output::default() };
1730 let bytes =
1731 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1732 .expect("an object");
1733 let file = object::File::parse(&bytes[..]).expect("a readable object");
1734 let note = file.section_by_name(".note.gnu.property").expect("the note");
1735 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1736 let want: Vec<u8> = [
1737 4u32,
1738 16,
1739 5,
1740 u32::from_le_bytes(*b"GNU\0"),
1741 Property::X86_FEATURES,
1742 4,
1743 Property::IBT | Property::SHSTK,
1744 0,
1745 ]
1746 .iter()
1747 .flat_map(|word| word.to_le_bytes())
1748 .collect();
1749 assert_eq!(note.data().expect("the bytes"), &want[..]);
1750 }
1751
1752 #[test]
1758 fn a_file_built_to_have_nothing_checked_says_nothing() {
1759 let bytes = write(
1760 &calling("puts"),
1761 &Data::default(),
1762 &[],
1763 &target(),
1764 Output::default(),
1765 &Info::default(),
1766 )
1767 .expect("an object");
1768 let file = object::File::parse(&bytes[..]).expect("a readable object");
1769 assert!(file.section_by_name(".note.gnu.property").is_none());
1770 }
1771
1772 #[test]
1781 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1782 let mut text = calling("puts");
1783 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1784 text.bytes.resize(17, 0x90);
1785 text.unwind.bytes = vec![0; 64];
1788 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1789 text.unwind.relocs.push(Reloc {
1790 at,
1791 symbol: name.to_owned(),
1792 kind: Reference::Address { bytes: 8 },
1793 addend: 0,
1794 after: 0,
1795 });
1796 }
1797 let bytes =
1798 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1799 .expect("an object");
1800 let file = object::File::parse(&bytes[..]).expect("a readable object");
1801 let mut found = points_at(&file);
1802 found.sort_unstable();
1803 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1804 }
1805
1806 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1809 let frames = file.section_by_name(".eh_frame").expect("the table");
1810 frames
1811 .relocations()
1812 .map(|(offset, reloc)| {
1813 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1814 panic!("a record points at something that is not a symbol");
1815 };
1816 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1817 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1818 let section = symbol.section_index().expect("a section symbol is in one");
1819 let name = file.section_by_index(section).expect("a readable section");
1820 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1821 })
1822 .collect()
1823 }
1824
1825 #[test]
1838 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1839 let mut text = two();
1840 text.unwind.bytes = vec![0; 64];
1841 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1842 text.unwind.relocs.push(Reloc {
1843 at,
1844 symbol: name.to_owned(),
1845 kind: Reference::Data,
1846 addend: 0,
1847 after: 0,
1848 });
1849 }
1850 let bytes =
1851 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1852 .expect("an object");
1853 let file = object::File::parse(&bytes[..]).expect("a readable object");
1854 let mut whole = points_at(&file);
1855 whole.sort_unstable();
1856 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1857
1858 let sections =
1859 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1860 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1861 .expect("an object");
1862 let file = object::File::parse(&bytes[..]).expect("a readable object");
1863 let mut split = points_at(&file);
1864 split.sort_unstable();
1865 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1866 }
1867
1868 #[test]
1875 fn a_record_about_something_this_file_does_not_define_is_refused() {
1876 let mut text = calling("puts");
1877 text.unwind.bytes = vec![0; 64];
1878 text.unwind.relocs.push(Reloc {
1879 at: 32,
1880 symbol: "puts".to_owned(),
1881 kind: Reference::Data,
1882 addend: 0,
1883 after: 0,
1884 });
1885 let why =
1886 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1887 .expect_err("a record about a name from somewhere else");
1888 assert!(why.to_string().contains("puts"), "{why}");
1889 }
1890
1891 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1893 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1894 let index = symbol.section_index().expect("a section to be defined in");
1895 let section = file.section_by_index(index).expect("a readable section");
1896 section.name().expect("a named section").to_owned()
1897 }
1898
1899 fn two() -> Text {
1901 let mut text = calling("puts");
1902 text.bytes.resize(16, 0x90);
1905 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1906 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1907 text.relocs.push(Reloc {
1908 at: 17,
1909 symbol: "puts".to_owned(),
1910 kind: Reference::Call,
1911 addend: -4,
1912 after: 0,
1913 });
1914 text
1915 }
1916
1917 #[test]
1924 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1925 let sections =
1926 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1927 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1928 .expect("an object");
1929 let file = object::File::parse(&bytes[..]).expect("a readable object");
1930 assert_eq!(lives_in(&file, "f"), ".text.f");
1931 assert_eq!(lives_in(&file, "g"), ".text.g");
1932 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1933 for name in ["f", "g"] {
1936 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1937 assert_eq!(symbol.address(), 0, "{name}");
1938 assert_eq!(symbol.size(), 6, "{name}");
1939 }
1940 let section = file.section_by_name(".text.g").expect("the second function");
1941 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1942 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1945 }
1946
1947 #[test]
1953 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1954 let sections =
1955 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1956 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1957 .expect("an object");
1958 let file = object::File::parse(&bytes[..]).expect("a readable object");
1959 for name in [".text.f", ".text.g"] {
1960 let section = file.section_by_name(name).expect("a function");
1961 let (offset, _) = section.relocations().next().expect("the call in it");
1962 assert_eq!(offset, 1, "{name}");
1965 assert_eq!(section.relocations().count(), 1, "{name}");
1966 }
1967 }
1968
1969 fn switching() -> Text {
1971 let mut text = two();
1972 let name = ".Lg_j0".to_owned();
1973 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5], absolute: false });
1974 text
1975 }
1976
1977 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1979 let section = file.section_by_name(section).expect("the table's section");
1980 section
1981 .relocations()
1982 .map(|(offset, reloc)| {
1983 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1984 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1985 panic!("a cell against something that is not a symbol");
1986 };
1987 let symbol = file.symbol_by_index(index).expect("a symbol");
1988 assert_eq!(symbol.kind(), SymbolKind::Section);
1989 let at = symbol.section_index().expect("a section symbol is in one");
1990 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1991 (offset, name.to_owned(), reloc.addend())
1992 })
1993 .collect()
1994 }
1995
1996 #[test]
1997 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1998 let mut text = switching();
2001 text.relocs[1].symbol = ".Lg_j0".to_owned();
2002 text.relocs[1].kind = Reference::Data;
2003 let bytes =
2004 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
2005 .expect("an object");
2006 let file = object::File::parse(&bytes[..]).expect("a readable object");
2007 let rodata = file.section_by_name(".rodata").expect("the table's section");
2008 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
2009 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
2010 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
2011 let (at, reloc) = file
2012 .section_by_name(".text")
2013 .expect("the code")
2014 .relocations()
2015 .find(|(at, _)| *at == 17)
2016 .expect("the reference to the table");
2017 assert_eq!((at, reloc.addend()), (17, -4));
2018 let object::RelocationTarget::Symbol(index) = reloc.target() else {
2019 panic!("a reference against something that is not a symbol");
2020 };
2021 let symbol = file.symbol_by_index(index).expect("a symbol");
2022 assert_eq!(symbol.section_index(), Some(rodata.index()));
2023 assert_eq!(symbol.kind(), SymbolKind::Section);
2024 assert_eq!(
2027 cells(&file, ".rodata"),
2028 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
2029 );
2030 }
2031
2032 #[test]
2033 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
2034 let sections =
2035 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
2036 let bytes =
2037 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
2038 .expect("an object");
2039 let file = object::File::parse(&bytes[..]).expect("a readable object");
2040 assert_eq!(
2042 cells(&file, ".rodata.g"),
2043 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
2044 );
2045 }
2046
2047 #[test]
2048 fn a_jump_table_outside_the_code_is_refused_on_windows() {
2049 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2050 let written = write(
2051 &switching(),
2052 &Data::default(),
2053 &[],
2054 &target,
2055 Output::default(),
2056 &Info::default(),
2057 );
2058 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
2059 }
2060
2061 #[test]
2064 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
2065 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
2066 let reloc = |at, symbol: &str, bytes| Reloc {
2067 at,
2068 symbol: symbol.to_owned(),
2069 kind: Reference::Address { bytes },
2070 addend: 0,
2071 after: 0,
2072 };
2073 let info = Info {
2074 chunks: vec![
2075 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
2076 Chunk {
2077 name: ".debug_info".to_owned(),
2078 bytes: vec![0; 12],
2079 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
2080 },
2081 ],
2082 ..Info::default()
2083 };
2084 let bytes =
2085 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
2086 .expect("object");
2087 let file = object::File::parse(&bytes[..]).expect("a readable object");
2088 let section = file.section_by_name(".debug_info").expect("the debug section");
2089 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
2090 assert_eq!(
2091 kinds,
2092 [
2093 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
2094 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
2095 ]
2096 );
2097 }
2098
2099 fn variable(name: &str, place: Place) -> Object {
2101 Object {
2102 name: name.to_owned(),
2103 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
2104 size: 4,
2105 align: 4,
2106 place,
2107 binding: Binding::Global,
2108 visibility: Visibility::Default,
2109 relocs: Vec::new(),
2110 }
2111 }
2112
2113 fn measured() -> (Text, Data) {
2115 let mut text = calling("puts");
2116 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
2117 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
2118 let mut table = variable("table", Place::ReadOnly);
2119 table.bytes = vec![0; 8];
2120 table.size = 8;
2121 let apart = |at, to: &str, from: &str| Apart {
2122 object: 0,
2123 at,
2124 to: to.to_owned(),
2125 from: from.to_owned(),
2126 addend: 0,
2127 bytes: 4,
2128 };
2129 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
2130 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
2131 }
2132
2133 #[test]
2134 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
2135 let (text, data) = measured();
2136 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2137 .expect("an object");
2138 let file = object::File::parse(&bytes[..]).expect("a readable object");
2139 let section = file.section_by_name(".rodata").expect("a read only section");
2140 assert_eq!(section.relocations().count(), 0);
2141 let image = section.data().expect("the image");
2142 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
2143 }
2144
2145 #[test]
2146 fn a_distance_between_labels_in_two_sections_is_refused() {
2147 let (mut text, data) = measured();
2150 text.bytes.resize(22, 0x90);
2151 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
2152 text.labels[1].at = 17;
2153 let output =
2154 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
2155 let refused = write(&text, &data, &[], &target(), output, &Info::default());
2156 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
2157 }
2158
2159 fn holding(object: Object) -> Vec<u8> {
2161 let data = Data {
2162 apart: Vec::new(),
2163 exports: Vec::new(),
2164 weak: Vec::new(),
2165 objects: vec![object],
2166 };
2167 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2168 .expect("an object")
2169 }
2170
2171 #[test]
2172 fn what_a_variable_is_decides_which_section_it_goes_in() {
2173 for (place, wanted) in [
2174 (Place::Written, ".data"),
2175 (Place::ReadOnly, ".rodata"),
2176 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
2177 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
2178 (Place::Zero, ".bss"),
2179 (Place::Thread { zero: false }, ".tdata"),
2180 (Place::Thread { zero: true }, ".tbss"),
2181 (Place::Named(".init_array".to_owned(), Holds::Written), ".init_array"),
2182 ] {
2183 let bytes = holding(variable("x", place.clone()));
2184 let file = object::File::parse(&bytes[..]).expect("a readable object");
2185 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
2186 assert_eq!(section.size(), 4, "{place:?}");
2187 let carried = section.data().expect("the bytes").len();
2190 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2191 }
2192 }
2193
2194 #[test]
2200 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
2201 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
2202 let bytes = holding(variable("counter", place.clone()));
2203 let file = object::File::parse(&bytes[..]).expect("a readable object");
2204 let symbol = file
2205 .symbols()
2206 .find(|symbol| symbol.name() == Ok("counter"))
2207 .unwrap_or_else(|| panic!("{place:?}"));
2208 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
2209 }
2210 }
2211
2212 #[test]
2218 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
2219 for (name, wanted) in [
2220 (".init_array", elf::SHT_INIT_ARRAY),
2221 (".init_array.00101", elf::SHT_INIT_ARRAY),
2222 (".fini_array", elf::SHT_FINI_ARRAY),
2223 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2224 (".init_arrays", elf::SHT_PROGBITS),
2225 ] {
2226 let bytes = holding(variable("x", Place::Named(name.to_owned(), Holds::Written)));
2227 let file = object::File::parse(&bytes[..]).expect("a readable object");
2228 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2229 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2230 panic!("{name} is not an elf section");
2231 };
2232 assert_eq!(sh_type, wanted, "{name}");
2233 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2234 }
2235 }
2236
2237 #[test]
2241 fn a_named_section_carries_the_flags_of_what_is_in_it() {
2242 for (name, holds, kind, flags) in [
2243 (".mine", Holds::Written, elf::SHT_PROGBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2244 (".roz", Holds::ReadOnly, elf::SHT_PROGBITS, elf::SHF_ALLOC),
2245 (".bss..page_aligned", Holds::Zero, elf::SHT_NOBITS, elf::SHF_ALLOC | elf::SHF_WRITE),
2246 ] {
2247 let bytes = holding(variable("x", Place::Named(name.to_owned(), holds)));
2248 let file = object::File::parse(&bytes[..]).expect("a readable object");
2249 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2250 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2251 panic!("{name} is not an elf section");
2252 };
2253 assert_eq!((sh_type, sh_flags), (kind, flags), "{name}");
2254 assert_eq!(section.size(), 4, "{name}");
2255 }
2256 }
2257
2258 #[test]
2264 fn two_variables_in_one_named_section_share_it() {
2265 let objects = vec![
2266 variable("x", Place::Named(".init_array".to_owned(), Holds::Written)),
2267 variable("y", Place::Named(".init_array".to_owned(), Holds::Written)),
2268 ];
2269 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2270 let bytes =
2271 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2272 .expect("an object");
2273 let file = object::File::parse(&bytes[..]).expect("a readable object");
2274 let named: Vec<_> =
2275 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2276 assert_eq!(named.len(), 1);
2277 assert_eq!(named[0].size(), 8);
2278 }
2279
2280 #[test]
2284 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2285 let sections =
2286 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2287 for (place, wanted) in [
2288 (Place::Written, ".data.x"),
2289 (Place::ReadOnly, ".rodata.x"),
2290 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2291 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2292 (Place::Zero, ".bss.x"),
2293 (Place::Thread { zero: false }, ".tdata.x"),
2294 (Place::Thread { zero: true }, ".tbss.x"),
2295 ] {
2296 let data = Data {
2297 apart: Vec::new(),
2298 exports: Vec::new(),
2299 weak: Vec::new(),
2300 objects: vec![variable("x", place.clone())],
2301 };
2302 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2303 .expect("object");
2304 let file = object::File::parse(&bytes[..]).expect("a readable object");
2305 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2306 let section = file.section_by_name(wanted).expect("the section it named");
2307 assert_eq!(section.size(), 4, "{place:?}");
2308 let carried = section.data().expect("the bytes").len();
2311 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2312 }
2313 }
2314
2315 #[test]
2319 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2320 let sections =
2321 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2322 let named = Place::Named(".init_array".to_owned(), Holds::Written);
2323 let objects = vec![variable("m", Place::Merged), variable("n", named)];
2324 let bytes = write(
2325 &Text::default(),
2326 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2327 &[],
2328 &target(),
2329 sections,
2330 &Info::default(),
2331 )
2332 .expect("object");
2333 let file = object::File::parse(&bytes[..]).expect("a readable object");
2334 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2335 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2336 assert_eq!(lives_in(&file, "n"), ".init_array");
2337 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2338 }
2339
2340 #[test]
2344 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2345 let sections =
2346 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2347 let pointer = Object {
2348 bytes: vec![0; 8],
2349 size: 8,
2350 align: 8,
2351 relocs: vec![Reloc {
2352 at: 0,
2353 symbol: "y".to_owned(),
2354 kind: Reference::Address { bytes: 8 },
2355 addend: 0,
2356 after: 0,
2357 }],
2358 ..variable("p", Place::Written)
2359 };
2360 let objects = vec![variable("first", Place::Written), pointer];
2361 let bytes = write(
2362 &Text::default(),
2363 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2364 &[],
2365 &target(),
2366 sections,
2367 &Info::default(),
2368 )
2369 .expect("object");
2370 let file = object::File::parse(&bytes[..]).expect("a readable object");
2371 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2372 let (offset, reloc) = section.relocations().next().expect("one relocation");
2373 assert_eq!(offset, 0);
2376 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2377 }
2378
2379 #[test]
2387 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2388 let place = Place::RelocReadOnly { local: true };
2389 let data = Data {
2390 apart: Vec::new(),
2391 exports: Vec::new(),
2392 weak: Vec::new(),
2393 objects: vec![variable("first", place.clone()), variable("second", place)],
2394 };
2395 let bytes =
2396 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2397 .expect("an object");
2398 let file = object::File::parse(&bytes[..]).expect("a readable object");
2399 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2400 assert_eq!(named, 1, "one section holding both, not one each");
2401 }
2402
2403 #[test]
2404 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2405 let mut data = Data {
2406 apart: Vec::new(),
2407 exports: Vec::new(),
2408 weak: Vec::new(),
2409 objects: vec![variable("first", Place::Written)],
2410 };
2411 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2412 let bytes =
2413 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2414 .expect("an object");
2415 let file = object::File::parse(&bytes[..]).expect("a readable object");
2416 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2417 assert_eq!(second.kind(), SymbolKind::Data);
2418 assert_eq!(second.size(), 4);
2419 assert_eq!(second.address(), 16);
2423 }
2424
2425 #[test]
2426 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2427 for (binding, global, weak) in [
2428 (Binding::Global, true, false),
2429 (Binding::Local, false, false),
2430 (Binding::Weak, true, true),
2431 ] {
2432 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2433 let file = object::File::parse(&bytes[..]).expect("a readable object");
2434 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2435 assert_eq!(x.is_global(), global, "{binding:?}");
2436 assert_eq!(x.is_weak(), weak, "{binding:?}");
2437 }
2438 }
2439
2440 #[test]
2441 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2442 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2443 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2444 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2445 assert!(x.is_common(), "the linker merges every definition of this name into one");
2446 assert_eq!(x.size(), 4);
2447 assert_eq!(x.address(), 0);
2451 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2452 }
2453
2454 #[test]
2455 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2456 let object = Object {
2457 bytes: vec![0; 8],
2458 size: 8,
2459 align: 8,
2460 relocs: vec![Reloc {
2461 at: 0,
2462 symbol: "y".to_owned(),
2463 kind: Reference::Address { bytes: 8 },
2464 addend: 16,
2465 after: 0,
2466 }],
2467 ..variable("p", Place::Written)
2468 };
2469 let bytes = holding(object);
2470 let file = object::File::parse(&bytes[..]).expect("a readable object");
2471 let section = file.section_by_name(".data").expect("a data section");
2472 let (offset, reloc) = section.relocations().next().expect("one relocation");
2473 assert_eq!(offset, 0);
2474 assert_eq!(reloc.addend(), 16);
2475 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2476 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2477 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2478 }
2479
2480 #[test]
2487 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2488 let mut text = Text::default();
2489 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2490 text.bytes.resize(8, 0x90);
2491 text.relocs.push(Reloc {
2492 at: 1,
2493 symbol: "hook".to_owned(),
2494 kind: Reference::Call,
2495 addend: -4,
2496 after: 0,
2497 });
2498 let data = Data {
2499 apart: Vec::new(),
2500 exports: Vec::new(),
2501 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2502 objects: vec![],
2503 };
2504 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2505 .expect("an object");
2506 let file = object::File::parse(&bytes[..]).expect("a readable object");
2507
2508 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2509 assert!(hook.is_undefined(), "nothing here defines it");
2510 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2511
2512 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2516 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2517 }
2518
2519 #[test]
2534 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2535 let mut text = Text::default();
2536 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2537 text.bytes.resize(16, 0x90);
2538 text.relocs.push(Reloc {
2539 at: 3,
2540 symbol: "flags".to_owned(),
2541 kind: Reference::Thread,
2542 addend: -4,
2543 after: 0,
2544 });
2545 text.relocs.push(Reloc {
2548 at: 10,
2549 symbol: "shared".to_owned(),
2550 kind: Reference::Got,
2551 addend: -4,
2552 after: 0,
2553 });
2554 let data =
2555 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2556 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2557 .expect("an object");
2558 let file = object::File::parse(&bytes[..]).expect("a readable object");
2559
2560 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2561 assert!(flags.is_undefined(), "nothing here defines it");
2562 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2563
2564 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2565 assert!(shared.is_undefined(), "nothing here defines this one either");
2566 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2567 }
2568
2569 #[test]
2571 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2572 let mut data = Data {
2573 apart: Vec::new(),
2574 exports: Vec::new(),
2575 weak: Vec::new(),
2576 objects: vec![variable("first", Place::Written)],
2577 };
2578 data.objects.push(Object {
2579 bytes: vec![0; 16],
2580 size: 16,
2581 align: 8,
2582 relocs: vec![Reloc {
2583 at: 8,
2584 symbol: "y".to_owned(),
2585 kind: Reference::Address { bytes: 8 },
2586 addend: 0,
2587 after: 0,
2588 }],
2589 ..variable("second", Place::Written)
2590 });
2591 let bytes =
2592 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2593 .expect("an object");
2594 let file = object::File::parse(&bytes[..]).expect("a readable object");
2595 let section = file.section_by_name(".data").expect("a data section");
2596 let (offset, _) = section.relocations().next().expect("one relocation");
2597 assert_eq!(offset, 16);
2600 }
2601
2602 #[test]
2603 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2604 let data = Data {
2605 apart: Vec::new(),
2606 exports: Vec::new(),
2607 weak: Vec::new(),
2608 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2609 };
2610 let aliases = [Alias {
2611 name: "b".to_owned(),
2612 target: "a".to_owned(),
2613 binding: Binding::Global,
2614 visibility: Visibility::Default,
2615 ifunc: false,
2616 }];
2617 let bytes = write(
2618 &Text::default(),
2619 &data,
2620 &aliases,
2621 &target(),
2622 Output::default(),
2623 &Info::default(),
2624 )
2625 .expect("an object");
2626 let file = object::File::parse(&bytes[..]).expect("a readable object");
2627 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2628 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2629 assert_eq!(b.address(), a.address(), "the same place");
2630 assert_eq!(b.size(), a.size());
2631 assert_eq!(b.section_index(), a.section_index());
2632 assert!(a.is_local(), "the target was written `static`");
2635 assert!(b.is_global(), "and the name given to it was not");
2636 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2638 }
2639
2640 #[test]
2641 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2642 let text = calling("puts");
2643 let aliases = [Alias {
2644 name: "g".to_owned(),
2645 target: "f".to_owned(),
2646 binding: Binding::Weak,
2647 visibility: Visibility::Default,
2648 ifunc: false,
2649 }];
2650 let bytes = write(
2651 &text,
2652 &Data::default(),
2653 &aliases,
2654 &target(),
2655 Output::default(),
2656 &Info::default(),
2657 )
2658 .expect("an object");
2659 let file = object::File::parse(&bytes[..]).expect("a readable object");
2660 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2661 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2662 assert_eq!(g.address(), f.address());
2663 assert_eq!(g.size(), f.size());
2664 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2665 assert!(g.is_weak(), "so that a program may define the name itself instead");
2666 }
2667
2668 #[test]
2672 fn an_ifunc_is_a_symbol_of_its_own_type_at_the_resolver() {
2673 let text = calling("puts");
2674 for (binding, bind) in [
2675 (Binding::Global, elf::STB_GLOBAL),
2676 (Binding::Weak, elf::STB_WEAK),
2677 (Binding::Local, elf::STB_LOCAL),
2678 ] {
2679 let aliases = [Alias {
2680 name: "g".to_owned(),
2681 target: "f".to_owned(),
2682 binding,
2683 visibility: Visibility::Default,
2684 ifunc: true,
2685 }];
2686 let bytes = write(
2687 &text,
2688 &Data::default(),
2689 &aliases,
2690 &target(),
2691 Output::default(),
2692 &Info::default(),
2693 )
2694 .expect("an object");
2695 let file = object::File::parse(&bytes[..]).expect("a readable object");
2696 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the resolver");
2697 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the ifunc");
2698 assert_eq!((g.address(), g.section_index()), (f.address(), f.section_index()));
2699 let SymbolFlags::Elf { st_info, .. } = g.flags() else {
2700 panic!("an ELF symbol");
2701 };
2702 assert_eq!(st_info, bind | elf::STT_GNU_IFUNC, "{binding:?}");
2703 let object::File::Elf64(elf) = &file else { panic!("a 64 bit ELF file") };
2704 let os_abi = elf.elf_header().e_ident.os_abi;
2705 assert_eq!(os_abi, elf::ELFOSABI_GNU, "gas marks a file with an ifunc in it as GNU");
2706 }
2707 }
2708
2709 #[test]
2712 fn an_ifunc_is_refused_on_a_format_without_the_type() {
2713 let aliases = [Alias {
2714 name: "g".to_owned(),
2715 target: "f".to_owned(),
2716 binding: Binding::Global,
2717 visibility: Visibility::Default,
2718 ifunc: true,
2719 }];
2720 let error = write(
2721 &calling("puts"),
2722 &Data::default(),
2723 &aliases,
2724 &windows(),
2725 Output::default(),
2726 &Info::default(),
2727 )
2728 .expect_err("no ifunc on COFF");
2729 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2730 }
2731
2732 #[test]
2735 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2736 let aliases = [Alias {
2737 name: "b".to_owned(),
2738 target: "a".to_owned(),
2739 binding: Binding::Global,
2740 visibility: Visibility::Default,
2741 ifunc: false,
2742 }];
2743 let error = write(
2744 &Text::default(),
2745 &Data::default(),
2746 &aliases,
2747 &target(),
2748 Output::default(),
2749 &Info::default(),
2750 )
2751 .expect_err("nothing to point at");
2752 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2753 }
2754
2755 #[test]
2756 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2757 let text = calling("puts");
2758 for triple in [
2759 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2760 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2761 ] {
2762 let error = write(
2763 &text,
2764 &Data::default(),
2765 &[],
2766 &TargetInfo::new(triple),
2767 Output::default(),
2768 &Info::default(),
2769 )
2770 .expect_err("no writer");
2771 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2772 }
2773 }
2774
2775 #[test]
2781 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2782 let mut text = calling("puts");
2783 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2784 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2785 text.bytes.resize(33, 0x90);
2786 let data = Data {
2787 apart: Vec::new(),
2788 exports: Vec::new(),
2789 weak: Vec::new(),
2790 objects: vec![variable("seen", Place::Written), {
2791 let mut quiet = variable("quiet", Place::Zero);
2792 quiet.binding = Binding::Local;
2793 quiet
2794 }],
2795 };
2796 let aliases = [Alias {
2797 name: "second".to_owned(),
2798 target: "f".to_owned(),
2799 binding: Binding::Global,
2800 visibility: Visibility::Default,
2801 ifunc: false,
2802 }];
2803
2804 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2805 assert_eq!(names, ["f", "shared", "seen", "second"]);
2806
2807 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2808 .expect("an object");
2809 let file = object::File::parse(&bytes[..]).expect("a readable object");
2810 let found: Vec<String> = file
2811 .symbols()
2812 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2813 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2814 .collect();
2815 let mut sorted = names.clone();
2816 sorted.sort();
2817 let mut theirs = found;
2818 theirs.sort();
2819 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2820 }
2821
2822 fn windows() -> TargetInfo {
2824 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2825 }
2826
2827 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2829 let file = object::File::parse(bytes).expect("a readable object");
2830 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2831 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2832 }
2833
2834 #[test]
2835 fn a_windows_target_is_written_rather_than_refused() {
2836 let text = calling("puts");
2837 let bytes =
2838 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2839 .expect("an object");
2840 let file = object::File::parse(&bytes[..]).expect("a readable object");
2841 assert_eq!(file.format(), BinaryFormat::Coff);
2842 let section = file.section_by_name(".text").expect("a text section");
2843 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2844 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2845 assert!(names.contains(&"f"), "{names:?}");
2846 assert!(names.contains(&"puts"), "{names:?}");
2847 }
2848
2849 #[test]
2857 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2858 for (after, typ) in [
2859 (0, pe::IMAGE_REL_AMD64_REL32),
2860 (1, pe::IMAGE_REL_AMD64_REL32_1),
2861 (4, pe::IMAGE_REL_AMD64_REL32_4),
2862 (5, pe::IMAGE_REL_AMD64_REL32_5),
2863 ] {
2864 let mut text = calling("puts");
2865 text.relocs[0].addend = -4 - i64::from(after);
2868 text.relocs[0].after = after;
2869 text.bytes.resize(6 + after as usize, 0x90);
2870 text.funcs[0].len = text.bytes.len();
2871 let bytes = write(
2872 &text,
2873 &Data::default(),
2874 &[],
2875 &windows(),
2876 Output::default(),
2877 &Info::default(),
2878 )
2879 .expect("an object");
2880 let file = object::File::parse(&bytes[..]).expect("a readable object");
2881 let section = file.section_by_name(".text").expect("a text section");
2882 let (_, reloc) = section.relocations().next().expect("the relocation");
2883 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2884 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2887 }
2888 }
2889
2890 #[test]
2893 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2894 let mut text = calling("puts");
2895 text.relocs[0].addend = 12;
2896 let bytes =
2897 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2898 .expect("an object");
2899 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2900 }
2901
2902 #[test]
2903 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2904 let object = Object {
2905 bytes: vec![0; 8],
2906 size: 8,
2907 align: 8,
2908 relocs: vec![Reloc {
2909 at: 0,
2910 symbol: "y".to_owned(),
2911 kind: Reference::Address { bytes: 8 },
2912 addend: 0,
2913 after: 0,
2914 }],
2915 ..variable("p", Place::Written)
2916 };
2917 let data = Data {
2918 apart: Vec::new(),
2919 exports: Vec::new(),
2920 weak: Vec::new(),
2921 objects: vec![object],
2922 };
2923 let bytes =
2924 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2925 .expect("an object");
2926 let file = object::File::parse(&bytes[..]).expect("a readable object");
2927 let section = file.section_by_name(".data").expect("a data section");
2928 let (_, reloc) = section.relocations().next().expect("the relocation");
2929 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2930 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2931 }
2932
2933 #[test]
2937 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
2938 let pointer = Object {
2939 bytes: vec![0; 8],
2940 size: 8,
2941 align: 8,
2942 relocs: vec![Reloc {
2943 at: 0,
2944 symbol: "environ".to_owned(),
2945 kind: Reference::Address { bytes: 8 },
2946 addend: 0,
2947 after: 0,
2948 }],
2949 ..variable(".refptr.environ", Place::Pointer)
2950 };
2951 let data = Data { objects: vec![pointer], ..Data::default() };
2952 let bytes =
2953 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2954 .expect("an object");
2955 let file = object::File::parse(&bytes[..]).expect("a readable object");
2956 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
2957 let SectionFlags::Coff { characteristics } = section.flags() else {
2958 panic!("a COFF section has COFF flags");
2959 };
2960 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
2961 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
2963 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
2964 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
2965 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
2966 assert_eq!(comdat.kind(), ComdatKind::Any);
2967 assert_eq!(comdat.name(), Ok(".refptr.environ"));
2968 let (_, reloc) = section.relocations().next().expect("the address it holds");
2969 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
2970 }
2971
2972 #[test]
2976 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
2977 let exports = vec![
2978 Export { name: "offered".to_owned(), kind: Offer::Function },
2979 Export { name: "count".to_owned(), kind: Offer::Variable },
2980 Export { name: "kept".to_owned(), kind: Offer::Hidden },
2981 ];
2982 let data = Data { exports, ..Data::default() };
2983 let bytes =
2984 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2985 .expect("an object");
2986 let file = object::File::parse(&bytes[..]).expect("a readable object");
2987 let section = file.section_by_name(".drectve").expect("the options section");
2988 assert_eq!(
2989 section.data().expect("the options"),
2990 b" -export:offered -export:count,data -exclude-symbols:kept"
2991 );
2992 let SectionFlags::Coff { characteristics } = section.flags() else {
2993 panic!("a COFF section has COFF flags");
2994 };
2995 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
2996 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
2997
2998 let none = write(
2999 &Text::default(),
3000 &Data::default(),
3001 &[],
3002 &windows(),
3003 Output::default(),
3004 &Info::default(),
3005 )
3006 .expect("an object");
3007 let file = object::File::parse(&none[..]).expect("a readable object");
3008 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
3009 }
3010
3011 #[test]
3014 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
3015 for local in [false, true] {
3016 let data = Data {
3017 apart: Vec::new(),
3018 exports: Vec::new(),
3019 weak: Vec::new(),
3020 objects: vec![variable("p", Place::RelocReadOnly { local })],
3021 };
3022 let bytes = write(
3023 &Text::default(),
3024 &data,
3025 &[],
3026 &windows(),
3027 Output::default(),
3028 &Info::default(),
3029 )
3030 .expect("an object");
3031 let file = object::File::parse(&bytes[..]).expect("a readable object");
3032 assert!(file.section_by_name(".rdata").is_some(), "{local}");
3033 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
3034 }
3035 }
3036
3037 #[test]
3040 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
3041 let text = calling("puts");
3042 let output = Output { property: Property { features: 3 }, ..Output::default() };
3043 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
3044 .expect("an object");
3045 let file = object::File::parse(&bytes[..]).expect("a readable object");
3046 assert!(file.section_by_name(".note.GNU-stack").is_none());
3047 assert!(file.section_by_name(".note.gnu.property").is_none());
3048 }
3049
3050 #[test]
3055 fn what_this_format_cannot_say_is_refused_by_name() {
3056 let ordinary = Text::default();
3057 let empty = Data::default();
3058
3059 let mut thread = Data::default();
3060 thread.objects.push(variable("t", Place::Thread { zero: true }));
3061
3062 let mut gathered = Data::default();
3063 gathered
3064 .objects
3065 .push(variable("c", Place::Named(".init_array".to_owned(), Holds::Written)));
3066
3067 let mut table = calling("puts");
3068 table.relocs[0].kind = Reference::Got;
3069
3070 let mut room = calling("puts");
3071 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
3072
3073 let cases: [(&str, &Text, &Data); 4] = [
3074 ("thread-local", &ordinary, &thread),
3075 ("startup", &ordinary, &gathered),
3076 ("table", &table, &empty),
3077 ("patcher", &room, &empty),
3078 ];
3079 for (what, text, data) in cases {
3080 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
3081 .expect_err("something this format cannot write");
3082 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
3083 }
3084 }
3085
3086 #[test]
3089 fn a_thread_local_variable_goes_in_the_tls_section() {
3090 let mut thread = Data::default();
3091 thread.objects.push(variable("t", Place::Thread { zero: false }));
3092 let bytes =
3093 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
3094 .expect("an object");
3095 let file = object::File::parse(&bytes[..]).expect("a readable object");
3096 assert!(file.section_by_name(".tls$").is_some());
3097 }
3098
3099 #[test]
3103 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
3104 let mut text = calling("puts");
3105 text.funcs[0].visibility = Visibility::Hidden;
3106 let bytes =
3107 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
3108 .expect("an object");
3109 let file = object::File::parse(&bytes[..]).expect("a readable object");
3110 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
3111 assert!(symbol.is_global(), "a name others may use either way");
3112 }
3113
3114 #[test]
3115 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
3116 let text = calling("puts");
3117 let data = Data {
3118 apart: Vec::new(),
3119 exports: Vec::new(),
3120 weak: Vec::new(),
3121 objects: vec![variable("shared", Place::Written)],
3122 };
3123 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
3124 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
3125 }
3126
3127 #[test]
3131 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
3132 let text = calling("puts");
3133 for triple in [
3134 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
3135 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
3136 ] {
3137 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
3138 .expect_err("no writer");
3139 assert!(matches!(error, Error::Format { .. }), "{error:?}");
3140 }
3141 }
3142
3143 fn i386() -> TargetInfo {
3145 TargetInfo::new(Triple::new(Arch::X86, Os::Linux, Env::Gnu))
3146 }
3147
3148 #[test]
3155 fn a_compilation_for_i386_is_32_bit_elf_with_rel_relocations() {
3156 let mut text = calling("puts");
3157 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
3158 text.funcs[0].start = 3;
3159 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
3160 text.relocs[0].at = 4;
3161 let data = Data {
3162 objects: vec![Object {
3163 name: "p".to_owned(),
3164 bytes: vec![0; 4],
3165 size: 4,
3166 align: 4,
3167 place: Place::Written,
3168 binding: Binding::Global,
3169 visibility: Visibility::Default,
3170 relocs: vec![Reloc {
3171 at: 0,
3172 symbol: "x".to_owned(),
3173 kind: Reference::Address { bytes: 4 },
3174 addend: 12,
3175 after: 0,
3176 }],
3177 }],
3178 ..Data::default()
3179 };
3180 let property = Property { features: Property::IBT | Property::SHSTK };
3181 let output = Output { property, ..Output::default() };
3182 let bytes = write(&text, &data, &[], &i386(), output, &Info::default()).expect("an object");
3183 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3184 assert_eq!(file.architecture(), Architecture::I386);
3185 assert_eq!(file.elf_header().e_machine.get(Endianness::Little), elf::EM_386);
3186 assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
3187
3188 let code = file.section_by_name(".text").expect("a text section");
3190 let [(at, reloc)] = &code.relocations().collect::<Vec<_>>()[..] else {
3191 panic!("one relocation in the text")
3192 };
3193 assert_eq!(*at, 4);
3194 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_PLT32 });
3195 assert!(reloc.has_implicit_addend());
3196 assert_eq!(&code.data().expect("the bytes")[4..8], &(-4i32).to_le_bytes());
3197
3198 let variable = file.section_by_name(".data").expect("a data section");
3200 let [(at, reloc)] = &variable.relocations().collect::<Vec<_>>()[..] else {
3201 panic!("one relocation in the data")
3202 };
3203 assert_eq!(*at, 0);
3204 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3205 assert_eq!(variable.data().expect("the bytes"), &12u32.to_le_bytes());
3206
3207 let record = file.section_by_name(PATCHABLE).expect("a record of the room");
3209 assert_eq!(record.size(), 4);
3210 assert_eq!(record.align(), 4);
3211 let index = code.index().0;
3212 assert_eq!(record.elf_section_header().sh_link.get(Endianness::Little) as usize, index);
3213 let [(_, reloc)] = &record.relocations().collect::<Vec<_>>()[..] else {
3214 panic!("one address in the record")
3215 };
3216 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_386_32 });
3217
3218 let note = file.section_by_name(".note.gnu.property").expect("the note");
3220 assert_eq!(note.align(), 4);
3221 let want: Vec<u8> = [
3222 4u32,
3223 12,
3224 5,
3225 u32::from_le_bytes(*b"GNU\0"),
3226 Property::X86_FEATURES,
3227 4,
3228 Property::IBT | Property::SHSTK,
3229 ]
3230 .iter()
3231 .flat_map(|word| word.to_le_bytes())
3232 .collect();
3233 assert_eq!(note.data().expect("the bytes"), &want[..]);
3234 }
3235
3236 #[test]
3239 fn an_i386_debug_section_keeps_its_addends_in_the_bytes_under_gz() {
3240 let info = Info {
3241 chunks: vec![Chunk {
3242 name: ".debug_info".to_owned(),
3243 bytes: vec![0; 64],
3244 relocs: vec![Reloc {
3245 at: 8,
3246 symbol: "f".to_owned(),
3247 kind: Reference::Address { bytes: 4 },
3248 addend: 7,
3249 after: 0,
3250 }],
3251 }],
3252 compress: Compress::Zlib,
3253 };
3254 let bytes =
3255 write(&calling("puts"), &Data::default(), &[], &i386(), Output::default(), &info)
3256 .expect("an object");
3257 let file = object::read::elf::ElfFile32::<Endianness>::parse(&bytes[..]).expect("readable");
3258 let section = file.section_by_name(".debug_info").expect("the debug section");
3259 let packed =
3260 SectionFlags::Elf { sh_type: elf::SHT_PROGBITS, sh_flags: elf::SHF_COMPRESSED };
3261 assert_ne!(section.flags(), packed);
3262 let data = section.data().expect("the bytes");
3263 assert_eq!(data.len(), 64);
3264 assert_eq!(&data[8..12], &7u32.to_le_bytes());
3265 }
3266
3267 fn i386_windows() -> TargetInfo {
3269 TargetInfo::new(Triple::new(Arch::X86, Os::Windows, Env::Gnu))
3270 }
3271
3272 #[test]
3281 fn a_compilation_for_i386_windows_is_coff_with_decorated_names() {
3282 let mut text = calling("puts");
3283 text.bytes.extend([0xe8, 0, 0, 0, 0, 0xc3]);
3284 text.funcs.push(extent("@fast@8".to_owned(), 6, 6, Binding::Global));
3285 text.relocs.push(Reloc {
3286 at: 7,
3287 symbol: "__imp_GetTickCount".to_owned(),
3288 kind: Reference::Call,
3289 addend: -4,
3290 after: 0,
3291 });
3292 let data = Data {
3293 objects: vec![Object {
3294 name: "p".to_owned(),
3295 bytes: vec![0; 12],
3296 size: 12,
3297 align: 4,
3298 place: Place::Written,
3299 binding: Binding::Global,
3300 visibility: Visibility::Default,
3301 relocs: vec![
3302 Reloc {
3303 at: 0,
3304 symbol: "x".to_owned(),
3305 kind: Reference::Address { bytes: 4 },
3306 addend: 12,
3307 after: 0,
3308 },
3309 Reloc {
3310 at: 4,
3311 symbol: "f".to_owned(),
3312 kind: Reference::Image,
3313 addend: 0,
3314 after: 0,
3315 },
3316 Reloc {
3317 at: 8,
3318 symbol: "x".to_owned(),
3319 kind: Reference::Away,
3320 addend: 0,
3321 after: 0,
3322 },
3323 ],
3324 }],
3325 ..Data::default()
3326 };
3327 let aliases = [Alias {
3328 name: "g".to_owned(),
3329 target: "f".to_owned(),
3330 binding: Binding::Global,
3331 visibility: Visibility::Default,
3332 ifunc: false,
3333 }];
3334 let target = i386_windows();
3335 let bytes = write(&text, &data, &aliases, &target, Output::default(), &Info::default())
3336 .expect("an object");
3337 let file = object::File::parse(&bytes[..]).expect("a readable object");
3338 assert_eq!(file.format(), BinaryFormat::Coff);
3339 assert_eq!(file.architecture(), Architecture::I386);
3340 assert!(!file.is_64());
3341
3342 let named = |name: &str| file.symbol_by_name(name).is_some();
3343 for name in ["_f", "@fast@8", "_p", "_g", "_puts", "__imp__GetTickCount", "_x"] {
3344 assert!(named(name), "{name}");
3345 }
3346 for name in ["f", "p", "puts", "_@fast@8", "___imp_GetTickCount"] {
3347 assert!(!named(name), "{name}");
3348 }
3349
3350 let code = file.section_by_name(".text").expect("a text section");
3351 let relocs: Vec<_> = code.relocations().collect();
3352 assert_eq!(relocs.len(), 2);
3353 for (at, reloc) in &relocs {
3354 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_REL32 });
3355 let at = *at as usize;
3356 assert_eq!(&code.data().expect("the bytes")[at..at + 4], &0i32.to_le_bytes());
3357 }
3358
3359 let variable = file.section_by_name(".data").expect("a data section");
3360 let types: Vec<_> = variable
3361 .relocations()
3362 .map(|(at, reloc)| match reloc.flags() {
3363 RelocationFlags::Coff { typ } => (at, typ),
3364 flags => panic!("{flags:?}"),
3365 })
3366 .collect();
3367 assert_eq!(
3368 types,
3369 [
3370 (0, pe::IMAGE_REL_I386_DIR32),
3371 (4, pe::IMAGE_REL_I386_DIR32NB),
3372 (8, pe::IMAGE_REL_I386_REL32)
3373 ]
3374 );
3375 let image = variable.data().expect("the bytes");
3378 assert_eq!(&image[0..4], &12u32.to_le_bytes());
3379 assert_eq!(&image[8..12], &4u32.to_le_bytes());
3380
3381 let listed = defines(&text, &data, &aliases, &target).expect("a list");
3383 assert_eq!(listed, ["_f", "@fast@8", "_p", "_g"]);
3384 }
3385
3386 #[test]
3389 fn an_i386_windows_object_has_no_unwind_table() {
3390 let mut text = calling("puts");
3391 text.unwind.bytes = vec![0; 12];
3392 let bytes = write(
3393 &text,
3394 &Data::default(),
3395 &[],
3396 &i386_windows(),
3397 Output::default(),
3398 &Info::default(),
3399 )
3400 .expect("an object");
3401 let file = object::File::parse(&bytes[..]).expect("a readable object");
3402 assert!(file.section_by_name(".pdata").is_none());
3403 assert!(file.section_by_name(".xdata").is_none());
3404 assert!(file.section_by_name(".eh_frame").is_none());
3405 }
3406
3407 #[test]
3410 fn i386_windows_has_no_eight_byte_or_table_relocations() {
3411 for kind in [
3412 Reference::Address { bytes: 8 },
3413 Reference::AwayWide,
3414 Reference::Got,
3415 Reference::GotOffset,
3416 Reference::Slot,
3417 Reference::Thread,
3418 ] {
3419 assert_eq!(Flavour::Coff.reloc(Architecture::I386, kind, 0), None, "{kind:?}");
3420 }
3421 assert_eq!(
3422 Flavour::Coff.reloc(Architecture::I386, Reference::Section, 0),
3423 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_SECREL })
3424 );
3425 assert_eq!(
3426 Flavour::Coff.reloc(Architecture::I386, Reference::Signed, 0),
3427 Some(RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 }),
3428 "an address an instruction holds"
3429 );
3430 }
3431}