1use std::collections::{BTreeMap, HashMap, HashSet};
34
35use object::write::{
36 Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
37};
38use object::{
39 Architecture, BinaryFormat, 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, Info, Object, Output, Place, Property, Reference, Reloc,
47 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}
64
65impl Flavour {
66 pub(crate) fn of(target: &TargetInfo) -> Option<Flavour> {
68 match target.object_format {
69 ObjectFormat::Elf => Some(Flavour::Elf),
70 ObjectFormat::Coff => Some(Flavour::Coff),
71 ObjectFormat::MachO | ObjectFormat::Wasm => None,
72 }
73 }
74
75 pub(crate) fn binary(self) -> BinaryFormat {
77 match self {
78 Flavour::Elf => BinaryFormat::Elf,
79 Flavour::Coff => BinaryFormat::Coff,
80 }
81 }
82
83 pub(crate) fn reloc(self, reference: Reference, after: u8) -> Option<RelocationFlags> {
88 match self {
89 Flavour::Elf => elf::r_type(reference).map(|r_type| RelocationFlags::Elf { r_type }),
90 Flavour::Coff => coff::reloc(reference, after),
91 }
92 }
93
94 pub(crate) fn see(
100 self,
101 obj: &mut Writer<'_>,
102 id: SymbolId,
103 binding: Binding,
104 visibility: Visibility,
105 ) {
106 match self {
107 Flavour::Elf => elf::see(obj, id, binding, visibility),
108 Flavour::Coff => {}
109 }
110 }
111
112 fn rel_ro_local(self) -> Option<&'static str> {
116 match self {
117 Flavour::Elf => elf::REL_RO_LOCAL,
118 Flavour::Coff => coff::REL_RO_LOCAL,
119 }
120 }
121
122 fn gathered(self, array: Array) -> Option<SectionFlags> {
128 match self {
129 Flavour::Elf => Some(elf::gathered(array)),
130 Flavour::Coff => None,
131 }
132 }
133
134 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
143 match self {
144 Flavour::Elf => {
145 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
146 }
147 Flavour::Coff => None,
148 }
149 }
150
151 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
163 match sort {
164 crate::source::Sort::Func => SymbolKind::Text,
165 crate::source::Sort::Object => SymbolKind::Data,
166 crate::source::Sort::Thread => SymbolKind::Tls,
167 crate::source::Sort::File => SymbolKind::File,
168 crate::source::Sort::Untyped => match (self, binding) {
169 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
170 _ => SymbolKind::Label,
171 },
172 }
173 }
174
175 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
177 match self {
178 Flavour::Elf => elf::marker(obj),
179 Flavour::Coff => coff::marker(obj),
180 }
181 }
182
183 fn property(self, obj: &mut Writer<'_>, property: Property) {
189 if !property.any() {
190 return;
191 }
192 match self {
193 Flavour::Elf => {
194 let note = obj.section_id(StandardSection::GnuProperty);
195 obj.append_section_data(note, &elf::record(property), 8);
196 }
197 Flavour::Coff => {}
198 }
199 }
200
201 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
205 match self {
206 Flavour::Elf => (elf::FRAMES, None),
207 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
208 }
209 }
210
211 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
213 match self {
214 Flavour::Elf => elf::link(bytes, ordered),
215 Flavour::Coff => debug_assert!(ordered.is_empty(), "a record this format cannot write"),
216 }
217 }
218}
219
220#[derive(Debug, Clone, PartialEq, Eq)]
222pub enum Error {
223 Format {
225 triple: String,
227 },
228 Refused {
230 why: String,
232 },
233}
234
235impl std::fmt::Display for Error {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 Error::Format { triple } => {
239 write!(f, "there is no object writer for {triple} in this compiler yet")
240 }
241 Error::Refused { why } => {
242 write!(f, "the object writer refused what it was given: {why}")
243 }
244 }
245 }
246}
247
248impl std::error::Error for Error {}
249
250pub fn write(
266 text: &Text,
267 data: &Data,
268 aliases: &[Alias],
269 target: &TargetInfo,
270 output: Output,
271 info: &Info,
272) -> Result<Vec<u8>, Error> {
273 let Output { sections, property } = output;
274 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
275 let Some(flavour) = flavour else {
276 return Err(Error::Format { triple: target.tuple.to_string() });
277 };
278 if flavour == Flavour::Coff {
279 beyond(text, data, info)?;
280 }
281 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
282 let whole = obj.section_id(StandardSection::Text);
286 if !sections.functions {
287 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
288 }
289
290 let mut symbols = BTreeMap::new();
294 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
299 let mut ordered: Vec<String> = Vec::new();
302 for func in &text.funcs {
303 let ahead = func.patch.map_or(0, |patch| patch.before);
313 let (section, at) = if sections.functions {
314 let name = format!(".text.{}", func.name).into_bytes();
315 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
316 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
317 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
318 (id, ahead as u64)
319 } else {
320 (whole, func.start as u64)
321 };
322 if let Some(patch) = func.patch {
337 let base = if sections.functions { func.start - ahead } else { 0 };
338 let name = elf::PATCHABLE.as_bytes().to_vec();
339 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
340 obj.section_mut(id).flags = elf::ordered();
341 obj.append_section_data(id, &[0; 8], 8);
342 let symbol = obj.section_symbol(section);
343 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
344 Error::Refused { why: "no relocation holds an address here".to_owned() }
345 })?;
346 obj.add_relocation(
347 id,
348 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
349 )
350 .map_err(|why| Error::Refused { why: why.to_string() })?;
351 ordered.push(if sections.functions {
352 format!(".text.{}", func.name)
353 } else {
354 ".text".to_owned()
355 });
356 }
357 let id = obj.add_symbol(Symbol {
358 name: func.name.clone().into_bytes(),
359 value: at,
360 size: func.len as u64,
361 kind: SymbolKind::Text,
362 scope: scope_of(func.binding),
363 weak: func.binding == Binding::Weak,
364 section: SymbolSection::Section(section),
365 flags: SymbolFlags::None,
366 });
367 flavour.see(&mut obj, id, func.binding, func.visibility);
368 symbols.insert(func.name.clone(), id);
369 split.push((section, at));
370 }
371
372 for label in &text.labels {
376 let after = text.funcs.partition_point(|func| func.start <= label.at);
377 let Some(index) = after.checked_sub(1) else {
378 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
379 return Err(Error::Refused { why });
380 };
381 let func = &text.funcs[index];
382 let (section, at) = if sections.functions {
383 let base = func.start - func.patch.map_or(0, |patch| patch.before);
386 (split[index].0, (label.at - base) as u64)
387 } else {
388 (whole, label.at as u64)
389 };
390 let id = obj.add_symbol(Symbol {
391 name: label.name.clone().into_bytes(),
392 value: at,
393 size: 0,
396 kind: SymbolKind::Label,
397 scope: SymbolScope::Compilation,
401 weak: false,
402 section: SymbolSection::Section(section),
403 flags: SymbolFlags::None,
404 });
405 symbols.insert(label.name.clone(), id);
406 }
407
408 let mut placed = Vec::with_capacity(data.objects.len());
413 let mut named = HashMap::new();
417 for object in &data.objects {
418 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
419 let id = obj.add_symbol(Symbol {
420 name: object.name.clone().into_bytes(),
421 value: if object.place == Place::Merged { object.align } else { offset },
424 size: object.size,
425 kind: match object.place {
430 Place::Thread { .. } => SymbolKind::Tls,
431 _ => SymbolKind::Data,
432 },
433 scope: scope_of(object.binding),
434 weak: object.binding == Binding::Weak,
435 section,
436 flags: SymbolFlags::None,
437 });
438 flavour.see(&mut obj, id, object.binding, object.visibility);
439 symbols.insert(object.name.clone(), id);
440 placed.push((section.id(), offset));
441 }
442
443 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
446
447 for apart in &data.apart {
451 let (Some(section), offset) = placed[apart.object] else { continue };
452 let value = distance(&obj, &symbols, apart)?;
453 let bytes = usize::from(apart.bytes);
454 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
455 let at = at + apart.at;
456 let image = obj.section_mut(section).data_mut();
457 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
458 }
459
460 for alias in aliases {
466 let Some(&id) = symbols.get(&alias.target) else {
467 let why =
468 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
469 return Err(Error::Refused { why });
470 };
471 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
472 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
473 let id = obj.add_symbol(Symbol {
474 name: alias.name.clone().into_bytes(),
475 value,
476 size,
477 kind,
478 scope: scope_of(alias.binding),
479 weak: alias.binding == Binding::Weak,
480 section,
481 flags: SymbolFlags::None,
482 });
483 flavour.see(&mut obj, id, alias.binding, alias.visibility);
484 symbols.insert(alias.name.clone(), id);
485 }
486
487 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
494 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
495 let thread: HashSet<&str> = relocs()
500 .filter(|reloc| reloc.kind == Reference::Thread)
501 .map(|reloc| reloc.symbol.as_str())
502 .collect();
503 let wanted: Vec<&String> =
504 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
505 for name in wanted {
506 if symbols.contains_key(name) || tables.contains_key(name) {
507 continue;
508 }
509 let id = obj.add_symbol(Symbol {
510 name: name.clone().into_bytes(),
511 value: 0,
512 size: 0,
513 kind: if thread.contains(name.as_str()) {
523 SymbolKind::Tls
524 } else {
525 SymbolKind::Unknown
526 },
527 scope: SymbolScope::Dynamic,
528 weak: weak.contains(name.as_str()),
529 section: SymbolSection::Undefined,
530 flags: SymbolFlags::None,
531 });
532 symbols.insert(name.clone(), id);
533 }
534
535 for reloc in &text.relocs {
536 let (section, at) = if sections.functions {
541 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
542 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
543 let why = format!("a relocation at {} is in front of every function", reloc.at);
544 return Err(Error::Refused { why });
545 };
546 let base = func.start - func.patch.map_or(0, |patch| patch.before);
549 (split[after - 1].0, (reloc.at - base) as u64)
550 } else {
551 (whole, reloc.at as u64)
552 };
553 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
557 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
558 why: format!("no relocation is {:?}", reloc.kind),
559 })?;
560 let symbol = obj.section_symbol(table);
561 let addend = reloc.addend + offset as i64;
562 obj.add_relocation(section, Relocation { offset: at, symbol, addend, flags })
563 .map_err(|why| Error::Refused { why: why.to_string() })?;
564 continue;
565 }
566 add(&mut obj, section, at, reloc, &symbols, flavour)?;
567 }
568
569 if !text.unwind.bytes.is_empty() {
573 let ((name, align), second) = flavour.tables();
574 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
575 obj.append_section_data(frames, &text.unwind.bytes, align);
576 let mut described = HashMap::new();
581 if !text.unwind.info.is_empty() {
582 let Some((name, align)) = second else {
583 let why = "an unwind table here is one section and it was given two".to_owned();
584 return Err(Error::Refused { why });
585 };
586 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
587 obj.append_section_data(codes, &text.unwind.info, align);
588 for label in &text.unwind.labels {
589 let id = obj.add_symbol(Symbol {
590 name: label.name.clone().into_bytes(),
591 value: label.at as u64,
592 size: 0,
593 kind: SymbolKind::Label,
594 scope: SymbolScope::Compilation,
595 weak: false,
596 section: SymbolSection::Section(codes),
597 flags: SymbolFlags::None,
598 });
599 described.insert(label.name.clone(), id);
600 }
601 }
602 for reloc in &text.unwind.relocs {
603 let (symbol, addend) = match described.get(&reloc.symbol) {
604 Some(&id) => (id, reloc.addend),
608 None => {
622 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
623 let Some((section, at)) = found.map(|i| split[i]) else {
624 let why = format!(
625 "'{}' has an unwind record and is not a function here",
626 reloc.symbol
627 );
628 return Err(Error::Refused { why });
629 };
630 (obj.section_symbol(section), reloc.addend + at as i64)
634 }
635 };
636 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
637 why: format!("no relocation is {:?}", reloc.kind),
638 })?;
639 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
640 obj.add_relocation(frames, record)
641 .map_err(|why| Error::Refused { why: why.to_string() })?;
642 }
643 }
644 let mut named = HashMap::new();
653 for chunk in &info.chunks {
654 let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
655 obj.append_section_data(id, &chunk.bytes, 1);
656 named.insert(chunk.name.as_str(), id);
657 }
658 for chunk in &info.chunks {
659 let section = named[chunk.name.as_str()];
660 for reloc in &chunk.relocs {
661 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
662 Some(&id) => (obj.section_symbol(id), reloc.addend),
665 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
670 Some(which) => {
671 let (section, at) = split[which];
672 (obj.section_symbol(section), reloc.addend + at as i64)
673 }
674 None => {
680 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
681 let Some(which) = found else {
682 let why = format!(
683 "'{}' is named by the debug information and is not defined here",
684 reloc.symbol
685 );
686 return Err(Error::Refused { why });
687 };
688 match placed[which] {
689 (Some(section), at) => {
690 (obj.section_symbol(section), reloc.addend + at as i64)
691 }
692 (None, _) => (symbols[&reloc.symbol], reloc.addend),
693 }
694 }
695 },
696 };
697 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
698 why: format!("no relocation is {:?}", reloc.kind),
699 })?;
700 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
701 obj.add_relocation(section, record)
702 .map_err(|why| Error::Refused { why: why.to_string() })?;
703 }
704 }
705 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
706 let Some(section) = section else { continue };
707 for reloc in &object.relocs {
708 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
709 }
710 }
711
712 flavour.property(&mut obj, property);
716
717 flavour.marker(&mut obj);
720
721 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
722 flavour.finish(&mut bytes, &ordered);
723 Ok(bytes)
724}
725
726fn distance(
733 obj: &Writer<'_>,
734 symbols: &BTreeMap<String, SymbolId>,
735 apart: &Apart,
736) -> Result<i64, Error> {
737 let find = |name: &str| match symbols.get(name) {
738 Some(&id) => Ok(obj.symbol(id)),
739 None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
740 };
741 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
742 if to.section != from.section {
743 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
744 return Err(Error::Refused { why });
745 }
746 let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
747 let bits = u32::from(apart.bytes) * 8;
748 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
749 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
750 return Err(Error::Refused { why });
751 }
752 Ok(value)
753}
754
755fn beyond(text: &Text, data: &Data, info: &Info) -> Result<(), Error> {
768 let why = |why: String| Err(Error::Refused { why });
769 if !info.chunks.is_empty() {
770 return why("debug information here goes in sections this writer does not name".to_owned());
771 }
772 if text.funcs.iter().any(|func| func.patch.is_some()) {
773 return why("a record of where a patcher's room is has no section flags here".to_owned());
774 }
775 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
776 if matches!(reloc.kind, Reference::Got | Reference::Thread) {
777 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
778 }
779 }
780 for object in &data.objects {
781 if matches!(object.place, Place::Thread { .. }) {
782 return why(format!("'{}' is thread-local and this format is not", object.name));
783 }
784 let Place::Named(name) = &object.place else { continue };
785 if Array::of(name).is_some() {
786 return why(format!("'{name}' is not a list the startup code here gathers"));
787 }
788 }
789 Ok(())
790}
791
792pub fn defines(
816 text: &Text,
817 data: &Data,
818 aliases: &[Alias],
819 target: &TargetInfo,
820) -> Result<Vec<String>, Error> {
821 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
822 return Err(Error::Format { triple: target.tuple.to_string() });
823 }
824 let names = text
825 .funcs
826 .iter()
827 .filter(|func| func.binding != Binding::Local)
828 .map(|func| func.name.clone())
829 .chain(
830 data.objects
831 .iter()
832 .filter(|object| object.binding != Binding::Local)
833 .map(|object| object.name.clone()),
834 )
835 .chain(
836 aliases
837 .iter()
838 .filter(|alias| alias.binding != Binding::Local)
839 .map(|alias| alias.name.clone()),
840 )
841 .collect();
842 Ok(names)
843}
844
845fn put(
852 obj: &mut Writer<'_>,
853 object: &Object,
854 named: &mut HashMap<String, object::write::SectionId>,
855 sections: Sections,
856 flavour: Flavour,
857) -> (SymbolSection, u64) {
858 if sections.data {
864 if let Some(name) = object.place.split(&object.name) {
865 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
866 let offset = if carries_no_bytes(&object.place) {
867 obj.append_section_bss(section, object.size, object.align)
868 } else {
869 obj.append_section_data(section, &object.bytes, object.align)
870 };
871 return (SymbolSection::Section(section), offset);
872 }
873 }
874 let section = match &object.place {
875 Place::Written => obj.section_id(StandardSection::Data),
876 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
877 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
883 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
884 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
885 },
886 Place::Zero => obj.section_id(StandardSection::UninitializedData),
887 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
888 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
889 Place::Merged => return (SymbolSection::Common, 0),
890 Place::Named(name) => {
896 let section = made(obj, named, name, SectionKind::Data);
897 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
898 obj.section_mut(section).flags = flags;
899 }
900 section
901 }
902 };
903 let offset = if carries_no_bytes(&object.place) {
904 obj.append_section_bss(section, object.size, object.align)
905 } else {
906 obj.append_section_data(section, &object.bytes, object.align)
907 };
908 (SymbolSection::Section(section), offset)
909}
910
911fn tables(
924 obj: &mut Writer<'_>,
925 text: &Text,
926 split: &[(object::write::SectionId, u64)],
927 named: &mut HashMap<String, object::write::SectionId>,
928 sections: Sections,
929 flavour: Flavour,
930) -> Result<HashMap<String, (object::write::SectionId, u64)>, Error> {
931 let mut placed = HashMap::new();
932 if text.tables.is_empty() {
933 return Ok(placed);
934 }
935 if flavour != Flavour::Elf {
936 let why = "a jump table outside the code is written on ELF only".to_owned();
937 return Err(Error::Refused { why });
938 }
939 let flags = flavour.reloc(Reference::Away, 0).ok_or_else(|| Error::Refused {
940 why: "no relocation is a distance from where it is written".to_owned(),
941 })?;
942 for table in &text.tables {
943 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
944 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
945 })?;
946 let section = if sections.data {
947 let name = format!(".rodata.{}", func.name);
948 made(obj, named, &name, SectionKind::ReadOnlyData)
949 } else {
950 obj.section_id(StandardSection::ReadOnlyData)
951 };
952 let offset = obj.append_section_data(section, &vec![0; 4 * table.cells.len()], 4);
953 placed.insert(table.name.clone(), (section, offset));
954 let (code, at) = split[table.func];
955 let symbol = obj.section_symbol(code);
956 for (index, &cell) in table.cells.iter().enumerate() {
957 let place = 4 * index as u64;
958 let addend = at as i64 + cell as i64 + place as i64;
959 let record = Relocation { offset: offset + place, symbol, addend, flags };
960 obj.add_relocation(section, record)
961 .map_err(|why| Error::Refused { why: why.to_string() })?;
962 }
963 }
964 Ok(placed)
965}
966
967fn carries_no_bytes(place: &Place) -> bool {
973 matches!(place, Place::Zero | Place::Thread { zero: true })
974}
975
976fn made(
984 obj: &mut Writer<'_>,
985 named: &mut HashMap<String, object::write::SectionId>,
986 name: &str,
987 kind: SectionKind,
988) -> object::write::SectionId {
989 if let Some(section) = named.get(name) {
990 return *section;
991 }
992 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
993 named.insert(name.to_owned(), section);
994 section
995}
996
997fn kind_of(place: &Place) -> SectionKind {
1006 match place {
1007 Place::ReadOnly => SectionKind::ReadOnlyData,
1008 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1009 Place::Zero => SectionKind::UninitializedData,
1010 Place::Thread { zero: false } => SectionKind::Tls,
1011 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1012 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
1013 }
1014}
1015
1016fn add(
1023 obj: &mut Writer<'_>,
1024 section: object::write::SectionId,
1025 at: u64,
1026 reloc: &Reloc,
1027 symbols: &BTreeMap<String, SymbolId>,
1028 flavour: Flavour,
1029) -> Result<(), Error> {
1030 let flags = flavour
1031 .reloc(reloc.kind, reloc.after)
1032 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1033 obj.add_relocation(
1034 section,
1035 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1036 )
1037 .map_err(|why| Error::Refused { why: why.to_string() })
1038}
1039
1040pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1053 match binding {
1054 Binding::Local => SymbolScope::Compilation,
1055 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1056 }
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061 use super::*;
1062
1063 use object::read::elf::Sym as _;
1064 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
1065 use object::{elf, pe};
1066 use rucc_target::{Arch, Env, Os, Triple};
1067
1068 use crate::elf::PATCHABLE;
1069 use crate::section::{Extent, Marker, Patch, Reloc};
1070
1071 fn target() -> TargetInfo {
1073 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1074 }
1075
1076 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1081 Extent {
1082 name,
1083 start,
1084 len,
1085 align: crate::FUNC_ALIGN,
1086 binding,
1087 visibility: Visibility::Default,
1088 patch: None,
1089 }
1090 }
1091
1092 fn calling(name: &str) -> Text {
1094 Text {
1095 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1096 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1097 relocs: vec![Reloc {
1098 at: 1,
1099 symbol: name.to_owned(),
1100 kind: Reference::Call,
1101 addend: -4,
1102 after: 0,
1103 }],
1104 ..Text::default()
1105 }
1106 }
1107
1108 #[test]
1109 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1110 let text = calling("puts");
1111 let bytes =
1112 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1113 .expect("an object");
1114 let file = object::File::parse(&bytes[..]).expect("a readable object");
1115 let section = file.section_by_name(".text").expect("a text section");
1116 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1117 }
1118
1119 #[test]
1120 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1121 let mut text = calling("puts");
1122 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1123 text.bytes.resize(17, 0x90);
1124 let bytes =
1125 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1126 .expect("an object");
1127 let file = object::File::parse(&bytes[..]).expect("a readable object");
1128 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1129 assert_eq!(g.address(), 16);
1130 assert_eq!(g.size(), 1);
1131 assert_eq!(g.kind(), SymbolKind::Text);
1132 assert!(g.is_global(), "nothing said otherwise about this one");
1133 }
1134
1135 #[test]
1136 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1137 let mut text = calling("puts");
1138 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1139 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1140 text.bytes.resize(33, 0x90);
1141 let bytes =
1142 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1143 .expect("an object");
1144 let file = object::File::parse(&bytes[..]).expect("a readable object");
1145 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1146 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1149 assert!(!hidden.is_weak());
1150 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1151 assert!(shared.is_weak(), "a weak function has to be able to lose");
1152 assert!(shared.is_global());
1153 }
1154
1155 #[test]
1172 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1173 let mut text = calling("puts");
1174 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1175 text.funcs[0].start = 3;
1176 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1177 text.relocs[0].at = 4;
1178 let bytes =
1179 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1180 .expect("an object");
1181 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1182 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1183 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1184 assert_eq!(section.align(), 8);
1185 let header = section.elf_section_header();
1186 assert_eq!(
1187 header.sh_flags.get(Endianness::Little),
1188 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1189 );
1190 let index = file.section_by_name(".text").expect("a text section").index().0;
1193 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1194 assert_ne!(index, 0);
1195
1196 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1198 panic!("one address in the record")
1199 };
1200 assert_eq!(*at, 0);
1201 assert_eq!(reloc.addend(), 0);
1202 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1203 }
1204
1205 #[test]
1207 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1208 let text = calling("puts");
1209 let bytes =
1210 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1211 .expect("an object");
1212 let file = object::File::parse(&bytes[..]).expect("a readable object");
1213 assert!(file.section_by_name(PATCHABLE).is_none());
1214 }
1215
1216 #[test]
1222 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1223 let mut text = calling("puts");
1224 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1225 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1226 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1227 text.bytes.resize(17, 0x90);
1228 let output =
1229 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1230 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1231 .expect("an object");
1232 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1233 let links: Vec<usize> = file
1234 .sections()
1235 .filter(|section| section.name() == Ok(PATCHABLE))
1236 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1237 .collect();
1238 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1239 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1240 }
1241
1242 #[test]
1243 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1244 let mut text = calling("puts");
1245 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1246 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1247 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1248 text.bytes.resize(49, 0x90);
1249 let bytes =
1250 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1251 .expect("an object");
1252 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1253 let visibility = |name: &str| {
1254 file.symbols()
1255 .find(|s| s.name() == Ok(name))
1256 .expect("the function")
1257 .elf_symbol()
1258 .st_visibility()
1259 };
1260 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1262 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1263 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1266 }
1267
1268 #[test]
1278 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1279 let mut text = calling("puts");
1280 for (index, (name, seen)) in
1281 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1282 {
1283 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1284 func.visibility = seen;
1285 text.funcs.push(func);
1286 }
1287 text.bytes.resize(49, 0x90);
1288 let mut data = Data::default();
1289 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1290 let mut object = variable(name, Place::Written);
1291 object.visibility = seen;
1292 data.objects.push(object);
1293 }
1294 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1295 .expect("an object");
1296 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1297 let visibility = |name: &str| {
1298 file.symbols()
1299 .find(|s| s.name() == Ok(name))
1300 .expect("the symbol")
1301 .elf_symbol()
1302 .st_visibility()
1303 };
1304 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1305 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1306 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1307 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1308 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1311 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1312 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1313 }
1314
1315 #[test]
1316 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1317 let bytes = write(
1318 &calling("puts"),
1319 &Data::default(),
1320 &[],
1321 &target(),
1322 Output::default(),
1323 &Info::default(),
1324 )
1325 .expect("an object");
1326 let file = object::File::parse(&bytes[..]).expect("a readable object");
1327 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1328 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1329 }
1330
1331 #[test]
1332 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1333 for (reference, wanted) in [
1334 (Reference::Call, elf::R_X86_64_PLT32),
1335 (Reference::Data, elf::R_X86_64_PC32),
1336 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1337 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1338 ] {
1339 let mut text = calling("puts");
1340 text.relocs[0].kind = reference;
1341 let bytes =
1342 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1343 .expect("an object");
1344 let file = object::File::parse(&bytes[..]).expect("a readable object");
1345 let section = file.section_by_name(".text").expect("a text section");
1346 let (offset, reloc) = section.relocations().next().expect("one relocation");
1347 assert_eq!(offset, 1);
1348 assert_eq!(reloc.addend(), -4);
1349 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1350 }
1351 }
1352
1353 #[test]
1354 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1355 let mut text = calling("puts");
1356 text.relocs.push(Reloc {
1357 at: 1,
1358 symbol: "puts".to_owned(),
1359 kind: Reference::Call,
1360 addend: -4,
1361 after: 0,
1362 });
1363 let bytes =
1364 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1365 .expect("an object");
1366 let file = object::File::parse(&bytes[..]).expect("a readable object");
1367 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1368 }
1369
1370 #[test]
1371 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1372 let text = calling("f");
1373 let bytes =
1374 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1375 .expect("an object");
1376 let file = object::File::parse(&bytes[..]).expect("a readable object");
1377 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1378 let f = found.next().expect("the function");
1379 assert!(!f.is_undefined(), "the file defines it");
1380 assert!(found.next().is_none(), "and defines it once");
1381 }
1382
1383 #[test]
1384 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1385 let bytes = write(
1386 &calling("puts"),
1387 &Data::default(),
1388 &[],
1389 &target(),
1390 Output::default(),
1391 &Info::default(),
1392 )
1393 .expect("an object");
1394 let file = object::File::parse(&bytes[..]).expect("a readable object");
1395 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1396 assert!(note.data().expect("no bytes").is_empty());
1397 }
1398
1399 #[test]
1406 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1407 let property = Property { features: Property::IBT | Property::SHSTK };
1408 let output = Output { property, ..Output::default() };
1409 let bytes =
1410 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1411 .expect("an object");
1412 let file = object::File::parse(&bytes[..]).expect("a readable object");
1413 let note = file.section_by_name(".note.gnu.property").expect("the note");
1414 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1415 let want: Vec<u8> = [
1416 4u32,
1417 16,
1418 5,
1419 u32::from_le_bytes(*b"GNU\0"),
1420 Property::X86_FEATURES,
1421 4,
1422 Property::IBT | Property::SHSTK,
1423 0,
1424 ]
1425 .iter()
1426 .flat_map(|word| word.to_le_bytes())
1427 .collect();
1428 assert_eq!(note.data().expect("the bytes"), &want[..]);
1429 }
1430
1431 #[test]
1437 fn a_file_built_to_have_nothing_checked_says_nothing() {
1438 let bytes = write(
1439 &calling("puts"),
1440 &Data::default(),
1441 &[],
1442 &target(),
1443 Output::default(),
1444 &Info::default(),
1445 )
1446 .expect("an object");
1447 let file = object::File::parse(&bytes[..]).expect("a readable object");
1448 assert!(file.section_by_name(".note.gnu.property").is_none());
1449 }
1450
1451 #[test]
1460 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1461 let mut text = calling("puts");
1462 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1463 text.bytes.resize(17, 0x90);
1464 text.unwind.bytes = vec![0; 64];
1467 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1468 text.unwind.relocs.push(Reloc {
1469 at,
1470 symbol: name.to_owned(),
1471 kind: Reference::Address { bytes: 8 },
1472 addend: 0,
1473 after: 0,
1474 });
1475 }
1476 let bytes =
1477 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1478 .expect("an object");
1479 let file = object::File::parse(&bytes[..]).expect("a readable object");
1480 let mut found = points_at(&file);
1481 found.sort_unstable();
1482 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1483 }
1484
1485 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1488 let frames = file.section_by_name(".eh_frame").expect("the table");
1489 frames
1490 .relocations()
1491 .map(|(offset, reloc)| {
1492 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1493 panic!("a record points at something that is not a symbol");
1494 };
1495 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1496 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1497 let section = symbol.section_index().expect("a section symbol is in one");
1498 let name = file.section_by_index(section).expect("a readable section");
1499 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1500 })
1501 .collect()
1502 }
1503
1504 #[test]
1517 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1518 let mut text = two();
1519 text.unwind.bytes = vec![0; 64];
1520 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1521 text.unwind.relocs.push(Reloc {
1522 at,
1523 symbol: name.to_owned(),
1524 kind: Reference::Data,
1525 addend: 0,
1526 after: 0,
1527 });
1528 }
1529 let bytes =
1530 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1531 .expect("an object");
1532 let file = object::File::parse(&bytes[..]).expect("a readable object");
1533 let mut whole = points_at(&file);
1534 whole.sort_unstable();
1535 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1536
1537 let sections =
1538 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1539 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1540 .expect("an object");
1541 let file = object::File::parse(&bytes[..]).expect("a readable object");
1542 let mut split = points_at(&file);
1543 split.sort_unstable();
1544 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1545 }
1546
1547 #[test]
1554 fn a_record_about_something_this_file_does_not_define_is_refused() {
1555 let mut text = calling("puts");
1556 text.unwind.bytes = vec![0; 64];
1557 text.unwind.relocs.push(Reloc {
1558 at: 32,
1559 symbol: "puts".to_owned(),
1560 kind: Reference::Data,
1561 addend: 0,
1562 after: 0,
1563 });
1564 let why =
1565 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1566 .expect_err("a record about a name from somewhere else");
1567 assert!(why.to_string().contains("puts"), "{why}");
1568 }
1569
1570 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1572 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1573 let index = symbol.section_index().expect("a section to be defined in");
1574 let section = file.section_by_index(index).expect("a readable section");
1575 section.name().expect("a named section").to_owned()
1576 }
1577
1578 fn two() -> Text {
1580 let mut text = calling("puts");
1581 text.bytes.resize(16, 0x90);
1584 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1585 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1586 text.relocs.push(Reloc {
1587 at: 17,
1588 symbol: "puts".to_owned(),
1589 kind: Reference::Call,
1590 addend: -4,
1591 after: 0,
1592 });
1593 text
1594 }
1595
1596 #[test]
1603 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1604 let sections =
1605 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1606 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1607 .expect("an object");
1608 let file = object::File::parse(&bytes[..]).expect("a readable object");
1609 assert_eq!(lives_in(&file, "f"), ".text.f");
1610 assert_eq!(lives_in(&file, "g"), ".text.g");
1611 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1612 for name in ["f", "g"] {
1615 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1616 assert_eq!(symbol.address(), 0, "{name}");
1617 assert_eq!(symbol.size(), 6, "{name}");
1618 }
1619 let section = file.section_by_name(".text.g").expect("the second function");
1620 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1621 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1624 }
1625
1626 #[test]
1632 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1633 let sections =
1634 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1635 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1636 .expect("an object");
1637 let file = object::File::parse(&bytes[..]).expect("a readable object");
1638 for name in [".text.f", ".text.g"] {
1639 let section = file.section_by_name(name).expect("a function");
1640 let (offset, _) = section.relocations().next().expect("the call in it");
1641 assert_eq!(offset, 1, "{name}");
1644 assert_eq!(section.relocations().count(), 1, "{name}");
1645 }
1646 }
1647
1648 fn switching() -> Text {
1650 let mut text = two();
1651 let name = ".Lg_j0".to_owned();
1652 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5] });
1653 text
1654 }
1655
1656 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1658 let section = file.section_by_name(section).expect("the table's section");
1659 section
1660 .relocations()
1661 .map(|(offset, reloc)| {
1662 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1663 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1664 panic!("a cell against something that is not a symbol");
1665 };
1666 let symbol = file.symbol_by_index(index).expect("a symbol");
1667 assert_eq!(symbol.kind(), SymbolKind::Section);
1668 let at = symbol.section_index().expect("a section symbol is in one");
1669 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1670 (offset, name.to_owned(), reloc.addend())
1671 })
1672 .collect()
1673 }
1674
1675 #[test]
1676 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1677 let mut text = switching();
1680 text.relocs[1].symbol = ".Lg_j0".to_owned();
1681 text.relocs[1].kind = Reference::Data;
1682 let bytes =
1683 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1684 .expect("an object");
1685 let file = object::File::parse(&bytes[..]).expect("a readable object");
1686 let rodata = file.section_by_name(".rodata").expect("the table's section");
1687 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
1688 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
1689 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
1690 let (at, reloc) = file
1691 .section_by_name(".text")
1692 .expect("the code")
1693 .relocations()
1694 .find(|(at, _)| *at == 17)
1695 .expect("the reference to the table");
1696 assert_eq!((at, reloc.addend()), (17, -4));
1697 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1698 panic!("a reference against something that is not a symbol");
1699 };
1700 let symbol = file.symbol_by_index(index).expect("a symbol");
1701 assert_eq!(symbol.section_index(), Some(rodata.index()));
1702 assert_eq!(symbol.kind(), SymbolKind::Section);
1703 assert_eq!(
1706 cells(&file, ".rodata"),
1707 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
1708 );
1709 }
1710
1711 #[test]
1712 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
1713 let sections =
1714 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
1715 let bytes =
1716 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
1717 .expect("an object");
1718 let file = object::File::parse(&bytes[..]).expect("a readable object");
1719 assert_eq!(
1721 cells(&file, ".rodata.g"),
1722 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
1723 );
1724 }
1725
1726 #[test]
1727 fn a_jump_table_outside_the_code_is_refused_on_windows() {
1728 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1729 let written = write(
1730 &switching(),
1731 &Data::default(),
1732 &[],
1733 &target,
1734 Output::default(),
1735 &Info::default(),
1736 );
1737 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
1738 }
1739
1740 fn variable(name: &str, place: Place) -> Object {
1742 Object {
1743 name: name.to_owned(),
1744 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1745 size: 4,
1746 align: 4,
1747 place,
1748 binding: Binding::Global,
1749 visibility: Visibility::Default,
1750 relocs: Vec::new(),
1751 }
1752 }
1753
1754 fn measured() -> (Text, Data) {
1756 let mut text = calling("puts");
1757 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1758 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1759 let mut table = variable("table", Place::ReadOnly);
1760 table.bytes = vec![0; 8];
1761 table.size = 8;
1762 let apart = |at, to: &str, from: &str| Apart {
1763 object: 0,
1764 at,
1765 to: to.to_owned(),
1766 from: from.to_owned(),
1767 addend: 0,
1768 bytes: 4,
1769 };
1770 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1771 (text, Data { apart, weak: Vec::new(), objects: vec![table] })
1772 }
1773
1774 #[test]
1775 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1776 let (text, data) = measured();
1777 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1778 .expect("an object");
1779 let file = object::File::parse(&bytes[..]).expect("a readable object");
1780 let section = file.section_by_name(".rodata").expect("a read only section");
1781 assert_eq!(section.relocations().count(), 0);
1782 let image = section.data().expect("the image");
1783 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1784 }
1785
1786 #[test]
1787 fn a_distance_between_labels_in_two_sections_is_refused() {
1788 let (mut text, data) = measured();
1791 text.bytes.resize(22, 0x90);
1792 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1793 text.labels[1].at = 17;
1794 let output =
1795 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1796 let refused = write(&text, &data, &[], &target(), output, &Info::default());
1797 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1798 }
1799
1800 fn holding(object: Object) -> Vec<u8> {
1802 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
1803 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1804 .expect("an object")
1805 }
1806
1807 #[test]
1808 fn what_a_variable_is_decides_which_section_it_goes_in() {
1809 for (place, wanted) in [
1810 (Place::Written, ".data"),
1811 (Place::ReadOnly, ".rodata"),
1812 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1813 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1814 (Place::Zero, ".bss"),
1815 (Place::Thread { zero: false }, ".tdata"),
1816 (Place::Thread { zero: true }, ".tbss"),
1817 (Place::Named(".init_array".to_owned()), ".init_array"),
1818 ] {
1819 let bytes = holding(variable("x", place.clone()));
1820 let file = object::File::parse(&bytes[..]).expect("a readable object");
1821 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1822 assert_eq!(section.size(), 4, "{place:?}");
1823 let carried = section.data().expect("the bytes").len();
1826 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1827 }
1828 }
1829
1830 #[test]
1836 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1837 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1838 let bytes = holding(variable("counter", place.clone()));
1839 let file = object::File::parse(&bytes[..]).expect("a readable object");
1840 let symbol = file
1841 .symbols()
1842 .find(|symbol| symbol.name() == Ok("counter"))
1843 .unwrap_or_else(|| panic!("{place:?}"));
1844 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1845 }
1846 }
1847
1848 #[test]
1854 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1855 for (name, wanted) in [
1856 (".init_array", elf::SHT_INIT_ARRAY),
1857 (".init_array.00101", elf::SHT_INIT_ARRAY),
1858 (".fini_array", elf::SHT_FINI_ARRAY),
1859 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1860 (".init_arrays", elf::SHT_PROGBITS),
1861 ] {
1862 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1863 let file = object::File::parse(&bytes[..]).expect("a readable object");
1864 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1865 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1866 panic!("{name} is not an elf section");
1867 };
1868 assert_eq!(sh_type, wanted, "{name}");
1869 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1870 }
1871 }
1872
1873 #[test]
1879 fn two_variables_in_one_named_section_share_it() {
1880 let objects = vec![
1881 variable("x", Place::Named(".init_array".to_owned())),
1882 variable("y", Place::Named(".init_array".to_owned())),
1883 ];
1884 let data = Data { apart: Vec::new(), weak: Vec::new(), objects };
1885 let bytes =
1886 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1887 .expect("an object");
1888 let file = object::File::parse(&bytes[..]).expect("a readable object");
1889 let named: Vec<_> =
1890 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1891 assert_eq!(named.len(), 1);
1892 assert_eq!(named[0].size(), 8);
1893 }
1894
1895 #[test]
1899 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1900 let sections =
1901 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1902 for (place, wanted) in [
1903 (Place::Written, ".data.x"),
1904 (Place::ReadOnly, ".rodata.x"),
1905 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1906 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1907 (Place::Zero, ".bss.x"),
1908 (Place::Thread { zero: false }, ".tdata.x"),
1909 (Place::Thread { zero: true }, ".tbss.x"),
1910 ] {
1911 let data = Data {
1912 apart: Vec::new(),
1913 weak: Vec::new(),
1914 objects: vec![variable("x", place.clone())],
1915 };
1916 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1917 .expect("object");
1918 let file = object::File::parse(&bytes[..]).expect("a readable object");
1919 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1920 let section = file.section_by_name(wanted).expect("the section it named");
1921 assert_eq!(section.size(), 4, "{place:?}");
1922 let carried = section.data().expect("the bytes").len();
1925 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1926 }
1927 }
1928
1929 #[test]
1933 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1934 let sections =
1935 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1936 let named = Place::Named(".init_array".to_owned());
1937 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1938 let bytes = write(
1939 &Text::default(),
1940 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1941 &[],
1942 &target(),
1943 sections,
1944 &Info::default(),
1945 )
1946 .expect("object");
1947 let file = object::File::parse(&bytes[..]).expect("a readable object");
1948 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1949 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1950 assert_eq!(lives_in(&file, "n"), ".init_array");
1951 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1952 }
1953
1954 #[test]
1958 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1959 let sections =
1960 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1961 let pointer = Object {
1962 bytes: vec![0; 8],
1963 size: 8,
1964 align: 8,
1965 relocs: vec![Reloc {
1966 at: 0,
1967 symbol: "y".to_owned(),
1968 kind: Reference::Address { bytes: 8 },
1969 addend: 0,
1970 after: 0,
1971 }],
1972 ..variable("p", Place::Written)
1973 };
1974 let objects = vec![variable("first", Place::Written), pointer];
1975 let bytes = write(
1976 &Text::default(),
1977 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1978 &[],
1979 &target(),
1980 sections,
1981 &Info::default(),
1982 )
1983 .expect("object");
1984 let file = object::File::parse(&bytes[..]).expect("a readable object");
1985 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1986 let (offset, reloc) = section.relocations().next().expect("one relocation");
1987 assert_eq!(offset, 0);
1990 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1991 }
1992
1993 #[test]
2001 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2002 let place = Place::RelocReadOnly { local: true };
2003 let data = Data {
2004 apart: Vec::new(),
2005 weak: Vec::new(),
2006 objects: vec![variable("first", place.clone()), variable("second", place)],
2007 };
2008 let bytes =
2009 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2010 .expect("an object");
2011 let file = object::File::parse(&bytes[..]).expect("a readable object");
2012 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2013 assert_eq!(named, 1, "one section holding both, not one each");
2014 }
2015
2016 #[test]
2017 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2018 let mut data = Data {
2019 apart: Vec::new(),
2020 weak: Vec::new(),
2021 objects: vec![variable("first", Place::Written)],
2022 };
2023 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2024 let bytes =
2025 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2026 .expect("an object");
2027 let file = object::File::parse(&bytes[..]).expect("a readable object");
2028 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2029 assert_eq!(second.kind(), SymbolKind::Data);
2030 assert_eq!(second.size(), 4);
2031 assert_eq!(second.address(), 16);
2035 }
2036
2037 #[test]
2038 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2039 for (binding, global, weak) in [
2040 (Binding::Global, true, false),
2041 (Binding::Local, false, false),
2042 (Binding::Weak, true, true),
2043 ] {
2044 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2045 let file = object::File::parse(&bytes[..]).expect("a readable object");
2046 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2047 assert_eq!(x.is_global(), global, "{binding:?}");
2048 assert_eq!(x.is_weak(), weak, "{binding:?}");
2049 }
2050 }
2051
2052 #[test]
2053 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2054 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2055 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2056 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2057 assert!(x.is_common(), "the linker merges every definition of this name into one");
2058 assert_eq!(x.size(), 4);
2059 assert_eq!(x.address(), 0);
2063 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2064 }
2065
2066 #[test]
2067 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2068 let object = Object {
2069 bytes: vec![0; 8],
2070 size: 8,
2071 align: 8,
2072 relocs: vec![Reloc {
2073 at: 0,
2074 symbol: "y".to_owned(),
2075 kind: Reference::Address { bytes: 8 },
2076 addend: 16,
2077 after: 0,
2078 }],
2079 ..variable("p", Place::Written)
2080 };
2081 let bytes = holding(object);
2082 let file = object::File::parse(&bytes[..]).expect("a readable object");
2083 let section = file.section_by_name(".data").expect("a data section");
2084 let (offset, reloc) = section.relocations().next().expect("one relocation");
2085 assert_eq!(offset, 0);
2086 assert_eq!(reloc.addend(), 16);
2087 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2088 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2089 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2090 }
2091
2092 #[test]
2099 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2100 let mut text = Text::default();
2101 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2102 text.bytes.resize(8, 0x90);
2103 text.relocs.push(Reloc {
2104 at: 1,
2105 symbol: "hook".to_owned(),
2106 kind: Reference::Call,
2107 addend: -4,
2108 after: 0,
2109 });
2110 let data = Data {
2111 apart: Vec::new(),
2112 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2113 objects: vec![],
2114 };
2115 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2116 .expect("an object");
2117 let file = object::File::parse(&bytes[..]).expect("a readable object");
2118
2119 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2120 assert!(hook.is_undefined(), "nothing here defines it");
2121 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2122
2123 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2127 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2128 }
2129
2130 #[test]
2145 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2146 let mut text = Text::default();
2147 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2148 text.bytes.resize(16, 0x90);
2149 text.relocs.push(Reloc {
2150 at: 3,
2151 symbol: "flags".to_owned(),
2152 kind: Reference::Thread,
2153 addend: -4,
2154 after: 0,
2155 });
2156 text.relocs.push(Reloc {
2159 at: 10,
2160 symbol: "shared".to_owned(),
2161 kind: Reference::Got,
2162 addend: -4,
2163 after: 0,
2164 });
2165 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![] };
2166 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2167 .expect("an object");
2168 let file = object::File::parse(&bytes[..]).expect("a readable object");
2169
2170 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2171 assert!(flags.is_undefined(), "nothing here defines it");
2172 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2173
2174 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2175 assert!(shared.is_undefined(), "nothing here defines this one either");
2176 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2177 }
2178
2179 #[test]
2181 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2182 let mut data = Data {
2183 apart: Vec::new(),
2184 weak: Vec::new(),
2185 objects: vec![variable("first", Place::Written)],
2186 };
2187 data.objects.push(Object {
2188 bytes: vec![0; 16],
2189 size: 16,
2190 align: 8,
2191 relocs: vec![Reloc {
2192 at: 8,
2193 symbol: "y".to_owned(),
2194 kind: Reference::Address { bytes: 8 },
2195 addend: 0,
2196 after: 0,
2197 }],
2198 ..variable("second", Place::Written)
2199 });
2200 let bytes =
2201 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2202 .expect("an object");
2203 let file = object::File::parse(&bytes[..]).expect("a readable object");
2204 let section = file.section_by_name(".data").expect("a data section");
2205 let (offset, _) = section.relocations().next().expect("one relocation");
2206 assert_eq!(offset, 16);
2209 }
2210
2211 #[test]
2212 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2213 let data = Data {
2214 apart: Vec::new(),
2215 weak: Vec::new(),
2216 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2217 };
2218 let aliases = [Alias {
2219 name: "b".to_owned(),
2220 target: "a".to_owned(),
2221 binding: Binding::Global,
2222 visibility: Visibility::Default,
2223 }];
2224 let bytes = write(
2225 &Text::default(),
2226 &data,
2227 &aliases,
2228 &target(),
2229 Output::default(),
2230 &Info::default(),
2231 )
2232 .expect("an object");
2233 let file = object::File::parse(&bytes[..]).expect("a readable object");
2234 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2235 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2236 assert_eq!(b.address(), a.address(), "the same place");
2237 assert_eq!(b.size(), a.size());
2238 assert_eq!(b.section_index(), a.section_index());
2239 assert!(a.is_local(), "the target was written `static`");
2242 assert!(b.is_global(), "and the name given to it was not");
2243 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2245 }
2246
2247 #[test]
2248 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2249 let text = calling("puts");
2250 let aliases = [Alias {
2251 name: "g".to_owned(),
2252 target: "f".to_owned(),
2253 binding: Binding::Weak,
2254 visibility: Visibility::Default,
2255 }];
2256 let bytes = write(
2257 &text,
2258 &Data::default(),
2259 &aliases,
2260 &target(),
2261 Output::default(),
2262 &Info::default(),
2263 )
2264 .expect("an object");
2265 let file = object::File::parse(&bytes[..]).expect("a readable object");
2266 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2267 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2268 assert_eq!(g.address(), f.address());
2269 assert_eq!(g.size(), f.size());
2270 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2271 assert!(g.is_weak(), "so that a program may define the name itself instead");
2272 }
2273
2274 #[test]
2277 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2278 let aliases = [Alias {
2279 name: "b".to_owned(),
2280 target: "a".to_owned(),
2281 binding: Binding::Global,
2282 visibility: Visibility::Default,
2283 }];
2284 let error = write(
2285 &Text::default(),
2286 &Data::default(),
2287 &aliases,
2288 &target(),
2289 Output::default(),
2290 &Info::default(),
2291 )
2292 .expect_err("nothing to point at");
2293 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2294 }
2295
2296 #[test]
2297 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2298 let text = calling("puts");
2299 for triple in [
2300 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2301 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2302 ] {
2303 let error = write(
2304 &text,
2305 &Data::default(),
2306 &[],
2307 &TargetInfo::new(triple),
2308 Output::default(),
2309 &Info::default(),
2310 )
2311 .expect_err("no writer");
2312 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2313 }
2314 }
2315
2316 #[test]
2322 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2323 let mut text = calling("puts");
2324 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2325 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2326 text.bytes.resize(33, 0x90);
2327 let data = Data {
2328 apart: Vec::new(),
2329 weak: Vec::new(),
2330 objects: vec![variable("seen", Place::Written), {
2331 let mut quiet = variable("quiet", Place::Zero);
2332 quiet.binding = Binding::Local;
2333 quiet
2334 }],
2335 };
2336 let aliases = [Alias {
2337 name: "second".to_owned(),
2338 target: "f".to_owned(),
2339 binding: Binding::Global,
2340 visibility: Visibility::Default,
2341 }];
2342
2343 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2344 assert_eq!(names, ["f", "shared", "seen", "second"]);
2345
2346 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2347 .expect("an object");
2348 let file = object::File::parse(&bytes[..]).expect("a readable object");
2349 let found: Vec<String> = file
2350 .symbols()
2351 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2352 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2353 .collect();
2354 let mut sorted = names.clone();
2355 sorted.sort();
2356 let mut theirs = found;
2357 theirs.sort();
2358 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2359 }
2360
2361 fn windows() -> TargetInfo {
2363 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2364 }
2365
2366 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2368 let file = object::File::parse(bytes).expect("a readable object");
2369 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2370 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2371 }
2372
2373 #[test]
2374 fn a_windows_target_is_written_rather_than_refused() {
2375 let text = calling("puts");
2376 let bytes =
2377 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2378 .expect("an object");
2379 let file = object::File::parse(&bytes[..]).expect("a readable object");
2380 assert_eq!(file.format(), BinaryFormat::Coff);
2381 let section = file.section_by_name(".text").expect("a text section");
2382 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2383 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2384 assert!(names.contains(&"f"), "{names:?}");
2385 assert!(names.contains(&"puts"), "{names:?}");
2386 }
2387
2388 #[test]
2396 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2397 for (after, typ) in [
2398 (0, pe::IMAGE_REL_AMD64_REL32),
2399 (1, pe::IMAGE_REL_AMD64_REL32_1),
2400 (4, pe::IMAGE_REL_AMD64_REL32_4),
2401 (5, pe::IMAGE_REL_AMD64_REL32_5),
2402 ] {
2403 let mut text = calling("puts");
2404 text.relocs[0].addend = -4 - i64::from(after);
2407 text.relocs[0].after = after;
2408 text.bytes.resize(6 + after as usize, 0x90);
2409 text.funcs[0].len = text.bytes.len();
2410 let bytes = write(
2411 &text,
2412 &Data::default(),
2413 &[],
2414 &windows(),
2415 Output::default(),
2416 &Info::default(),
2417 )
2418 .expect("an object");
2419 let file = object::File::parse(&bytes[..]).expect("a readable object");
2420 let section = file.section_by_name(".text").expect("a text section");
2421 let (_, reloc) = section.relocations().next().expect("the relocation");
2422 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2423 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2426 }
2427 }
2428
2429 #[test]
2432 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2433 let mut text = calling("puts");
2434 text.relocs[0].addend = 12;
2435 let bytes =
2436 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2437 .expect("an object");
2438 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2439 }
2440
2441 #[test]
2442 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2443 let object = Object {
2444 bytes: vec![0; 8],
2445 size: 8,
2446 align: 8,
2447 relocs: vec![Reloc {
2448 at: 0,
2449 symbol: "y".to_owned(),
2450 kind: Reference::Address { bytes: 8 },
2451 addend: 0,
2452 after: 0,
2453 }],
2454 ..variable("p", Place::Written)
2455 };
2456 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
2457 let bytes =
2458 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2459 .expect("an object");
2460 let file = object::File::parse(&bytes[..]).expect("a readable object");
2461 let section = file.section_by_name(".data").expect("a data section");
2462 let (_, reloc) = section.relocations().next().expect("the relocation");
2463 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2464 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2465 }
2466
2467 #[test]
2470 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2471 for local in [false, true] {
2472 let data = Data {
2473 apart: Vec::new(),
2474 weak: Vec::new(),
2475 objects: vec![variable("p", Place::RelocReadOnly { local })],
2476 };
2477 let bytes = write(
2478 &Text::default(),
2479 &data,
2480 &[],
2481 &windows(),
2482 Output::default(),
2483 &Info::default(),
2484 )
2485 .expect("an object");
2486 let file = object::File::parse(&bytes[..]).expect("a readable object");
2487 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2488 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2489 }
2490 }
2491
2492 #[test]
2495 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2496 let text = calling("puts");
2497 let output = Output { property: Property { features: 3 }, ..Output::default() };
2498 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2499 .expect("an object");
2500 let file = object::File::parse(&bytes[..]).expect("a readable object");
2501 assert!(file.section_by_name(".note.GNU-stack").is_none());
2502 assert!(file.section_by_name(".note.gnu.property").is_none());
2503 }
2504
2505 #[test]
2510 fn what_this_format_cannot_say_is_refused_by_name() {
2511 let ordinary = Text::default();
2512 let empty = Data::default();
2513
2514 let mut thread = Data::default();
2515 thread.objects.push(variable("t", Place::Thread { zero: false }));
2516
2517 let mut gathered = Data::default();
2518 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2519
2520 let mut table = calling("puts");
2521 table.relocs[0].kind = Reference::Got;
2522
2523 let mut room = calling("puts");
2524 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2525
2526 let cases: [(&str, &Text, &Data); 4] = [
2527 ("thread-local", &ordinary, &thread),
2528 ("startup", &ordinary, &gathered),
2529 ("table", &table, &empty),
2530 ("patcher", &room, &empty),
2531 ];
2532 for (what, text, data) in cases {
2533 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2534 .expect_err("something this format cannot write");
2535 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2536 }
2537 }
2538
2539 #[test]
2543 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2544 let mut text = calling("puts");
2545 text.funcs[0].visibility = Visibility::Hidden;
2546 let bytes =
2547 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2548 .expect("an object");
2549 let file = object::File::parse(&bytes[..]).expect("a readable object");
2550 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2551 assert!(symbol.is_global(), "a name others may use either way");
2552 }
2553
2554 #[test]
2555 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2556 let text = calling("puts");
2557 let data = Data {
2558 apart: Vec::new(),
2559 weak: Vec::new(),
2560 objects: vec![variable("shared", Place::Written)],
2561 };
2562 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2563 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2564 }
2565
2566 #[test]
2570 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2571 let text = calling("puts");
2572 for triple in [
2573 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2574 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2575 ] {
2576 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2577 .expect_err("no writer");
2578 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2579 }
2580 }
2581}