1use std::collections::{BTreeMap, HashMap, HashSet};
34
35use object::write::{
36 Comdat, Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
37};
38use object::{
39 Architecture, BinaryFormat, ComdatKind, Endianness, RelocationFlags, SectionFlags, SectionKind,
40 SymbolFlags, SymbolKind, SymbolScope,
41};
42use rucc_target::{ObjectFormat, TargetInfo};
43use rucc_tuple::Arch;
44
45use crate::section::{
46 Alias, Apart, Array, Binding, Data, EXCEPT_TABLE, Export, Info, Object, Output, Place,
47 Property, Reference, Reloc, Sections, Text, Visibility,
48};
49use crate::{coff, elf};
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub(crate) enum Flavour {
59 Elf,
61 Coff,
63 MachO,
66}
67
68impl Flavour {
69 pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
71 match target.object_format {
72 ObjectFormat::Elf => Some(Flavour::Elf),
73 ObjectFormat::Coff => Some(Flavour::Coff),
74 ObjectFormat::MachO | ObjectFormat::Wasm => None,
75 }
76 }
77
78 pub(crate) fn binary(self) -> BinaryFormat {
80 match self {
81 Flavour::Elf => BinaryFormat::Elf,
82 Flavour::Coff => BinaryFormat::Coff,
83 Flavour::MachO => BinaryFormat::MachO,
84 }
85 }
86
87 pub(crate) fn reloc(self, reference: Reference, after: u8) -> Option<RelocationFlags> {
92 match self {
93 Flavour::Elf => elf::r_type(reference).map(|r_type| RelocationFlags::Elf { r_type }),
94 Flavour::Coff => coff::reloc(reference, after),
95 Flavour::MachO => crate::macho::reloc(reference, 0).ok(),
96 }
97 }
98
99 pub(crate) fn see(
105 self,
106 obj: &mut Writer<'_>,
107 id: SymbolId,
108 binding: Binding,
109 visibility: Visibility,
110 ) {
111 match self {
112 Flavour::Elf => elf::see(obj, id, binding, visibility),
113 Flavour::Coff => {}
114 Flavour::MachO => {
117 if binding != Binding::Local && visibility == Visibility::Hidden {
118 obj.symbol_mut(id).scope = SymbolScope::Linkage;
119 }
120 }
121 }
122 }
123
124 fn rel_ro_local(self) -> Option<&'static str> {
128 match self {
129 Flavour::Elf => elf::REL_RO_LOCAL,
130 Flavour::Coff => coff::REL_RO_LOCAL,
131 Flavour::MachO => None,
132 }
133 }
134
135 pub(crate) fn debug(self, kind: Reference, into_debug: bool) -> Reference {
147 match kind {
148 Reference::Address { bytes: 4 } if self == Flavour::Coff && into_debug => {
149 Reference::Section
150 }
151 kind => kind,
152 }
153 }
154
155 fn gathered(self, array: Array) -> Option<SectionFlags> {
156 match self {
157 Flavour::Elf => Some(elf::gathered(array)),
158 Flavour::Coff | Flavour::MachO => None,
159 }
160 }
161
162 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
171 match self {
172 Flavour::Elf => {
173 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
174 }
175 Flavour::Coff => (shape.coff != 0).then_some(SectionFlags::Coff {
176 characteristics: object::pe::SectionFlags(shape.coff),
177 }),
178 Flavour::MachO => Some(SectionFlags::MachO {
179 flags: object::macho::SectionFlags(shape.mach),
180 reserved2: 0,
181 }),
182 }
183 }
184
185 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
201 if self == Flavour::MachO {
202 return match sort {
203 crate::source::Sort::Func => SymbolKind::Text,
204 crate::source::Sort::File => SymbolKind::File,
205 _ => SymbolKind::Data,
206 };
207 }
208 match sort {
209 crate::source::Sort::Func => SymbolKind::Text,
210 crate::source::Sort::Object => SymbolKind::Data,
211 crate::source::Sort::Thread => SymbolKind::Tls,
212 crate::source::Sort::File => SymbolKind::File,
213 crate::source::Sort::Untyped => match (self, binding) {
214 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
215 _ => SymbolKind::Label,
216 },
217 }
218 }
219
220 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
222 match self {
223 Flavour::Elf => elf::marker(obj),
224 Flavour::Coff => coff::marker(obj),
225 Flavour::MachO => {}
226 }
227 }
228
229 fn property(self, obj: &mut Writer<'_>, property: Property) {
235 if !property.any() {
236 return;
237 }
238 match self {
239 Flavour::Elf => {
240 let note = obj.section_id(StandardSection::GnuProperty);
241 obj.append_section_data(note, &elf::record(property), 8);
242 }
243 Flavour::Coff | Flavour::MachO => {}
244 }
245 }
246
247 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
251 match self {
252 Flavour::Elf => (elf::FRAMES, None),
253 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
254 Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
255 }
256 }
257
258 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
260 match self {
261 Flavour::Elf => elf::link(bytes, ordered),
262 Flavour::Coff | Flavour::MachO => {
263 debug_assert!(ordered.is_empty(), "a record this format cannot write");
264 }
265 }
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
271pub enum Error {
272 Format {
274 triple: String,
276 },
277 Refused {
279 why: String,
281 },
282}
283
284impl std::fmt::Display for Error {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 match self {
287 Error::Format { triple } => {
288 write!(f, "there is no object writer for {triple} in this compiler yet")
289 }
290 Error::Refused { why } => {
291 write!(f, "the object writer refused what it was given: {why}")
292 }
293 }
294 }
295}
296
297impl std::error::Error for Error {}
298
299pub fn write(
315 text: &Text,
316 data: &Data,
317 aliases: &[Alias],
318 target: &TargetInfo,
319 output: Output,
320 info: &Info,
321) -> Result<Vec<u8>, Error> {
322 let Output { sections, property } = output;
323 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
324 let Some(flavour) = flavour else {
325 return Err(Error::Format { triple: target.tuple.to_string() });
326 };
327 if flavour == Flavour::Coff {
328 beyond(text, data)?;
329 }
330 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
331 let whole = obj.section_id(StandardSection::Text);
335 if !sections.functions {
336 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
337 }
338
339 let mut symbols = BTreeMap::new();
343 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
348 let mut ordered: Vec<String> = Vec::new();
351 for func in &text.funcs {
352 let ahead = func.patch.map_or(0, |patch| patch.before);
362 let (section, at) = if sections.functions {
363 let name = format!(".text.{}", func.name).into_bytes();
364 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
365 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
366 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
367 (id, ahead as u64)
368 } else {
369 (whole, func.start as u64)
370 };
371 if let Some(patch) = func.patch {
386 let base = if sections.functions { func.start - ahead } else { 0 };
387 let name = elf::PATCHABLE.as_bytes().to_vec();
388 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
389 obj.section_mut(id).flags = elf::ordered();
390 obj.append_section_data(id, &[0; 8], 8);
391 let symbol = obj.section_symbol(section);
392 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
393 Error::Refused { why: "no relocation holds an address here".to_owned() }
394 })?;
395 obj.add_relocation(
396 id,
397 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
398 )
399 .map_err(|why| Error::Refused { why: why.to_string() })?;
400 ordered.push(if sections.functions {
401 format!(".text.{}", func.name)
402 } else {
403 ".text".to_owned()
404 });
405 }
406 let id = obj.add_symbol(Symbol {
407 name: func.name.clone().into_bytes(),
408 value: at,
409 size: func.len as u64,
410 kind: SymbolKind::Text,
411 scope: scope_of(func.binding),
412 weak: func.binding == Binding::Weak,
413 section: SymbolSection::Section(section),
414 flags: SymbolFlags::None,
415 });
416 flavour.see(&mut obj, id, func.binding, func.visibility);
417 symbols.insert(func.name.clone(), id);
418 split.push((section, at));
419 }
420
421 for label in &text.labels {
425 let after = text.funcs.partition_point(|func| func.start <= label.at);
426 let Some(index) = after.checked_sub(1) else {
427 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
428 return Err(Error::Refused { why });
429 };
430 let func = &text.funcs[index];
431 let (section, at) = if sections.functions {
432 let base = func.start - func.patch.map_or(0, |patch| patch.before);
435 (split[index].0, (label.at - base) as u64)
436 } else {
437 (whole, label.at as u64)
438 };
439 let id = obj.add_symbol(Symbol {
440 name: label.name.clone().into_bytes(),
441 value: at,
442 size: 0,
445 kind: SymbolKind::Label,
446 scope: SymbolScope::Compilation,
450 weak: false,
451 section: SymbolSection::Section(section),
452 flags: SymbolFlags::None,
453 });
454 symbols.insert(label.name.clone(), id);
455 }
456
457 let mut placed = Vec::with_capacity(data.objects.len());
462 let mut named = HashMap::new();
466 for object in &data.objects {
467 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
468 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
472 obj.section_symbol(section);
473 }
474 let id = obj.add_symbol(Symbol {
475 name: object.name.clone().into_bytes(),
476 value: if object.place == Place::Merged { object.align } else { offset },
479 size: object.size,
480 kind: match object.place {
485 Place::Thread { .. } => SymbolKind::Tls,
486 _ => SymbolKind::Data,
487 },
488 scope: scope_of(object.binding),
489 weak: object.binding == Binding::Weak,
490 section,
491 flags: SymbolFlags::None,
492 });
493 flavour.see(&mut obj, id, object.binding, object.visibility);
494 if let (Place::Pointer, Some(section)) = (&object.place, section.id()) {
498 obj.add_comdat(Comdat { kind: ComdatKind::Any, symbol: id, sections: vec![section] });
499 }
500 symbols.insert(object.name.clone(), id);
501 placed.push((section.id(), offset));
502 }
503
504 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
507
508 for apart in &data.apart {
512 let (Some(section), offset) = placed[apart.object] else { continue };
513 let value = distance(&obj, &symbols, apart)?;
514 let bytes = usize::from(apart.bytes);
515 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
516 let at = at + apart.at;
517 let image = obj.section_mut(section).data_mut();
518 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
519 }
520
521 for alias in aliases {
527 let Some(&id) = symbols.get(&alias.target) else {
528 let why =
529 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
530 return Err(Error::Refused { why });
531 };
532 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
533 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
534 let id = obj.add_symbol(Symbol {
535 name: alias.name.clone().into_bytes(),
536 value,
537 size,
538 kind,
539 scope: scope_of(alias.binding),
540 weak: alias.binding == Binding::Weak,
541 section,
542 flags: SymbolFlags::None,
543 });
544 flavour.see(&mut obj, id, alias.binding, alias.visibility);
545 symbols.insert(alias.name.clone(), id);
546 }
547
548 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
555 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
556 let thread: HashSet<&str> = relocs()
561 .filter(|reloc| reloc.kind == Reference::Thread)
562 .map(|reloc| reloc.symbol.as_str())
563 .collect();
564 let wanted: Vec<&String> =
565 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
566 for name in wanted {
567 if symbols.contains_key(name) || tables.contains_key(name) {
568 continue;
569 }
570 let id = obj.add_symbol(Symbol {
571 name: name.clone().into_bytes(),
572 value: 0,
573 size: 0,
574 kind: if thread.contains(name.as_str()) {
584 SymbolKind::Tls
585 } else {
586 SymbolKind::Unknown
587 },
588 scope: SymbolScope::Dynamic,
589 weak: weak.contains(name.as_str()),
590 section: SymbolSection::Undefined,
591 flags: SymbolFlags::None,
592 });
593 symbols.insert(name.clone(), id);
594 }
595
596 for reloc in &text.relocs {
597 let (section, at) = if sections.functions {
602 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
603 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
604 let why = format!("a relocation at {} is in front of every function", reloc.at);
605 return Err(Error::Refused { why });
606 };
607 let base = func.start - func.patch.map_or(0, |patch| patch.before);
610 (split[after - 1].0, (reloc.at - base) as u64)
611 } else {
612 (whole, reloc.at as u64)
613 };
614 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
618 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
619 why: format!("no relocation is {:?}", reloc.kind),
620 })?;
621 let symbol = obj.section_symbol(table);
622 let addend = reloc.addend + offset as i64;
623 obj.add_relocation(section, Relocation { offset: at, symbol, addend, flags })
624 .map_err(|why| Error::Refused { why: why.to_string() })?;
625 continue;
626 }
627 add(&mut obj, section, at, reloc, &symbols, flavour)?;
628 }
629
630 if !text.unwind.bytes.is_empty() {
634 let ((name, align), second) = flavour.tables();
635 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
636 obj.append_section_data(frames, &text.unwind.bytes, align);
637 let mut described = HashMap::new();
642 if !text.unwind.info.is_empty() {
643 let Some((name, align)) = second else {
644 let why = "an unwind table here is one section and it was given two".to_owned();
645 return Err(Error::Refused { why });
646 };
647 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
648 obj.append_section_data(codes, &text.unwind.info, align);
649 for label in &text.unwind.labels {
650 let id = obj.add_symbol(Symbol {
651 name: label.name.clone().into_bytes(),
652 value: label.at as u64,
653 size: 0,
654 kind: SymbolKind::Label,
655 scope: SymbolScope::Compilation,
656 weak: false,
657 section: SymbolSection::Section(codes),
658 flags: SymbolFlags::None,
659 });
660 described.insert(label.name.clone(), id);
661 }
662 }
663 if !text.unwind.except.is_empty() {
668 let except =
669 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
670 obj.append_section_data(except, &text.unwind.except, 4);
671 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
672 }
673 for reloc in &text.unwind.relocs {
674 let found = described.get(&reloc.symbol).or_else(|| {
675 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
679 if ours { symbols.get(&reloc.symbol) } else { None }
680 });
681 let (symbol, addend) = match found {
682 Some(&id) => (id, reloc.addend),
686 None => {
700 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
701 let Some((section, at)) = found.map(|i| split[i]) else {
702 let why = format!(
703 "'{}' has an unwind record and is not a function here",
704 reloc.symbol
705 );
706 return Err(Error::Refused { why });
707 };
708 (obj.section_symbol(section), reloc.addend + at as i64)
712 }
713 };
714 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
715 why: format!("no relocation is {:?}", reloc.kind),
716 })?;
717 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
718 obj.add_relocation(frames, record)
719 .map_err(|why| Error::Refused { why: why.to_string() })?;
720 }
721 }
722 let mut named = HashMap::new();
731 for chunk in &info.chunks {
732 let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
733 obj.append_section_data(id, &chunk.bytes, 1);
734 named.insert(chunk.name.as_str(), id);
735 }
736 for chunk in &info.chunks {
737 let section = named[chunk.name.as_str()];
738 for reloc in &chunk.relocs {
739 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
740 Some(&id) => (obj.section_symbol(id), reloc.addend),
743 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
748 Some(which) => {
749 let (section, at) = split[which];
750 (obj.section_symbol(section), reloc.addend + at as i64)
751 }
752 None => {
758 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
759 let Some(which) = found else {
760 let why = format!(
761 "'{}' is named by the debug information and is not defined here",
762 reloc.symbol
763 );
764 return Err(Error::Refused { why });
765 };
766 match placed[which] {
767 (Some(section), at) => {
768 (obj.section_symbol(section), reloc.addend + at as i64)
769 }
770 (None, _) => (symbols[&reloc.symbol], reloc.addend),
771 }
772 }
773 },
774 };
775 let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
776 let flags = flavour
777 .reloc(kind, reloc.after)
778 .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
779 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
780 obj.add_relocation(section, record)
781 .map_err(|why| Error::Refused { why: why.to_string() })?;
782 }
783 }
784 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
785 let Some(section) = section else { continue };
786 for reloc in &object.relocs {
787 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
788 }
789 }
790
791 if !data.exports.is_empty() {
795 let options: String = data.exports.iter().map(Export::option).collect();
796 let id = obj.add_section(Vec::new(), b".drectve".to_vec(), SectionKind::Linker);
797 obj.append_section_data(id, options.as_bytes(), 1);
798 }
799
800 flavour.property(&mut obj, property);
804
805 flavour.marker(&mut obj);
808
809 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
810 flavour.finish(&mut bytes, &ordered);
811 Ok(bytes)
812}
813
814fn distance(
821 obj: &Writer<'_>,
822 symbols: &BTreeMap<String, SymbolId>,
823 apart: &Apart,
824) -> Result<i64, Error> {
825 let find = |name: &str| match symbols.get(name) {
826 Some(&id) => Ok(obj.symbol(id)),
827 None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
828 };
829 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
830 if to.section != from.section {
831 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
832 return Err(Error::Refused { why });
833 }
834 let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
835 let bits = u32::from(apart.bytes) * 8;
836 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
837 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
838 return Err(Error::Refused { why });
839 }
840 Ok(value)
841}
842
843fn beyond(text: &Text, data: &Data) -> Result<(), Error> {
856 let why = |why: String| Err(Error::Refused { why });
857 if text.funcs.iter().any(|func| func.patch.is_some()) {
858 return why("a record of where a patcher's room is has no section flags here".to_owned());
859 }
860 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
861 if matches!(
862 reloc.kind,
863 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
864 ) {
865 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
866 }
867 }
868 for object in &data.objects {
869 if matches!(object.place, Place::Thread { zero: true }) {
870 return why(format!(
871 "'{}' is zeroed thread-local storage, which is not here",
872 object.name
873 ));
874 }
875 let Place::Named(name) = &object.place else { continue };
876 if Array::of(name).is_some() {
877 return why(format!("'{name}' is not a list the startup code here gathers"));
878 }
879 }
880 Ok(())
881}
882
883pub fn defines(
907 text: &Text,
908 data: &Data,
909 aliases: &[Alias],
910 target: &TargetInfo,
911) -> Result<Vec<String>, Error> {
912 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
913 return Err(Error::Format { triple: target.tuple.to_string() });
914 }
915 let names = text
916 .funcs
917 .iter()
918 .filter(|func| func.binding != Binding::Local)
919 .map(|func| func.name.clone())
920 .chain(
921 data.objects
922 .iter()
923 .filter(|object| object.binding != Binding::Local)
924 .map(|object| object.name.clone()),
925 )
926 .chain(
927 aliases
928 .iter()
929 .filter(|alias| alias.binding != Binding::Local)
930 .map(|alias| alias.name.clone()),
931 )
932 .collect();
933 Ok(names)
934}
935
936fn put(
943 obj: &mut Writer<'_>,
944 object: &Object,
945 named: &mut HashMap<String, object::write::SectionId>,
946 sections: Sections,
947 flavour: Flavour,
948) -> (SymbolSection, u64) {
949 if sections.data {
955 if let Some(name) = object.place.split(&object.name) {
956 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
957 let offset = if carries_no_bytes(&object.place) {
958 obj.append_section_bss(section, object.size, object.align)
959 } else {
960 obj.append_section_data(section, &object.bytes, object.align)
961 };
962 return (SymbolSection::Section(section), offset);
963 }
964 }
965 let section = match &object.place {
966 Place::Written => obj.section_id(StandardSection::Data),
967 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
968 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
974 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
975 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
976 },
977 Place::Zero => obj.section_id(StandardSection::UninitializedData),
978 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
979 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
980 Place::Merged => return (SymbolSection::Common, 0),
981 Place::Named(name) => {
987 let section = made(obj, named, name, SectionKind::Data);
988 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
989 obj.section_mut(section).flags = flags;
990 }
991 section
992 }
993 Place::Pointer => {
997 let name = format!(".rdata${}", object.name);
998 made(obj, named, &name, SectionKind::ReadOnlyData)
999 }
1000 };
1001 let offset = if carries_no_bytes(&object.place) {
1002 obj.append_section_bss(section, object.size, object.align)
1003 } else {
1004 obj.append_section_data(section, &object.bytes, object.align)
1005 };
1006 (SymbolSection::Section(section), offset)
1007}
1008
1009fn tables(
1022 obj: &mut Writer<'_>,
1023 text: &Text,
1024 split: &[(object::write::SectionId, u64)],
1025 named: &mut HashMap<String, object::write::SectionId>,
1026 sections: Sections,
1027 flavour: Flavour,
1028) -> Result<HashMap<String, (object::write::SectionId, u64)>, Error> {
1029 let mut placed = HashMap::new();
1030 if text.tables.is_empty() {
1031 return Ok(placed);
1032 }
1033 if flavour != Flavour::Elf {
1034 let why = "a jump table outside the code is written on ELF only".to_owned();
1035 return Err(Error::Refused { why });
1036 }
1037 let flags = flavour.reloc(Reference::Away, 0).ok_or_else(|| Error::Refused {
1038 why: "no relocation is a distance from where it is written".to_owned(),
1039 })?;
1040 for table in &text.tables {
1041 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
1042 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1043 })?;
1044 let section = if sections.data {
1045 let name = format!(".rodata.{}", func.name);
1046 made(obj, named, &name, SectionKind::ReadOnlyData)
1047 } else {
1048 obj.section_id(StandardSection::ReadOnlyData)
1049 };
1050 let offset = obj.append_section_data(section, &vec![0; 4 * table.cells.len()], 4);
1051 placed.insert(table.name.clone(), (section, offset));
1052 let (code, at) = split[table.func];
1053 let symbol = obj.section_symbol(code);
1054 for (index, &cell) in table.cells.iter().enumerate() {
1055 let place = 4 * index as u64;
1056 let addend = at as i64 + cell as i64 + place as i64;
1057 let record = Relocation { offset: offset + place, symbol, addend, flags };
1058 obj.add_relocation(section, record)
1059 .map_err(|why| Error::Refused { why: why.to_string() })?;
1060 }
1061 }
1062 Ok(placed)
1063}
1064
1065fn carries_no_bytes(place: &Place) -> bool {
1071 matches!(place, Place::Zero | Place::Thread { zero: true })
1072}
1073
1074fn made(
1082 obj: &mut Writer<'_>,
1083 named: &mut HashMap<String, object::write::SectionId>,
1084 name: &str,
1085 kind: SectionKind,
1086) -> object::write::SectionId {
1087 if let Some(section) = named.get(name) {
1088 return *section;
1089 }
1090 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1091 named.insert(name.to_owned(), section);
1092 section
1093}
1094
1095fn kind_of(place: &Place) -> SectionKind {
1104 match place {
1105 Place::ReadOnly | Place::Pointer => SectionKind::ReadOnlyData,
1106 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1107 Place::Zero => SectionKind::UninitializedData,
1108 Place::Thread { zero: false } => SectionKind::Tls,
1109 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1110 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
1111 }
1112}
1113
1114fn add(
1121 obj: &mut Writer<'_>,
1122 section: object::write::SectionId,
1123 at: u64,
1124 reloc: &Reloc,
1125 symbols: &BTreeMap<String, SymbolId>,
1126 flavour: Flavour,
1127) -> Result<(), Error> {
1128 let flags = flavour
1129 .reloc(reloc.kind, reloc.after)
1130 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1131 obj.add_relocation(
1132 section,
1133 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1134 )
1135 .map_err(|why| Error::Refused { why: why.to_string() })
1136}
1137
1138pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1151 match binding {
1152 Binding::Local => SymbolScope::Compilation,
1153 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1154 }
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 use object::read::elf::Sym as _;
1162 use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1163 use object::{elf, pe};
1164 use rucc_target::{Arch, Env, Os, Triple};
1165
1166 use crate::elf::PATCHABLE;
1167 use crate::section::{Chunk, Extent, Marker, Offer, Patch, Reloc};
1168
1169 fn target() -> TargetInfo {
1171 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1172 }
1173
1174 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1179 Extent {
1180 name,
1181 start,
1182 len,
1183 align: crate::FUNC_ALIGN,
1184 binding,
1185 visibility: Visibility::Default,
1186 patch: None,
1187 landings: Vec::new(),
1188 }
1189 }
1190
1191 fn calling(name: &str) -> Text {
1193 Text {
1194 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1195 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1196 relocs: vec![Reloc {
1197 at: 1,
1198 symbol: name.to_owned(),
1199 kind: Reference::Call,
1200 addend: -4,
1201 after: 0,
1202 }],
1203 ..Text::default()
1204 }
1205 }
1206
1207 #[test]
1208 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1209 let text = calling("puts");
1210 let bytes =
1211 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1212 .expect("an object");
1213 let file = object::File::parse(&bytes[..]).expect("a readable object");
1214 let section = file.section_by_name(".text").expect("a text section");
1215 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1216 }
1217
1218 #[test]
1219 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1220 let mut text = calling("puts");
1221 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1222 text.bytes.resize(17, 0x90);
1223 let bytes =
1224 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1225 .expect("an object");
1226 let file = object::File::parse(&bytes[..]).expect("a readable object");
1227 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1228 assert_eq!(g.address(), 16);
1229 assert_eq!(g.size(), 1);
1230 assert_eq!(g.kind(), SymbolKind::Text);
1231 assert!(g.is_global(), "nothing said otherwise about this one");
1232 }
1233
1234 #[test]
1235 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1236 let mut text = calling("puts");
1237 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1238 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1239 text.bytes.resize(33, 0x90);
1240 let bytes =
1241 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1242 .expect("an object");
1243 let file = object::File::parse(&bytes[..]).expect("a readable object");
1244 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1245 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1248 assert!(!hidden.is_weak());
1249 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1250 assert!(shared.is_weak(), "a weak function has to be able to lose");
1251 assert!(shared.is_global());
1252 }
1253
1254 #[test]
1271 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1272 let mut text = calling("puts");
1273 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1274 text.funcs[0].start = 3;
1275 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1276 text.relocs[0].at = 4;
1277 let bytes =
1278 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1279 .expect("an object");
1280 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1281 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1282 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1283 assert_eq!(section.align(), 8);
1284 let header = section.elf_section_header();
1285 assert_eq!(
1286 header.sh_flags.get(Endianness::Little),
1287 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1288 );
1289 let index = file.section_by_name(".text").expect("a text section").index().0;
1292 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1293 assert_ne!(index, 0);
1294
1295 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1297 panic!("one address in the record")
1298 };
1299 assert_eq!(*at, 0);
1300 assert_eq!(reloc.addend(), 0);
1301 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1302 }
1303
1304 #[test]
1306 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1307 let text = calling("puts");
1308 let bytes =
1309 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1310 .expect("an object");
1311 let file = object::File::parse(&bytes[..]).expect("a readable object");
1312 assert!(file.section_by_name(PATCHABLE).is_none());
1313 }
1314
1315 #[test]
1321 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1322 let mut text = calling("puts");
1323 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1324 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1325 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1326 text.bytes.resize(17, 0x90);
1327 let output =
1328 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1329 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1330 .expect("an object");
1331 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1332 let links: Vec<usize> = file
1333 .sections()
1334 .filter(|section| section.name() == Ok(PATCHABLE))
1335 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1336 .collect();
1337 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1338 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1339 }
1340
1341 #[test]
1342 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1343 let mut text = calling("puts");
1344 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1345 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1346 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1347 text.bytes.resize(49, 0x90);
1348 let bytes =
1349 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1350 .expect("an object");
1351 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1352 let visibility = |name: &str| {
1353 file.symbols()
1354 .find(|s| s.name() == Ok(name))
1355 .expect("the function")
1356 .elf_symbol()
1357 .st_visibility()
1358 };
1359 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1361 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1362 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1365 }
1366
1367 #[test]
1377 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1378 let mut text = calling("puts");
1379 for (index, (name, seen)) in
1380 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1381 {
1382 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1383 func.visibility = seen;
1384 text.funcs.push(func);
1385 }
1386 text.bytes.resize(49, 0x90);
1387 let mut data = Data::default();
1388 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1389 let mut object = variable(name, Place::Written);
1390 object.visibility = seen;
1391 data.objects.push(object);
1392 }
1393 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1394 .expect("an object");
1395 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1396 let visibility = |name: &str| {
1397 file.symbols()
1398 .find(|s| s.name() == Ok(name))
1399 .expect("the symbol")
1400 .elf_symbol()
1401 .st_visibility()
1402 };
1403 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1404 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1405 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1406 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1407 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1410 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1411 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1412 }
1413
1414 #[test]
1415 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1416 let bytes = write(
1417 &calling("puts"),
1418 &Data::default(),
1419 &[],
1420 &target(),
1421 Output::default(),
1422 &Info::default(),
1423 )
1424 .expect("an object");
1425 let file = object::File::parse(&bytes[..]).expect("a readable object");
1426 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1427 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1428 }
1429
1430 #[test]
1431 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1432 for (reference, wanted) in [
1433 (Reference::Call, elf::R_X86_64_PLT32),
1434 (Reference::Data, elf::R_X86_64_PC32),
1435 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1436 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1437 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1438 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1439 ] {
1440 let mut text = calling("puts");
1441 text.relocs[0].kind = reference;
1442 let bytes =
1443 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1444 .expect("an object");
1445 let file = object::File::parse(&bytes[..]).expect("a readable object");
1446 let section = file.section_by_name(".text").expect("a text section");
1447 let (offset, reloc) = section.relocations().next().expect("one relocation");
1448 assert_eq!(offset, 1);
1449 assert_eq!(reloc.addend(), -4);
1450 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1451 }
1452 }
1453
1454 #[test]
1455 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1456 let mut text = calling("puts");
1457 text.relocs.push(Reloc {
1458 at: 1,
1459 symbol: "puts".to_owned(),
1460 kind: Reference::Call,
1461 addend: -4,
1462 after: 0,
1463 });
1464 let bytes =
1465 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1466 .expect("an object");
1467 let file = object::File::parse(&bytes[..]).expect("a readable object");
1468 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1469 }
1470
1471 #[test]
1472 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1473 let text = calling("f");
1474 let bytes =
1475 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1476 .expect("an object");
1477 let file = object::File::parse(&bytes[..]).expect("a readable object");
1478 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1479 let f = found.next().expect("the function");
1480 assert!(!f.is_undefined(), "the file defines it");
1481 assert!(found.next().is_none(), "and defines it once");
1482 }
1483
1484 #[test]
1485 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1486 let bytes = write(
1487 &calling("puts"),
1488 &Data::default(),
1489 &[],
1490 &target(),
1491 Output::default(),
1492 &Info::default(),
1493 )
1494 .expect("an object");
1495 let file = object::File::parse(&bytes[..]).expect("a readable object");
1496 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1497 assert!(note.data().expect("no bytes").is_empty());
1498 }
1499
1500 #[test]
1507 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1508 let property = Property { features: Property::IBT | Property::SHSTK };
1509 let output = Output { property, ..Output::default() };
1510 let bytes =
1511 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1512 .expect("an object");
1513 let file = object::File::parse(&bytes[..]).expect("a readable object");
1514 let note = file.section_by_name(".note.gnu.property").expect("the note");
1515 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1516 let want: Vec<u8> = [
1517 4u32,
1518 16,
1519 5,
1520 u32::from_le_bytes(*b"GNU\0"),
1521 Property::X86_FEATURES,
1522 4,
1523 Property::IBT | Property::SHSTK,
1524 0,
1525 ]
1526 .iter()
1527 .flat_map(|word| word.to_le_bytes())
1528 .collect();
1529 assert_eq!(note.data().expect("the bytes"), &want[..]);
1530 }
1531
1532 #[test]
1538 fn a_file_built_to_have_nothing_checked_says_nothing() {
1539 let bytes = write(
1540 &calling("puts"),
1541 &Data::default(),
1542 &[],
1543 &target(),
1544 Output::default(),
1545 &Info::default(),
1546 )
1547 .expect("an object");
1548 let file = object::File::parse(&bytes[..]).expect("a readable object");
1549 assert!(file.section_by_name(".note.gnu.property").is_none());
1550 }
1551
1552 #[test]
1561 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1562 let mut text = calling("puts");
1563 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1564 text.bytes.resize(17, 0x90);
1565 text.unwind.bytes = vec![0; 64];
1568 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1569 text.unwind.relocs.push(Reloc {
1570 at,
1571 symbol: name.to_owned(),
1572 kind: Reference::Address { bytes: 8 },
1573 addend: 0,
1574 after: 0,
1575 });
1576 }
1577 let bytes =
1578 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1579 .expect("an object");
1580 let file = object::File::parse(&bytes[..]).expect("a readable object");
1581 let mut found = points_at(&file);
1582 found.sort_unstable();
1583 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1584 }
1585
1586 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1589 let frames = file.section_by_name(".eh_frame").expect("the table");
1590 frames
1591 .relocations()
1592 .map(|(offset, reloc)| {
1593 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1594 panic!("a record points at something that is not a symbol");
1595 };
1596 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1597 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1598 let section = symbol.section_index().expect("a section symbol is in one");
1599 let name = file.section_by_index(section).expect("a readable section");
1600 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1601 })
1602 .collect()
1603 }
1604
1605 #[test]
1618 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1619 let mut text = two();
1620 text.unwind.bytes = vec![0; 64];
1621 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1622 text.unwind.relocs.push(Reloc {
1623 at,
1624 symbol: name.to_owned(),
1625 kind: Reference::Data,
1626 addend: 0,
1627 after: 0,
1628 });
1629 }
1630 let bytes =
1631 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1632 .expect("an object");
1633 let file = object::File::parse(&bytes[..]).expect("a readable object");
1634 let mut whole = points_at(&file);
1635 whole.sort_unstable();
1636 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1637
1638 let sections =
1639 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1640 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1641 .expect("an object");
1642 let file = object::File::parse(&bytes[..]).expect("a readable object");
1643 let mut split = points_at(&file);
1644 split.sort_unstable();
1645 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1646 }
1647
1648 #[test]
1655 fn a_record_about_something_this_file_does_not_define_is_refused() {
1656 let mut text = calling("puts");
1657 text.unwind.bytes = vec![0; 64];
1658 text.unwind.relocs.push(Reloc {
1659 at: 32,
1660 symbol: "puts".to_owned(),
1661 kind: Reference::Data,
1662 addend: 0,
1663 after: 0,
1664 });
1665 let why =
1666 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1667 .expect_err("a record about a name from somewhere else");
1668 assert!(why.to_string().contains("puts"), "{why}");
1669 }
1670
1671 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1673 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1674 let index = symbol.section_index().expect("a section to be defined in");
1675 let section = file.section_by_index(index).expect("a readable section");
1676 section.name().expect("a named section").to_owned()
1677 }
1678
1679 fn two() -> Text {
1681 let mut text = calling("puts");
1682 text.bytes.resize(16, 0x90);
1685 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1686 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1687 text.relocs.push(Reloc {
1688 at: 17,
1689 symbol: "puts".to_owned(),
1690 kind: Reference::Call,
1691 addend: -4,
1692 after: 0,
1693 });
1694 text
1695 }
1696
1697 #[test]
1704 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1705 let sections =
1706 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1707 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1708 .expect("an object");
1709 let file = object::File::parse(&bytes[..]).expect("a readable object");
1710 assert_eq!(lives_in(&file, "f"), ".text.f");
1711 assert_eq!(lives_in(&file, "g"), ".text.g");
1712 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1713 for name in ["f", "g"] {
1716 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1717 assert_eq!(symbol.address(), 0, "{name}");
1718 assert_eq!(symbol.size(), 6, "{name}");
1719 }
1720 let section = file.section_by_name(".text.g").expect("the second function");
1721 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1722 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1725 }
1726
1727 #[test]
1733 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1734 let sections =
1735 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1736 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1737 .expect("an object");
1738 let file = object::File::parse(&bytes[..]).expect("a readable object");
1739 for name in [".text.f", ".text.g"] {
1740 let section = file.section_by_name(name).expect("a function");
1741 let (offset, _) = section.relocations().next().expect("the call in it");
1742 assert_eq!(offset, 1, "{name}");
1745 assert_eq!(section.relocations().count(), 1, "{name}");
1746 }
1747 }
1748
1749 fn switching() -> Text {
1751 let mut text = two();
1752 let name = ".Lg_j0".to_owned();
1753 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5] });
1754 text
1755 }
1756
1757 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1759 let section = file.section_by_name(section).expect("the table's section");
1760 section
1761 .relocations()
1762 .map(|(offset, reloc)| {
1763 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1764 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1765 panic!("a cell against something that is not a symbol");
1766 };
1767 let symbol = file.symbol_by_index(index).expect("a symbol");
1768 assert_eq!(symbol.kind(), SymbolKind::Section);
1769 let at = symbol.section_index().expect("a section symbol is in one");
1770 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1771 (offset, name.to_owned(), reloc.addend())
1772 })
1773 .collect()
1774 }
1775
1776 #[test]
1777 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1778 let mut text = switching();
1781 text.relocs[1].symbol = ".Lg_j0".to_owned();
1782 text.relocs[1].kind = Reference::Data;
1783 let bytes =
1784 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1785 .expect("an object");
1786 let file = object::File::parse(&bytes[..]).expect("a readable object");
1787 let rodata = file.section_by_name(".rodata").expect("the table's section");
1788 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
1789 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
1790 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
1791 let (at, reloc) = file
1792 .section_by_name(".text")
1793 .expect("the code")
1794 .relocations()
1795 .find(|(at, _)| *at == 17)
1796 .expect("the reference to the table");
1797 assert_eq!((at, reloc.addend()), (17, -4));
1798 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1799 panic!("a reference against something that is not a symbol");
1800 };
1801 let symbol = file.symbol_by_index(index).expect("a symbol");
1802 assert_eq!(symbol.section_index(), Some(rodata.index()));
1803 assert_eq!(symbol.kind(), SymbolKind::Section);
1804 assert_eq!(
1807 cells(&file, ".rodata"),
1808 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
1809 );
1810 }
1811
1812 #[test]
1813 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
1814 let sections =
1815 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
1816 let bytes =
1817 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
1818 .expect("an object");
1819 let file = object::File::parse(&bytes[..]).expect("a readable object");
1820 assert_eq!(
1822 cells(&file, ".rodata.g"),
1823 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
1824 );
1825 }
1826
1827 #[test]
1828 fn a_jump_table_outside_the_code_is_refused_on_windows() {
1829 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1830 let written = write(
1831 &switching(),
1832 &Data::default(),
1833 &[],
1834 &target,
1835 Output::default(),
1836 &Info::default(),
1837 );
1838 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
1839 }
1840
1841 #[test]
1844 fn debug_sections_on_windows_reach_each_other_by_section_offset() {
1845 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1846 let reloc = |at, symbol: &str, bytes| Reloc {
1847 at,
1848 symbol: symbol.to_owned(),
1849 kind: Reference::Address { bytes },
1850 addend: 0,
1851 after: 0,
1852 };
1853 let info = Info {
1854 chunks: vec![
1855 Chunk { name: ".debug_abbrev".to_owned(), bytes: vec![0; 4], relocs: Vec::new() },
1856 Chunk {
1857 name: ".debug_info".to_owned(),
1858 bytes: vec![0; 12],
1859 relocs: vec![reloc(0, ".debug_abbrev", 4), reloc(4, "f", 8)],
1860 },
1861 ],
1862 };
1863 let bytes =
1864 write(&calling("puts"), &Data::default(), &[], &target, Output::default(), &info)
1865 .expect("object");
1866 let file = object::File::parse(&bytes[..]).expect("a readable object");
1867 let section = file.section_by_name(".debug_info").expect("the debug section");
1868 let kinds: Vec<_> = section.relocations().map(|(at, reloc)| (at, reloc.flags())).collect();
1869 assert_eq!(
1870 kinds,
1871 [
1872 (0, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_SECREL }),
1873 (4, RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }),
1874 ]
1875 );
1876 }
1877
1878 fn variable(name: &str, place: Place) -> Object {
1880 Object {
1881 name: name.to_owned(),
1882 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1883 size: 4,
1884 align: 4,
1885 place,
1886 binding: Binding::Global,
1887 visibility: Visibility::Default,
1888 relocs: Vec::new(),
1889 }
1890 }
1891
1892 fn measured() -> (Text, Data) {
1894 let mut text = calling("puts");
1895 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1896 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1897 let mut table = variable("table", Place::ReadOnly);
1898 table.bytes = vec![0; 8];
1899 table.size = 8;
1900 let apart = |at, to: &str, from: &str| Apart {
1901 object: 0,
1902 at,
1903 to: to.to_owned(),
1904 from: from.to_owned(),
1905 addend: 0,
1906 bytes: 4,
1907 };
1908 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1909 (text, Data { apart, exports: Vec::new(), weak: Vec::new(), objects: vec![table] })
1910 }
1911
1912 #[test]
1913 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1914 let (text, data) = measured();
1915 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1916 .expect("an object");
1917 let file = object::File::parse(&bytes[..]).expect("a readable object");
1918 let section = file.section_by_name(".rodata").expect("a read only section");
1919 assert_eq!(section.relocations().count(), 0);
1920 let image = section.data().expect("the image");
1921 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1922 }
1923
1924 #[test]
1925 fn a_distance_between_labels_in_two_sections_is_refused() {
1926 let (mut text, data) = measured();
1929 text.bytes.resize(22, 0x90);
1930 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1931 text.labels[1].at = 17;
1932 let output =
1933 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1934 let refused = write(&text, &data, &[], &target(), output, &Info::default());
1935 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1936 }
1937
1938 fn holding(object: Object) -> Vec<u8> {
1940 let data = Data {
1941 apart: Vec::new(),
1942 exports: Vec::new(),
1943 weak: Vec::new(),
1944 objects: vec![object],
1945 };
1946 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1947 .expect("an object")
1948 }
1949
1950 #[test]
1951 fn what_a_variable_is_decides_which_section_it_goes_in() {
1952 for (place, wanted) in [
1953 (Place::Written, ".data"),
1954 (Place::ReadOnly, ".rodata"),
1955 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1956 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1957 (Place::Zero, ".bss"),
1958 (Place::Thread { zero: false }, ".tdata"),
1959 (Place::Thread { zero: true }, ".tbss"),
1960 (Place::Named(".init_array".to_owned()), ".init_array"),
1961 ] {
1962 let bytes = holding(variable("x", place.clone()));
1963 let file = object::File::parse(&bytes[..]).expect("a readable object");
1964 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1965 assert_eq!(section.size(), 4, "{place:?}");
1966 let carried = section.data().expect("the bytes").len();
1969 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1970 }
1971 }
1972
1973 #[test]
1979 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1980 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1981 let bytes = holding(variable("counter", place.clone()));
1982 let file = object::File::parse(&bytes[..]).expect("a readable object");
1983 let symbol = file
1984 .symbols()
1985 .find(|symbol| symbol.name() == Ok("counter"))
1986 .unwrap_or_else(|| panic!("{place:?}"));
1987 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1988 }
1989 }
1990
1991 #[test]
1997 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1998 for (name, wanted) in [
1999 (".init_array", elf::SHT_INIT_ARRAY),
2000 (".init_array.00101", elf::SHT_INIT_ARRAY),
2001 (".fini_array", elf::SHT_FINI_ARRAY),
2002 (".preinit_array", elf::SHT_PREINIT_ARRAY),
2003 (".init_arrays", elf::SHT_PROGBITS),
2004 ] {
2005 let bytes = holding(variable("x", Place::Named(name.to_owned())));
2006 let file = object::File::parse(&bytes[..]).expect("a readable object");
2007 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
2008 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
2009 panic!("{name} is not an elf section");
2010 };
2011 assert_eq!(sh_type, wanted, "{name}");
2012 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
2013 }
2014 }
2015
2016 #[test]
2022 fn two_variables_in_one_named_section_share_it() {
2023 let objects = vec![
2024 variable("x", Place::Named(".init_array".to_owned())),
2025 variable("y", Place::Named(".init_array".to_owned())),
2026 ];
2027 let data = Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects };
2028 let bytes =
2029 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2030 .expect("an object");
2031 let file = object::File::parse(&bytes[..]).expect("a readable object");
2032 let named: Vec<_> =
2033 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
2034 assert_eq!(named.len(), 1);
2035 assert_eq!(named[0].size(), 8);
2036 }
2037
2038 #[test]
2042 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
2043 let sections =
2044 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2045 for (place, wanted) in [
2046 (Place::Written, ".data.x"),
2047 (Place::ReadOnly, ".rodata.x"),
2048 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
2049 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
2050 (Place::Zero, ".bss.x"),
2051 (Place::Thread { zero: false }, ".tdata.x"),
2052 (Place::Thread { zero: true }, ".tbss.x"),
2053 ] {
2054 let data = Data {
2055 apart: Vec::new(),
2056 exports: Vec::new(),
2057 weak: Vec::new(),
2058 objects: vec![variable("x", place.clone())],
2059 };
2060 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
2061 .expect("object");
2062 let file = object::File::parse(&bytes[..]).expect("a readable object");
2063 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
2064 let section = file.section_by_name(wanted).expect("the section it named");
2065 assert_eq!(section.size(), 4, "{place:?}");
2066 let carried = section.data().expect("the bytes").len();
2069 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
2070 }
2071 }
2072
2073 #[test]
2077 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
2078 let sections =
2079 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2080 let named = Place::Named(".init_array".to_owned());
2081 let objects = vec![variable("m", Place::Merged), variable("n", named)];
2082 let bytes = write(
2083 &Text::default(),
2084 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2085 &[],
2086 &target(),
2087 sections,
2088 &Info::default(),
2089 )
2090 .expect("object");
2091 let file = object::File::parse(&bytes[..]).expect("a readable object");
2092 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2093 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2094 assert_eq!(lives_in(&file, "n"), ".init_array");
2095 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2096 }
2097
2098 #[test]
2102 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2103 let sections =
2104 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2105 let pointer = Object {
2106 bytes: vec![0; 8],
2107 size: 8,
2108 align: 8,
2109 relocs: vec![Reloc {
2110 at: 0,
2111 symbol: "y".to_owned(),
2112 kind: Reference::Address { bytes: 8 },
2113 addend: 0,
2114 after: 0,
2115 }],
2116 ..variable("p", Place::Written)
2117 };
2118 let objects = vec![variable("first", Place::Written), pointer];
2119 let bytes = write(
2120 &Text::default(),
2121 &Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects },
2122 &[],
2123 &target(),
2124 sections,
2125 &Info::default(),
2126 )
2127 .expect("object");
2128 let file = object::File::parse(&bytes[..]).expect("a readable object");
2129 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2130 let (offset, reloc) = section.relocations().next().expect("one relocation");
2131 assert_eq!(offset, 0);
2134 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2135 }
2136
2137 #[test]
2145 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2146 let place = Place::RelocReadOnly { local: true };
2147 let data = Data {
2148 apart: Vec::new(),
2149 exports: Vec::new(),
2150 weak: Vec::new(),
2151 objects: vec![variable("first", place.clone()), variable("second", place)],
2152 };
2153 let bytes =
2154 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2155 .expect("an object");
2156 let file = object::File::parse(&bytes[..]).expect("a readable object");
2157 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2158 assert_eq!(named, 1, "one section holding both, not one each");
2159 }
2160
2161 #[test]
2162 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2163 let mut data = Data {
2164 apart: Vec::new(),
2165 exports: Vec::new(),
2166 weak: Vec::new(),
2167 objects: vec![variable("first", Place::Written)],
2168 };
2169 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2170 let bytes =
2171 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2172 .expect("an object");
2173 let file = object::File::parse(&bytes[..]).expect("a readable object");
2174 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2175 assert_eq!(second.kind(), SymbolKind::Data);
2176 assert_eq!(second.size(), 4);
2177 assert_eq!(second.address(), 16);
2181 }
2182
2183 #[test]
2184 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2185 for (binding, global, weak) in [
2186 (Binding::Global, true, false),
2187 (Binding::Local, false, false),
2188 (Binding::Weak, true, true),
2189 ] {
2190 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2191 let file = object::File::parse(&bytes[..]).expect("a readable object");
2192 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2193 assert_eq!(x.is_global(), global, "{binding:?}");
2194 assert_eq!(x.is_weak(), weak, "{binding:?}");
2195 }
2196 }
2197
2198 #[test]
2199 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2200 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2201 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2202 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2203 assert!(x.is_common(), "the linker merges every definition of this name into one");
2204 assert_eq!(x.size(), 4);
2205 assert_eq!(x.address(), 0);
2209 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2210 }
2211
2212 #[test]
2213 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2214 let object = Object {
2215 bytes: vec![0; 8],
2216 size: 8,
2217 align: 8,
2218 relocs: vec![Reloc {
2219 at: 0,
2220 symbol: "y".to_owned(),
2221 kind: Reference::Address { bytes: 8 },
2222 addend: 16,
2223 after: 0,
2224 }],
2225 ..variable("p", Place::Written)
2226 };
2227 let bytes = holding(object);
2228 let file = object::File::parse(&bytes[..]).expect("a readable object");
2229 let section = file.section_by_name(".data").expect("a data section");
2230 let (offset, reloc) = section.relocations().next().expect("one relocation");
2231 assert_eq!(offset, 0);
2232 assert_eq!(reloc.addend(), 16);
2233 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2234 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2235 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2236 }
2237
2238 #[test]
2245 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2246 let mut text = Text::default();
2247 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2248 text.bytes.resize(8, 0x90);
2249 text.relocs.push(Reloc {
2250 at: 1,
2251 symbol: "hook".to_owned(),
2252 kind: Reference::Call,
2253 addend: -4,
2254 after: 0,
2255 });
2256 let data = Data {
2257 apart: Vec::new(),
2258 exports: Vec::new(),
2259 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2260 objects: vec![],
2261 };
2262 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2263 .expect("an object");
2264 let file = object::File::parse(&bytes[..]).expect("a readable object");
2265
2266 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2267 assert!(hook.is_undefined(), "nothing here defines it");
2268 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2269
2270 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2274 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2275 }
2276
2277 #[test]
2292 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2293 let mut text = Text::default();
2294 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2295 text.bytes.resize(16, 0x90);
2296 text.relocs.push(Reloc {
2297 at: 3,
2298 symbol: "flags".to_owned(),
2299 kind: Reference::Thread,
2300 addend: -4,
2301 after: 0,
2302 });
2303 text.relocs.push(Reloc {
2306 at: 10,
2307 symbol: "shared".to_owned(),
2308 kind: Reference::Got,
2309 addend: -4,
2310 after: 0,
2311 });
2312 let data =
2313 Data { apart: Vec::new(), exports: Vec::new(), weak: Vec::new(), objects: vec![] };
2314 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2315 .expect("an object");
2316 let file = object::File::parse(&bytes[..]).expect("a readable object");
2317
2318 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2319 assert!(flags.is_undefined(), "nothing here defines it");
2320 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2321
2322 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2323 assert!(shared.is_undefined(), "nothing here defines this one either");
2324 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2325 }
2326
2327 #[test]
2329 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2330 let mut data = Data {
2331 apart: Vec::new(),
2332 exports: Vec::new(),
2333 weak: Vec::new(),
2334 objects: vec![variable("first", Place::Written)],
2335 };
2336 data.objects.push(Object {
2337 bytes: vec![0; 16],
2338 size: 16,
2339 align: 8,
2340 relocs: vec![Reloc {
2341 at: 8,
2342 symbol: "y".to_owned(),
2343 kind: Reference::Address { bytes: 8 },
2344 addend: 0,
2345 after: 0,
2346 }],
2347 ..variable("second", Place::Written)
2348 });
2349 let bytes =
2350 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2351 .expect("an object");
2352 let file = object::File::parse(&bytes[..]).expect("a readable object");
2353 let section = file.section_by_name(".data").expect("a data section");
2354 let (offset, _) = section.relocations().next().expect("one relocation");
2355 assert_eq!(offset, 16);
2358 }
2359
2360 #[test]
2361 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2362 let data = Data {
2363 apart: Vec::new(),
2364 exports: Vec::new(),
2365 weak: Vec::new(),
2366 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2367 };
2368 let aliases = [Alias {
2369 name: "b".to_owned(),
2370 target: "a".to_owned(),
2371 binding: Binding::Global,
2372 visibility: Visibility::Default,
2373 }];
2374 let bytes = write(
2375 &Text::default(),
2376 &data,
2377 &aliases,
2378 &target(),
2379 Output::default(),
2380 &Info::default(),
2381 )
2382 .expect("an object");
2383 let file = object::File::parse(&bytes[..]).expect("a readable object");
2384 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2385 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2386 assert_eq!(b.address(), a.address(), "the same place");
2387 assert_eq!(b.size(), a.size());
2388 assert_eq!(b.section_index(), a.section_index());
2389 assert!(a.is_local(), "the target was written `static`");
2392 assert!(b.is_global(), "and the name given to it was not");
2393 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2395 }
2396
2397 #[test]
2398 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2399 let text = calling("puts");
2400 let aliases = [Alias {
2401 name: "g".to_owned(),
2402 target: "f".to_owned(),
2403 binding: Binding::Weak,
2404 visibility: Visibility::Default,
2405 }];
2406 let bytes = write(
2407 &text,
2408 &Data::default(),
2409 &aliases,
2410 &target(),
2411 Output::default(),
2412 &Info::default(),
2413 )
2414 .expect("an object");
2415 let file = object::File::parse(&bytes[..]).expect("a readable object");
2416 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2417 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2418 assert_eq!(g.address(), f.address());
2419 assert_eq!(g.size(), f.size());
2420 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2421 assert!(g.is_weak(), "so that a program may define the name itself instead");
2422 }
2423
2424 #[test]
2427 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2428 let aliases = [Alias {
2429 name: "b".to_owned(),
2430 target: "a".to_owned(),
2431 binding: Binding::Global,
2432 visibility: Visibility::Default,
2433 }];
2434 let error = write(
2435 &Text::default(),
2436 &Data::default(),
2437 &aliases,
2438 &target(),
2439 Output::default(),
2440 &Info::default(),
2441 )
2442 .expect_err("nothing to point at");
2443 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2444 }
2445
2446 #[test]
2447 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2448 let text = calling("puts");
2449 for triple in [
2450 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2451 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2452 ] {
2453 let error = write(
2454 &text,
2455 &Data::default(),
2456 &[],
2457 &TargetInfo::new(triple),
2458 Output::default(),
2459 &Info::default(),
2460 )
2461 .expect_err("no writer");
2462 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2463 }
2464 }
2465
2466 #[test]
2472 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2473 let mut text = calling("puts");
2474 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2475 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2476 text.bytes.resize(33, 0x90);
2477 let data = Data {
2478 apart: Vec::new(),
2479 exports: Vec::new(),
2480 weak: Vec::new(),
2481 objects: vec![variable("seen", Place::Written), {
2482 let mut quiet = variable("quiet", Place::Zero);
2483 quiet.binding = Binding::Local;
2484 quiet
2485 }],
2486 };
2487 let aliases = [Alias {
2488 name: "second".to_owned(),
2489 target: "f".to_owned(),
2490 binding: Binding::Global,
2491 visibility: Visibility::Default,
2492 }];
2493
2494 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2495 assert_eq!(names, ["f", "shared", "seen", "second"]);
2496
2497 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2498 .expect("an object");
2499 let file = object::File::parse(&bytes[..]).expect("a readable object");
2500 let found: Vec<String> = file
2501 .symbols()
2502 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2503 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2504 .collect();
2505 let mut sorted = names.clone();
2506 sorted.sort();
2507 let mut theirs = found;
2508 theirs.sort();
2509 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2510 }
2511
2512 fn windows() -> TargetInfo {
2514 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2515 }
2516
2517 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2519 let file = object::File::parse(bytes).expect("a readable object");
2520 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2521 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2522 }
2523
2524 #[test]
2525 fn a_windows_target_is_written_rather_than_refused() {
2526 let text = calling("puts");
2527 let bytes =
2528 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2529 .expect("an object");
2530 let file = object::File::parse(&bytes[..]).expect("a readable object");
2531 assert_eq!(file.format(), BinaryFormat::Coff);
2532 let section = file.section_by_name(".text").expect("a text section");
2533 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2534 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2535 assert!(names.contains(&"f"), "{names:?}");
2536 assert!(names.contains(&"puts"), "{names:?}");
2537 }
2538
2539 #[test]
2547 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2548 for (after, typ) in [
2549 (0, pe::IMAGE_REL_AMD64_REL32),
2550 (1, pe::IMAGE_REL_AMD64_REL32_1),
2551 (4, pe::IMAGE_REL_AMD64_REL32_4),
2552 (5, pe::IMAGE_REL_AMD64_REL32_5),
2553 ] {
2554 let mut text = calling("puts");
2555 text.relocs[0].addend = -4 - i64::from(after);
2558 text.relocs[0].after = after;
2559 text.bytes.resize(6 + after as usize, 0x90);
2560 text.funcs[0].len = text.bytes.len();
2561 let bytes = write(
2562 &text,
2563 &Data::default(),
2564 &[],
2565 &windows(),
2566 Output::default(),
2567 &Info::default(),
2568 )
2569 .expect("an object");
2570 let file = object::File::parse(&bytes[..]).expect("a readable object");
2571 let section = file.section_by_name(".text").expect("a text section");
2572 let (_, reloc) = section.relocations().next().expect("the relocation");
2573 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2574 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2577 }
2578 }
2579
2580 #[test]
2583 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2584 let mut text = calling("puts");
2585 text.relocs[0].addend = 12;
2586 let bytes =
2587 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2588 .expect("an object");
2589 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2590 }
2591
2592 #[test]
2593 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2594 let object = Object {
2595 bytes: vec![0; 8],
2596 size: 8,
2597 align: 8,
2598 relocs: vec![Reloc {
2599 at: 0,
2600 symbol: "y".to_owned(),
2601 kind: Reference::Address { bytes: 8 },
2602 addend: 0,
2603 after: 0,
2604 }],
2605 ..variable("p", Place::Written)
2606 };
2607 let data = Data {
2608 apart: Vec::new(),
2609 exports: Vec::new(),
2610 weak: Vec::new(),
2611 objects: vec![object],
2612 };
2613 let bytes =
2614 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2615 .expect("an object");
2616 let file = object::File::parse(&bytes[..]).expect("a readable object");
2617 let section = file.section_by_name(".data").expect("a data section");
2618 let (_, reloc) = section.relocations().next().expect("the relocation");
2619 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2620 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2621 }
2622
2623 #[test]
2627 fn a_pointer_to_a_variable_elsewhere_is_a_section_the_linker_keeps_one_copy_of() {
2628 let pointer = Object {
2629 bytes: vec![0; 8],
2630 size: 8,
2631 align: 8,
2632 relocs: vec![Reloc {
2633 at: 0,
2634 symbol: "environ".to_owned(),
2635 kind: Reference::Address { bytes: 8 },
2636 addend: 0,
2637 after: 0,
2638 }],
2639 ..variable(".refptr.environ", Place::Pointer)
2640 };
2641 let data = Data { objects: vec![pointer], ..Data::default() };
2642 let bytes =
2643 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2644 .expect("an object");
2645 let file = object::File::parse(&bytes[..]).expect("a readable object");
2646 let section = file.section_by_name(".rdata$.refptr.environ").expect("a section of its own");
2647 let SectionFlags::Coff { characteristics } = section.flags() else {
2648 panic!("a COFF section has COFF flags");
2649 };
2650 let read_only = pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0 | pe::IMAGE_SCN_MEM_READ.0;
2651 let set_apart = 0x00f0_0000 | pe::IMAGE_SCN_LNK_COMDAT.0;
2653 assert_eq!(characteristics.0 & !set_apart, read_only, "{characteristics:#x}");
2654 assert_ne!(characteristics.0 & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "{characteristics:#x}");
2655 let comdat = file.comdats().next().expect("a group the linker picks one copy of");
2656 assert_eq!(comdat.kind(), ComdatKind::Any);
2657 assert_eq!(comdat.name(), Ok(".refptr.environ"));
2658 let (_, reloc) = section.relocations().next().expect("the address it holds");
2659 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 });
2660 }
2661
2662 #[test]
2666 fn a_name_offered_to_other_dlls_is_an_option_to_the_linker() {
2667 let exports = vec![
2668 Export { name: "offered".to_owned(), kind: Offer::Function },
2669 Export { name: "count".to_owned(), kind: Offer::Variable },
2670 Export { name: "kept".to_owned(), kind: Offer::Hidden },
2671 ];
2672 let data = Data { exports, ..Data::default() };
2673 let bytes =
2674 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2675 .expect("an object");
2676 let file = object::File::parse(&bytes[..]).expect("a readable object");
2677 let section = file.section_by_name(".drectve").expect("the options section");
2678 assert_eq!(
2679 section.data().expect("the options"),
2680 b" -export:offered -export:count,data -exclude-symbols:kept"
2681 );
2682 let SectionFlags::Coff { characteristics } = section.flags() else {
2683 panic!("a COFF section has COFF flags");
2684 };
2685 let removed = pe::IMAGE_SCN_LNK_INFO.0 | pe::IMAGE_SCN_LNK_REMOVE.0;
2686 assert_eq!(characteristics.0 & removed, removed, "{characteristics:#x}");
2687
2688 let none = write(
2689 &Text::default(),
2690 &Data::default(),
2691 &[],
2692 &windows(),
2693 Output::default(),
2694 &Info::default(),
2695 )
2696 .expect("an object");
2697 let file = object::File::parse(&none[..]).expect("a readable object");
2698 assert!(file.section_by_name(".drectve").is_none(), "nothing to say is no section");
2699 }
2700
2701 #[test]
2704 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2705 for local in [false, true] {
2706 let data = Data {
2707 apart: Vec::new(),
2708 exports: Vec::new(),
2709 weak: Vec::new(),
2710 objects: vec![variable("p", Place::RelocReadOnly { local })],
2711 };
2712 let bytes = write(
2713 &Text::default(),
2714 &data,
2715 &[],
2716 &windows(),
2717 Output::default(),
2718 &Info::default(),
2719 )
2720 .expect("an object");
2721 let file = object::File::parse(&bytes[..]).expect("a readable object");
2722 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2723 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2724 }
2725 }
2726
2727 #[test]
2730 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2731 let text = calling("puts");
2732 let output = Output { property: Property { features: 3 }, ..Output::default() };
2733 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2734 .expect("an object");
2735 let file = object::File::parse(&bytes[..]).expect("a readable object");
2736 assert!(file.section_by_name(".note.GNU-stack").is_none());
2737 assert!(file.section_by_name(".note.gnu.property").is_none());
2738 }
2739
2740 #[test]
2745 fn what_this_format_cannot_say_is_refused_by_name() {
2746 let ordinary = Text::default();
2747 let empty = Data::default();
2748
2749 let mut thread = Data::default();
2750 thread.objects.push(variable("t", Place::Thread { zero: true }));
2751
2752 let mut gathered = Data::default();
2753 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2754
2755 let mut table = calling("puts");
2756 table.relocs[0].kind = Reference::Got;
2757
2758 let mut room = calling("puts");
2759 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2760
2761 let cases: [(&str, &Text, &Data); 4] = [
2762 ("thread-local", &ordinary, &thread),
2763 ("startup", &ordinary, &gathered),
2764 ("table", &table, &empty),
2765 ("patcher", &room, &empty),
2766 ];
2767 for (what, text, data) in cases {
2768 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2769 .expect_err("something this format cannot write");
2770 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2771 }
2772 }
2773
2774 #[test]
2777 fn a_thread_local_variable_goes_in_the_tls_section() {
2778 let mut thread = Data::default();
2779 thread.objects.push(variable("t", Place::Thread { zero: false }));
2780 let bytes =
2781 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
2782 .expect("an object");
2783 let file = object::File::parse(&bytes[..]).expect("a readable object");
2784 assert!(file.section_by_name(".tls$").is_some());
2785 }
2786
2787 #[test]
2791 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2792 let mut text = calling("puts");
2793 text.funcs[0].visibility = Visibility::Hidden;
2794 let bytes =
2795 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2796 .expect("an object");
2797 let file = object::File::parse(&bytes[..]).expect("a readable object");
2798 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2799 assert!(symbol.is_global(), "a name others may use either way");
2800 }
2801
2802 #[test]
2803 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2804 let text = calling("puts");
2805 let data = Data {
2806 apart: Vec::new(),
2807 exports: Vec::new(),
2808 weak: Vec::new(),
2809 objects: vec![variable("shared", Place::Written)],
2810 };
2811 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2812 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2813 }
2814
2815 #[test]
2819 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2820 let text = calling("puts");
2821 for triple in [
2822 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2823 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2824 ] {
2825 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2826 .expect_err("no writer");
2827 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2828 }
2829 }
2830}