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, EXCEPT_TABLE, Info, Object, Output, Place, Property,
47 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 fn gathered(self, array: Array) -> Option<SectionFlags> {
141 match self {
142 Flavour::Elf => Some(elf::gathered(array)),
143 Flavour::Coff | Flavour::MachO => None,
144 }
145 }
146
147 pub(crate) fn stated(self, shape: crate::source::Shape) -> Option<SectionFlags> {
156 match self {
157 Flavour::Elf => {
158 Some(SectionFlags::Elf { sh_type: shape.sh_type(), sh_flags: shape.sh_flags() })
159 }
160 Flavour::Coff => None,
161 Flavour::MachO => Some(SectionFlags::MachO {
162 flags: object::macho::SectionFlags(shape.mach),
163 reserved2: 0,
164 }),
165 }
166 }
167
168 pub(crate) fn sort(self, sort: crate::source::Sort, binding: Binding) -> SymbolKind {
184 if self == Flavour::MachO {
185 return match sort {
186 crate::source::Sort::Func => SymbolKind::Text,
187 crate::source::Sort::File => SymbolKind::File,
188 _ => SymbolKind::Data,
189 };
190 }
191 match sort {
192 crate::source::Sort::Func => SymbolKind::Text,
193 crate::source::Sort::Object => SymbolKind::Data,
194 crate::source::Sort::Thread => SymbolKind::Tls,
195 crate::source::Sort::File => SymbolKind::File,
196 crate::source::Sort::Untyped => match (self, binding) {
197 (Flavour::Coff, Binding::Global | Binding::Weak) => SymbolKind::Data,
198 _ => SymbolKind::Label,
199 },
200 }
201 }
202
203 pub(crate) fn marker(self, obj: &mut Writer<'_>) {
205 match self {
206 Flavour::Elf => elf::marker(obj),
207 Flavour::Coff => coff::marker(obj),
208 Flavour::MachO => {}
209 }
210 }
211
212 fn property(self, obj: &mut Writer<'_>, property: Property) {
218 if !property.any() {
219 return;
220 }
221 match self {
222 Flavour::Elf => {
223 let note = obj.section_id(StandardSection::GnuProperty);
224 obj.append_section_data(note, &elf::record(property), 8);
225 }
226 Flavour::Coff | Flavour::MachO => {}
227 }
228 }
229
230 fn tables(self) -> ((&'static str, u64), Option<(&'static str, u64)>) {
234 match self {
235 Flavour::Elf => (elf::FRAMES, None),
236 Flavour::Coff => (coff::FUNCTIONS, Some(coff::CODES)),
237 Flavour::MachO => (("__TEXT,__eh_frame", 8), None),
238 }
239 }
240
241 fn finish(self, bytes: &mut [u8], ordered: &[String]) {
243 match self {
244 Flavour::Elf => elf::link(bytes, ordered),
245 Flavour::Coff | Flavour::MachO => {
246 debug_assert!(ordered.is_empty(), "a record this format cannot write");
247 }
248 }
249 }
250}
251
252#[derive(Debug, Clone, PartialEq, Eq)]
254pub enum Error {
255 Format {
257 triple: String,
259 },
260 Refused {
262 why: String,
264 },
265}
266
267impl std::fmt::Display for Error {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 match self {
270 Error::Format { triple } => {
271 write!(f, "there is no object writer for {triple} in this compiler yet")
272 }
273 Error::Refused { why } => {
274 write!(f, "the object writer refused what it was given: {why}")
275 }
276 }
277 }
278}
279
280impl std::error::Error for Error {}
281
282pub fn write(
298 text: &Text,
299 data: &Data,
300 aliases: &[Alias],
301 target: &TargetInfo,
302 output: Output,
303 info: &Info,
304) -> Result<Vec<u8>, Error> {
305 let Output { sections, property } = output;
306 let flavour = Flavour::of(target).filter(|_| target.tuple.arch() == Arch::X86_64);
307 let Some(flavour) = flavour else {
308 return Err(Error::Format { triple: target.tuple.to_string() });
309 };
310 if flavour == Flavour::Coff {
311 beyond(text, data, info)?;
312 }
313 let mut obj = Writer::new(flavour.binary(), Architecture::X86_64, Endianness::Little);
314 let whole = obj.section_id(StandardSection::Text);
318 if !sections.functions {
319 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
320 }
321
322 let mut symbols = BTreeMap::new();
326 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
331 let mut ordered: Vec<String> = Vec::new();
334 for func in &text.funcs {
335 let ahead = func.patch.map_or(0, |patch| patch.before);
345 let (section, at) = if sections.functions {
346 let name = format!(".text.{}", func.name).into_bytes();
347 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
348 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
349 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
350 (id, ahead as u64)
351 } else {
352 (whole, func.start as u64)
353 };
354 if let Some(patch) = func.patch {
369 let base = if sections.functions { func.start - ahead } else { 0 };
370 let name = elf::PATCHABLE.as_bytes().to_vec();
371 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
372 obj.section_mut(id).flags = elf::ordered();
373 obj.append_section_data(id, &[0; 8], 8);
374 let symbol = obj.section_symbol(section);
375 let flags = flavour.reloc(Reference::Address { bytes: 8 }, 0).ok_or_else(|| {
376 Error::Refused { why: "no relocation holds an address here".to_owned() }
377 })?;
378 obj.add_relocation(
379 id,
380 Relocation { offset: 0, symbol, addend: (patch.at - base) as i64, flags },
381 )
382 .map_err(|why| Error::Refused { why: why.to_string() })?;
383 ordered.push(if sections.functions {
384 format!(".text.{}", func.name)
385 } else {
386 ".text".to_owned()
387 });
388 }
389 let id = obj.add_symbol(Symbol {
390 name: func.name.clone().into_bytes(),
391 value: at,
392 size: func.len as u64,
393 kind: SymbolKind::Text,
394 scope: scope_of(func.binding),
395 weak: func.binding == Binding::Weak,
396 section: SymbolSection::Section(section),
397 flags: SymbolFlags::None,
398 });
399 flavour.see(&mut obj, id, func.binding, func.visibility);
400 symbols.insert(func.name.clone(), id);
401 split.push((section, at));
402 }
403
404 for label in &text.labels {
408 let after = text.funcs.partition_point(|func| func.start <= label.at);
409 let Some(index) = after.checked_sub(1) else {
410 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
411 return Err(Error::Refused { why });
412 };
413 let func = &text.funcs[index];
414 let (section, at) = if sections.functions {
415 let base = func.start - func.patch.map_or(0, |patch| patch.before);
418 (split[index].0, (label.at - base) as u64)
419 } else {
420 (whole, label.at as u64)
421 };
422 let id = obj.add_symbol(Symbol {
423 name: label.name.clone().into_bytes(),
424 value: at,
425 size: 0,
428 kind: SymbolKind::Label,
429 scope: SymbolScope::Compilation,
433 weak: false,
434 section: SymbolSection::Section(section),
435 flags: SymbolFlags::None,
436 });
437 symbols.insert(label.name.clone(), id);
438 }
439
440 let mut placed = Vec::with_capacity(data.objects.len());
445 let mut named = HashMap::new();
449 for object in &data.objects {
450 let (section, offset) = put(&mut obj, object, &mut named, sections, flavour);
451 let id = obj.add_symbol(Symbol {
452 name: object.name.clone().into_bytes(),
453 value: if object.place == Place::Merged { object.align } else { offset },
456 size: object.size,
457 kind: match object.place {
462 Place::Thread { .. } => SymbolKind::Tls,
463 _ => SymbolKind::Data,
464 },
465 scope: scope_of(object.binding),
466 weak: object.binding == Binding::Weak,
467 section,
468 flags: SymbolFlags::None,
469 });
470 flavour.see(&mut obj, id, object.binding, object.visibility);
471 symbols.insert(object.name.clone(), id);
472 placed.push((section.id(), offset));
473 }
474
475 let tables = tables(&mut obj, text, &split, &mut named, sections, flavour)?;
478
479 for apart in &data.apart {
483 let (Some(section), offset) = placed[apart.object] else { continue };
484 let value = distance(&obj, &symbols, apart)?;
485 let bytes = usize::from(apart.bytes);
486 let at = usize::try_from(offset).map_err(|why| Error::Refused { why: why.to_string() })?;
487 let at = at + apart.at;
488 let image = obj.section_mut(section).data_mut();
489 image[at..at + bytes].copy_from_slice(&value.to_le_bytes()[..bytes]);
490 }
491
492 for alias in aliases {
498 let Some(&id) = symbols.get(&alias.target) else {
499 let why =
500 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
501 return Err(Error::Refused { why });
502 };
503 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
504 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
505 let id = obj.add_symbol(Symbol {
506 name: alias.name.clone().into_bytes(),
507 value,
508 size,
509 kind,
510 scope: scope_of(alias.binding),
511 weak: alias.binding == Binding::Weak,
512 section,
513 flags: SymbolFlags::None,
514 });
515 flavour.see(&mut obj, id, alias.binding, alias.visibility);
516 symbols.insert(alias.name.clone(), id);
517 }
518
519 let weak: HashSet<&str> = data.weak.iter().map(String::as_str).collect();
526 let relocs = || text.relocs.iter().chain(data.objects.iter().flat_map(|o| &o.relocs));
527 let thread: HashSet<&str> = relocs()
532 .filter(|reloc| reloc.kind == Reference::Thread)
533 .map(|reloc| reloc.symbol.as_str())
534 .collect();
535 let wanted: Vec<&String> =
536 relocs().map(|reloc| &reloc.symbol).chain(data.weak.iter()).collect();
537 for name in wanted {
538 if symbols.contains_key(name) || tables.contains_key(name) {
539 continue;
540 }
541 let id = obj.add_symbol(Symbol {
542 name: name.clone().into_bytes(),
543 value: 0,
544 size: 0,
545 kind: if thread.contains(name.as_str()) {
555 SymbolKind::Tls
556 } else {
557 SymbolKind::Unknown
558 },
559 scope: SymbolScope::Dynamic,
560 weak: weak.contains(name.as_str()),
561 section: SymbolSection::Undefined,
562 flags: SymbolFlags::None,
563 });
564 symbols.insert(name.clone(), id);
565 }
566
567 for reloc in &text.relocs {
568 let (section, at) = if sections.functions {
573 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
574 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
575 let why = format!("a relocation at {} is in front of every function", reloc.at);
576 return Err(Error::Refused { why });
577 };
578 let base = func.start - func.patch.map_or(0, |patch| patch.before);
581 (split[after - 1].0, (reloc.at - base) as u64)
582 } else {
583 (whole, reloc.at as u64)
584 };
585 if let Some(&(table, offset)) = tables.get(&reloc.symbol) {
589 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
590 why: format!("no relocation is {:?}", reloc.kind),
591 })?;
592 let symbol = obj.section_symbol(table);
593 let addend = reloc.addend + offset as i64;
594 obj.add_relocation(section, Relocation { offset: at, symbol, addend, flags })
595 .map_err(|why| Error::Refused { why: why.to_string() })?;
596 continue;
597 }
598 add(&mut obj, section, at, reloc, &symbols, flavour)?;
599 }
600
601 if !text.unwind.bytes.is_empty() {
605 let ((name, align), second) = flavour.tables();
606 let frames = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
607 obj.append_section_data(frames, &text.unwind.bytes, align);
608 let mut described = HashMap::new();
613 if !text.unwind.info.is_empty() {
614 let Some((name, align)) = second else {
615 let why = "an unwind table here is one section and it was given two".to_owned();
616 return Err(Error::Refused { why });
617 };
618 let codes = obj.add_section(Vec::new(), name.into(), SectionKind::ReadOnlyData);
619 obj.append_section_data(codes, &text.unwind.info, align);
620 for label in &text.unwind.labels {
621 let id = obj.add_symbol(Symbol {
622 name: label.name.clone().into_bytes(),
623 value: label.at as u64,
624 size: 0,
625 kind: SymbolKind::Label,
626 scope: SymbolScope::Compilation,
627 weak: false,
628 section: SymbolSection::Section(codes),
629 flags: SymbolFlags::None,
630 });
631 described.insert(label.name.clone(), id);
632 }
633 }
634 if !text.unwind.except.is_empty() {
639 let except =
640 obj.add_section(Vec::new(), EXCEPT_TABLE.into(), SectionKind::ReadOnlyData);
641 obj.append_section_data(except, &text.unwind.except, 4);
642 described.insert(EXCEPT_TABLE.to_owned(), obj.section_symbol(except));
643 }
644 for reloc in &text.unwind.relocs {
645 let found = described.get(&reloc.symbol).or_else(|| {
646 let ours = data.objects.iter().any(|object| object.name == reloc.symbol);
650 if ours { symbols.get(&reloc.symbol) } else { None }
651 });
652 let (symbol, addend) = match found {
653 Some(&id) => (id, reloc.addend),
657 None => {
671 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
672 let Some((section, at)) = found.map(|i| split[i]) else {
673 let why = format!(
674 "'{}' has an unwind record and is not a function here",
675 reloc.symbol
676 );
677 return Err(Error::Refused { why });
678 };
679 (obj.section_symbol(section), reloc.addend + at as i64)
683 }
684 };
685 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
686 why: format!("no relocation is {:?}", reloc.kind),
687 })?;
688 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
689 obj.add_relocation(frames, record)
690 .map_err(|why| Error::Refused { why: why.to_string() })?;
691 }
692 }
693 let mut named = HashMap::new();
702 for chunk in &info.chunks {
703 let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
704 obj.append_section_data(id, &chunk.bytes, 1);
705 named.insert(chunk.name.as_str(), id);
706 }
707 for chunk in &info.chunks {
708 let section = named[chunk.name.as_str()];
709 for reloc in &chunk.relocs {
710 let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
711 Some(&id) => (obj.section_symbol(id), reloc.addend),
714 None => match text.funcs.iter().position(|func| func.name == reloc.symbol) {
719 Some(which) => {
720 let (section, at) = split[which];
721 (obj.section_symbol(section), reloc.addend + at as i64)
722 }
723 None => {
729 let found = data.objects.iter().position(|had| had.name == reloc.symbol);
730 let Some(which) = found else {
731 let why = format!(
732 "'{}' is named by the debug information and is not defined here",
733 reloc.symbol
734 );
735 return Err(Error::Refused { why });
736 };
737 match placed[which] {
738 (Some(section), at) => {
739 (obj.section_symbol(section), reloc.addend + at as i64)
740 }
741 (None, _) => (symbols[&reloc.symbol], reloc.addend),
742 }
743 }
744 },
745 };
746 let flags = flavour.reloc(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
747 why: format!("no relocation is {:?}", reloc.kind),
748 })?;
749 let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
750 obj.add_relocation(section, record)
751 .map_err(|why| Error::Refused { why: why.to_string() })?;
752 }
753 }
754 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
755 let Some(section) = section else { continue };
756 for reloc in &object.relocs {
757 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols, flavour)?;
758 }
759 }
760
761 flavour.property(&mut obj, property);
765
766 flavour.marker(&mut obj);
769
770 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
771 flavour.finish(&mut bytes, &ordered);
772 Ok(bytes)
773}
774
775fn distance(
782 obj: &Writer<'_>,
783 symbols: &BTreeMap<String, SymbolId>,
784 apart: &Apart,
785) -> Result<i64, Error> {
786 let find = |name: &str| match symbols.get(name) {
787 Some(&id) => Ok(obj.symbol(id)),
788 None => Err(Error::Refused { why: format!("'{name}' is measured from and is not here") }),
789 };
790 let (to, from) = (find(&apart.to)?, find(&apart.from)?);
791 if to.section != from.section {
792 let why = format!("'{}' and '{}' are in different sections", apart.to, apart.from);
793 return Err(Error::Refused { why });
794 }
795 let value = (to.value as i64).wrapping_sub(from.value as i64).wrapping_add(apart.addend);
796 let bits = u32::from(apart.bytes) * 8;
797 if bits < 64 && (value >> (bits - 1)) != 0 && (value >> (bits - 1)) != -1 {
798 let why = format!("'{}' is too far from '{}' for {} bytes", apart.to, apart.from, bits / 8);
799 return Err(Error::Refused { why });
800 }
801 Ok(value)
802}
803
804fn beyond(text: &Text, data: &Data, info: &Info) -> Result<(), Error> {
817 let why = |why: String| Err(Error::Refused { why });
818 if !info.chunks.is_empty() {
819 return why("debug information here goes in sections this writer does not name".to_owned());
820 }
821 if text.funcs.iter().any(|func| func.patch.is_some()) {
822 return why("a record of where a patcher's room is has no section flags here".to_owned());
823 }
824 for reloc in text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs)) {
825 if matches!(
826 reloc.kind,
827 Reference::Got | Reference::GotBare | Reference::GotKept | Reference::Thread
828 ) {
829 return why(format!("nothing reaches '{}' through a table here", reloc.symbol));
830 }
831 }
832 for object in &data.objects {
833 if matches!(object.place, Place::Thread { zero: true }) {
834 return why(format!(
835 "'{}' is zeroed thread-local storage, which is not here",
836 object.name
837 ));
838 }
839 let Place::Named(name) = &object.place else { continue };
840 if Array::of(name).is_some() {
841 return why(format!("'{name}' is not a list the startup code here gathers"));
842 }
843 }
844 Ok(())
845}
846
847pub fn defines(
871 text: &Text,
872 data: &Data,
873 aliases: &[Alias],
874 target: &TargetInfo,
875) -> Result<Vec<String>, Error> {
876 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
877 return Err(Error::Format { triple: target.tuple.to_string() });
878 }
879 let names = text
880 .funcs
881 .iter()
882 .filter(|func| func.binding != Binding::Local)
883 .map(|func| func.name.clone())
884 .chain(
885 data.objects
886 .iter()
887 .filter(|object| object.binding != Binding::Local)
888 .map(|object| object.name.clone()),
889 )
890 .chain(
891 aliases
892 .iter()
893 .filter(|alias| alias.binding != Binding::Local)
894 .map(|alias| alias.name.clone()),
895 )
896 .collect();
897 Ok(names)
898}
899
900fn put(
907 obj: &mut Writer<'_>,
908 object: &Object,
909 named: &mut HashMap<String, object::write::SectionId>,
910 sections: Sections,
911 flavour: Flavour,
912) -> (SymbolSection, u64) {
913 if sections.data {
919 if let Some(name) = object.place.split(&object.name) {
920 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
921 let offset = if carries_no_bytes(&object.place) {
922 obj.append_section_bss(section, object.size, object.align)
923 } else {
924 obj.append_section_data(section, &object.bytes, object.align)
925 };
926 return (SymbolSection::Section(section), offset);
927 }
928 }
929 let section = match &object.place {
930 Place::Written => obj.section_id(StandardSection::Data),
931 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
932 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
938 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
939 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
940 },
941 Place::Zero => obj.section_id(StandardSection::UninitializedData),
942 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
943 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
944 Place::Merged => return (SymbolSection::Common, 0),
945 Place::Named(name) => {
951 let section = made(obj, named, name, SectionKind::Data);
952 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
953 obj.section_mut(section).flags = flags;
954 }
955 section
956 }
957 };
958 let offset = if carries_no_bytes(&object.place) {
959 obj.append_section_bss(section, object.size, object.align)
960 } else {
961 obj.append_section_data(section, &object.bytes, object.align)
962 };
963 (SymbolSection::Section(section), offset)
964}
965
966fn tables(
979 obj: &mut Writer<'_>,
980 text: &Text,
981 split: &[(object::write::SectionId, u64)],
982 named: &mut HashMap<String, object::write::SectionId>,
983 sections: Sections,
984 flavour: Flavour,
985) -> Result<HashMap<String, (object::write::SectionId, u64)>, Error> {
986 let mut placed = HashMap::new();
987 if text.tables.is_empty() {
988 return Ok(placed);
989 }
990 if flavour != Flavour::Elf {
991 let why = "a jump table outside the code is written on ELF only".to_owned();
992 return Err(Error::Refused { why });
993 }
994 let flags = flavour.reloc(Reference::Away, 0).ok_or_else(|| Error::Refused {
995 why: "no relocation is a distance from where it is written".to_owned(),
996 })?;
997 for table in &text.tables {
998 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
999 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
1000 })?;
1001 let section = if sections.data {
1002 let name = format!(".rodata.{}", func.name);
1003 made(obj, named, &name, SectionKind::ReadOnlyData)
1004 } else {
1005 obj.section_id(StandardSection::ReadOnlyData)
1006 };
1007 let offset = obj.append_section_data(section, &vec![0; 4 * table.cells.len()], 4);
1008 placed.insert(table.name.clone(), (section, offset));
1009 let (code, at) = split[table.func];
1010 let symbol = obj.section_symbol(code);
1011 for (index, &cell) in table.cells.iter().enumerate() {
1012 let place = 4 * index as u64;
1013 let addend = at as i64 + cell as i64 + place as i64;
1014 let record = Relocation { offset: offset + place, symbol, addend, flags };
1015 obj.add_relocation(section, record)
1016 .map_err(|why| Error::Refused { why: why.to_string() })?;
1017 }
1018 }
1019 Ok(placed)
1020}
1021
1022fn carries_no_bytes(place: &Place) -> bool {
1028 matches!(place, Place::Zero | Place::Thread { zero: true })
1029}
1030
1031fn made(
1039 obj: &mut Writer<'_>,
1040 named: &mut HashMap<String, object::write::SectionId>,
1041 name: &str,
1042 kind: SectionKind,
1043) -> object::write::SectionId {
1044 if let Some(section) = named.get(name) {
1045 return *section;
1046 }
1047 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1048 named.insert(name.to_owned(), section);
1049 section
1050}
1051
1052fn kind_of(place: &Place) -> SectionKind {
1061 match place {
1062 Place::ReadOnly => SectionKind::ReadOnlyData,
1063 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1064 Place::Zero => SectionKind::UninitializedData,
1065 Place::Thread { zero: false } => SectionKind::Tls,
1066 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1067 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
1068 }
1069}
1070
1071fn add(
1078 obj: &mut Writer<'_>,
1079 section: object::write::SectionId,
1080 at: u64,
1081 reloc: &Reloc,
1082 symbols: &BTreeMap<String, SymbolId>,
1083 flavour: Flavour,
1084) -> Result<(), Error> {
1085 let flags = flavour
1086 .reloc(reloc.kind, reloc.after)
1087 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1088 obj.add_relocation(
1089 section,
1090 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1091 )
1092 .map_err(|why| Error::Refused { why: why.to_string() })
1093}
1094
1095pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1108 match binding {
1109 Binding::Local => SymbolScope::Compilation,
1110 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1111 }
1112}
1113
1114#[cfg(test)]
1115mod tests {
1116 use super::*;
1117
1118 use object::read::elf::Sym as _;
1119 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
1120 use object::{elf, pe};
1121 use rucc_target::{Arch, Env, Os, Triple};
1122
1123 use crate::elf::PATCHABLE;
1124 use crate::section::{Extent, Marker, Patch, Reloc};
1125
1126 fn target() -> TargetInfo {
1128 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1129 }
1130
1131 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1136 Extent {
1137 name,
1138 start,
1139 len,
1140 align: crate::FUNC_ALIGN,
1141 binding,
1142 visibility: Visibility::Default,
1143 patch: None,
1144 landings: Vec::new(),
1145 }
1146 }
1147
1148 fn calling(name: &str) -> Text {
1150 Text {
1151 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1152 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1153 relocs: vec![Reloc {
1154 at: 1,
1155 symbol: name.to_owned(),
1156 kind: Reference::Call,
1157 addend: -4,
1158 after: 0,
1159 }],
1160 ..Text::default()
1161 }
1162 }
1163
1164 #[test]
1165 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1166 let text = calling("puts");
1167 let bytes =
1168 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1169 .expect("an object");
1170 let file = object::File::parse(&bytes[..]).expect("a readable object");
1171 let section = file.section_by_name(".text").expect("a text section");
1172 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1173 }
1174
1175 #[test]
1176 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1177 let mut text = calling("puts");
1178 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1179 text.bytes.resize(17, 0x90);
1180 let bytes =
1181 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1182 .expect("an object");
1183 let file = object::File::parse(&bytes[..]).expect("a readable object");
1184 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1185 assert_eq!(g.address(), 16);
1186 assert_eq!(g.size(), 1);
1187 assert_eq!(g.kind(), SymbolKind::Text);
1188 assert!(g.is_global(), "nothing said otherwise about this one");
1189 }
1190
1191 #[test]
1192 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1193 let mut text = calling("puts");
1194 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1195 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1196 text.bytes.resize(33, 0x90);
1197 let bytes =
1198 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1199 .expect("an object");
1200 let file = object::File::parse(&bytes[..]).expect("a readable object");
1201 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1202 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1205 assert!(!hidden.is_weak());
1206 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1207 assert!(shared.is_weak(), "a weak function has to be able to lose");
1208 assert!(shared.is_global());
1209 }
1210
1211 #[test]
1228 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1229 let mut text = calling("puts");
1230 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1231 text.funcs[0].start = 3;
1232 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1233 text.relocs[0].at = 4;
1234 let bytes =
1235 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1236 .expect("an object");
1237 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1238 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1239 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1240 assert_eq!(section.align(), 8);
1241 let header = section.elf_section_header();
1242 assert_eq!(
1243 header.sh_flags.get(Endianness::Little),
1244 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1245 );
1246 let index = file.section_by_name(".text").expect("a text section").index().0;
1249 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1250 assert_ne!(index, 0);
1251
1252 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1254 panic!("one address in the record")
1255 };
1256 assert_eq!(*at, 0);
1257 assert_eq!(reloc.addend(), 0);
1258 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1259 }
1260
1261 #[test]
1263 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1264 let text = calling("puts");
1265 let bytes =
1266 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1267 .expect("an object");
1268 let file = object::File::parse(&bytes[..]).expect("a readable object");
1269 assert!(file.section_by_name(PATCHABLE).is_none());
1270 }
1271
1272 #[test]
1278 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1279 let mut text = calling("puts");
1280 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1281 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1282 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1283 text.bytes.resize(17, 0x90);
1284 let output =
1285 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1286 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1287 .expect("an object");
1288 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1289 let links: Vec<usize> = file
1290 .sections()
1291 .filter(|section| section.name() == Ok(PATCHABLE))
1292 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1293 .collect();
1294 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1295 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1296 }
1297
1298 #[test]
1299 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1300 let mut text = calling("puts");
1301 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1302 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1303 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1304 text.bytes.resize(49, 0x90);
1305 let bytes =
1306 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1307 .expect("an object");
1308 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1309 let visibility = |name: &str| {
1310 file.symbols()
1311 .find(|s| s.name() == Ok(name))
1312 .expect("the function")
1313 .elf_symbol()
1314 .st_visibility()
1315 };
1316 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1318 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1319 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1322 }
1323
1324 #[test]
1334 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1335 let mut text = calling("puts");
1336 for (index, (name, seen)) in
1337 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1338 {
1339 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1340 func.visibility = seen;
1341 text.funcs.push(func);
1342 }
1343 text.bytes.resize(49, 0x90);
1344 let mut data = Data::default();
1345 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1346 let mut object = variable(name, Place::Written);
1347 object.visibility = seen;
1348 data.objects.push(object);
1349 }
1350 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1351 .expect("an object");
1352 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1353 let visibility = |name: &str| {
1354 file.symbols()
1355 .find(|s| s.name() == Ok(name))
1356 .expect("the symbol")
1357 .elf_symbol()
1358 .st_visibility()
1359 };
1360 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1361 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1362 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1363 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1364 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1367 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1368 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1369 }
1370
1371 #[test]
1372 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1373 let bytes = write(
1374 &calling("puts"),
1375 &Data::default(),
1376 &[],
1377 &target(),
1378 Output::default(),
1379 &Info::default(),
1380 )
1381 .expect("an object");
1382 let file = object::File::parse(&bytes[..]).expect("a readable object");
1383 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1384 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1385 }
1386
1387 #[test]
1388 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1389 for (reference, wanted) in [
1390 (Reference::Call, elf::R_X86_64_PLT32),
1391 (Reference::Data, elf::R_X86_64_PC32),
1392 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1393 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1394 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1395 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1396 ] {
1397 let mut text = calling("puts");
1398 text.relocs[0].kind = reference;
1399 let bytes =
1400 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1401 .expect("an object");
1402 let file = object::File::parse(&bytes[..]).expect("a readable object");
1403 let section = file.section_by_name(".text").expect("a text section");
1404 let (offset, reloc) = section.relocations().next().expect("one relocation");
1405 assert_eq!(offset, 1);
1406 assert_eq!(reloc.addend(), -4);
1407 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1408 }
1409 }
1410
1411 #[test]
1412 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1413 let mut text = calling("puts");
1414 text.relocs.push(Reloc {
1415 at: 1,
1416 symbol: "puts".to_owned(),
1417 kind: Reference::Call,
1418 addend: -4,
1419 after: 0,
1420 });
1421 let bytes =
1422 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1423 .expect("an object");
1424 let file = object::File::parse(&bytes[..]).expect("a readable object");
1425 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1426 }
1427
1428 #[test]
1429 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1430 let text = calling("f");
1431 let bytes =
1432 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1433 .expect("an object");
1434 let file = object::File::parse(&bytes[..]).expect("a readable object");
1435 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1436 let f = found.next().expect("the function");
1437 assert!(!f.is_undefined(), "the file defines it");
1438 assert!(found.next().is_none(), "and defines it once");
1439 }
1440
1441 #[test]
1442 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1443 let bytes = write(
1444 &calling("puts"),
1445 &Data::default(),
1446 &[],
1447 &target(),
1448 Output::default(),
1449 &Info::default(),
1450 )
1451 .expect("an object");
1452 let file = object::File::parse(&bytes[..]).expect("a readable object");
1453 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1454 assert!(note.data().expect("no bytes").is_empty());
1455 }
1456
1457 #[test]
1464 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1465 let property = Property { features: Property::IBT | Property::SHSTK };
1466 let output = Output { property, ..Output::default() };
1467 let bytes =
1468 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1469 .expect("an object");
1470 let file = object::File::parse(&bytes[..]).expect("a readable object");
1471 let note = file.section_by_name(".note.gnu.property").expect("the note");
1472 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1473 let want: Vec<u8> = [
1474 4u32,
1475 16,
1476 5,
1477 u32::from_le_bytes(*b"GNU\0"),
1478 Property::X86_FEATURES,
1479 4,
1480 Property::IBT | Property::SHSTK,
1481 0,
1482 ]
1483 .iter()
1484 .flat_map(|word| word.to_le_bytes())
1485 .collect();
1486 assert_eq!(note.data().expect("the bytes"), &want[..]);
1487 }
1488
1489 #[test]
1495 fn a_file_built_to_have_nothing_checked_says_nothing() {
1496 let bytes = write(
1497 &calling("puts"),
1498 &Data::default(),
1499 &[],
1500 &target(),
1501 Output::default(),
1502 &Info::default(),
1503 )
1504 .expect("an object");
1505 let file = object::File::parse(&bytes[..]).expect("a readable object");
1506 assert!(file.section_by_name(".note.gnu.property").is_none());
1507 }
1508
1509 #[test]
1518 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1519 let mut text = calling("puts");
1520 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1521 text.bytes.resize(17, 0x90);
1522 text.unwind.bytes = vec![0; 64];
1525 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1526 text.unwind.relocs.push(Reloc {
1527 at,
1528 symbol: name.to_owned(),
1529 kind: Reference::Address { bytes: 8 },
1530 addend: 0,
1531 after: 0,
1532 });
1533 }
1534 let bytes =
1535 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1536 .expect("an object");
1537 let file = object::File::parse(&bytes[..]).expect("a readable object");
1538 let mut found = points_at(&file);
1539 found.sort_unstable();
1540 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1541 }
1542
1543 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1546 let frames = file.section_by_name(".eh_frame").expect("the table");
1547 frames
1548 .relocations()
1549 .map(|(offset, reloc)| {
1550 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1551 panic!("a record points at something that is not a symbol");
1552 };
1553 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1554 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1555 let section = symbol.section_index().expect("a section symbol is in one");
1556 let name = file.section_by_index(section).expect("a readable section");
1557 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1558 })
1559 .collect()
1560 }
1561
1562 #[test]
1575 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1576 let mut text = two();
1577 text.unwind.bytes = vec![0; 64];
1578 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1579 text.unwind.relocs.push(Reloc {
1580 at,
1581 symbol: name.to_owned(),
1582 kind: Reference::Data,
1583 addend: 0,
1584 after: 0,
1585 });
1586 }
1587 let bytes =
1588 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1589 .expect("an object");
1590 let file = object::File::parse(&bytes[..]).expect("a readable object");
1591 let mut whole = points_at(&file);
1592 whole.sort_unstable();
1593 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1594
1595 let sections =
1596 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1597 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1598 .expect("an object");
1599 let file = object::File::parse(&bytes[..]).expect("a readable object");
1600 let mut split = points_at(&file);
1601 split.sort_unstable();
1602 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1603 }
1604
1605 #[test]
1612 fn a_record_about_something_this_file_does_not_define_is_refused() {
1613 let mut text = calling("puts");
1614 text.unwind.bytes = vec![0; 64];
1615 text.unwind.relocs.push(Reloc {
1616 at: 32,
1617 symbol: "puts".to_owned(),
1618 kind: Reference::Data,
1619 addend: 0,
1620 after: 0,
1621 });
1622 let why =
1623 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1624 .expect_err("a record about a name from somewhere else");
1625 assert!(why.to_string().contains("puts"), "{why}");
1626 }
1627
1628 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1630 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1631 let index = symbol.section_index().expect("a section to be defined in");
1632 let section = file.section_by_index(index).expect("a readable section");
1633 section.name().expect("a named section").to_owned()
1634 }
1635
1636 fn two() -> Text {
1638 let mut text = calling("puts");
1639 text.bytes.resize(16, 0x90);
1642 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1643 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1644 text.relocs.push(Reloc {
1645 at: 17,
1646 symbol: "puts".to_owned(),
1647 kind: Reference::Call,
1648 addend: -4,
1649 after: 0,
1650 });
1651 text
1652 }
1653
1654 #[test]
1661 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1662 let sections =
1663 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1664 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1665 .expect("an object");
1666 let file = object::File::parse(&bytes[..]).expect("a readable object");
1667 assert_eq!(lives_in(&file, "f"), ".text.f");
1668 assert_eq!(lives_in(&file, "g"), ".text.g");
1669 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1670 for name in ["f", "g"] {
1673 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1674 assert_eq!(symbol.address(), 0, "{name}");
1675 assert_eq!(symbol.size(), 6, "{name}");
1676 }
1677 let section = file.section_by_name(".text.g").expect("the second function");
1678 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1679 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1682 }
1683
1684 #[test]
1690 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1691 let sections =
1692 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1693 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1694 .expect("an object");
1695 let file = object::File::parse(&bytes[..]).expect("a readable object");
1696 for name in [".text.f", ".text.g"] {
1697 let section = file.section_by_name(name).expect("a function");
1698 let (offset, _) = section.relocations().next().expect("the call in it");
1699 assert_eq!(offset, 1, "{name}");
1702 assert_eq!(section.relocations().count(), 1, "{name}");
1703 }
1704 }
1705
1706 fn switching() -> Text {
1708 let mut text = two();
1709 let name = ".Lg_j0".to_owned();
1710 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5] });
1711 text
1712 }
1713
1714 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1716 let section = file.section_by_name(section).expect("the table's section");
1717 section
1718 .relocations()
1719 .map(|(offset, reloc)| {
1720 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1721 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1722 panic!("a cell against something that is not a symbol");
1723 };
1724 let symbol = file.symbol_by_index(index).expect("a symbol");
1725 assert_eq!(symbol.kind(), SymbolKind::Section);
1726 let at = symbol.section_index().expect("a section symbol is in one");
1727 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1728 (offset, name.to_owned(), reloc.addend())
1729 })
1730 .collect()
1731 }
1732
1733 #[test]
1734 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1735 let mut text = switching();
1738 text.relocs[1].symbol = ".Lg_j0".to_owned();
1739 text.relocs[1].kind = Reference::Data;
1740 let bytes =
1741 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1742 .expect("an object");
1743 let file = object::File::parse(&bytes[..]).expect("a readable object");
1744 let rodata = file.section_by_name(".rodata").expect("the table's section");
1745 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
1746 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
1747 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
1748 let (at, reloc) = file
1749 .section_by_name(".text")
1750 .expect("the code")
1751 .relocations()
1752 .find(|(at, _)| *at == 17)
1753 .expect("the reference to the table");
1754 assert_eq!((at, reloc.addend()), (17, -4));
1755 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1756 panic!("a reference against something that is not a symbol");
1757 };
1758 let symbol = file.symbol_by_index(index).expect("a symbol");
1759 assert_eq!(symbol.section_index(), Some(rodata.index()));
1760 assert_eq!(symbol.kind(), SymbolKind::Section);
1761 assert_eq!(
1764 cells(&file, ".rodata"),
1765 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
1766 );
1767 }
1768
1769 #[test]
1770 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
1771 let sections =
1772 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
1773 let bytes =
1774 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
1775 .expect("an object");
1776 let file = object::File::parse(&bytes[..]).expect("a readable object");
1777 assert_eq!(
1779 cells(&file, ".rodata.g"),
1780 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
1781 );
1782 }
1783
1784 #[test]
1785 fn a_jump_table_outside_the_code_is_refused_on_windows() {
1786 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1787 let written = write(
1788 &switching(),
1789 &Data::default(),
1790 &[],
1791 &target,
1792 Output::default(),
1793 &Info::default(),
1794 );
1795 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
1796 }
1797
1798 fn variable(name: &str, place: Place) -> Object {
1800 Object {
1801 name: name.to_owned(),
1802 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1803 size: 4,
1804 align: 4,
1805 place,
1806 binding: Binding::Global,
1807 visibility: Visibility::Default,
1808 relocs: Vec::new(),
1809 }
1810 }
1811
1812 fn measured() -> (Text, Data) {
1814 let mut text = calling("puts");
1815 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1816 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1817 let mut table = variable("table", Place::ReadOnly);
1818 table.bytes = vec![0; 8];
1819 table.size = 8;
1820 let apart = |at, to: &str, from: &str| Apart {
1821 object: 0,
1822 at,
1823 to: to.to_owned(),
1824 from: from.to_owned(),
1825 addend: 0,
1826 bytes: 4,
1827 };
1828 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1829 (text, Data { apart, weak: Vec::new(), objects: vec![table] })
1830 }
1831
1832 #[test]
1833 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1834 let (text, data) = measured();
1835 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1836 .expect("an object");
1837 let file = object::File::parse(&bytes[..]).expect("a readable object");
1838 let section = file.section_by_name(".rodata").expect("a read only section");
1839 assert_eq!(section.relocations().count(), 0);
1840 let image = section.data().expect("the image");
1841 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1842 }
1843
1844 #[test]
1845 fn a_distance_between_labels_in_two_sections_is_refused() {
1846 let (mut text, data) = measured();
1849 text.bytes.resize(22, 0x90);
1850 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1851 text.labels[1].at = 17;
1852 let output =
1853 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1854 let refused = write(&text, &data, &[], &target(), output, &Info::default());
1855 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1856 }
1857
1858 fn holding(object: Object) -> Vec<u8> {
1860 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
1861 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1862 .expect("an object")
1863 }
1864
1865 #[test]
1866 fn what_a_variable_is_decides_which_section_it_goes_in() {
1867 for (place, wanted) in [
1868 (Place::Written, ".data"),
1869 (Place::ReadOnly, ".rodata"),
1870 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1871 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1872 (Place::Zero, ".bss"),
1873 (Place::Thread { zero: false }, ".tdata"),
1874 (Place::Thread { zero: true }, ".tbss"),
1875 (Place::Named(".init_array".to_owned()), ".init_array"),
1876 ] {
1877 let bytes = holding(variable("x", place.clone()));
1878 let file = object::File::parse(&bytes[..]).expect("a readable object");
1879 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1880 assert_eq!(section.size(), 4, "{place:?}");
1881 let carried = section.data().expect("the bytes").len();
1884 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1885 }
1886 }
1887
1888 #[test]
1894 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1895 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1896 let bytes = holding(variable("counter", place.clone()));
1897 let file = object::File::parse(&bytes[..]).expect("a readable object");
1898 let symbol = file
1899 .symbols()
1900 .find(|symbol| symbol.name() == Ok("counter"))
1901 .unwrap_or_else(|| panic!("{place:?}"));
1902 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1903 }
1904 }
1905
1906 #[test]
1912 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1913 for (name, wanted) in [
1914 (".init_array", elf::SHT_INIT_ARRAY),
1915 (".init_array.00101", elf::SHT_INIT_ARRAY),
1916 (".fini_array", elf::SHT_FINI_ARRAY),
1917 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1918 (".init_arrays", elf::SHT_PROGBITS),
1919 ] {
1920 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1921 let file = object::File::parse(&bytes[..]).expect("a readable object");
1922 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1923 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1924 panic!("{name} is not an elf section");
1925 };
1926 assert_eq!(sh_type, wanted, "{name}");
1927 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1928 }
1929 }
1930
1931 #[test]
1937 fn two_variables_in_one_named_section_share_it() {
1938 let objects = vec![
1939 variable("x", Place::Named(".init_array".to_owned())),
1940 variable("y", Place::Named(".init_array".to_owned())),
1941 ];
1942 let data = Data { apart: Vec::new(), weak: Vec::new(), objects };
1943 let bytes =
1944 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1945 .expect("an object");
1946 let file = object::File::parse(&bytes[..]).expect("a readable object");
1947 let named: Vec<_> =
1948 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1949 assert_eq!(named.len(), 1);
1950 assert_eq!(named[0].size(), 8);
1951 }
1952
1953 #[test]
1957 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1958 let sections =
1959 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1960 for (place, wanted) in [
1961 (Place::Written, ".data.x"),
1962 (Place::ReadOnly, ".rodata.x"),
1963 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1964 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1965 (Place::Zero, ".bss.x"),
1966 (Place::Thread { zero: false }, ".tdata.x"),
1967 (Place::Thread { zero: true }, ".tbss.x"),
1968 ] {
1969 let data = Data {
1970 apart: Vec::new(),
1971 weak: Vec::new(),
1972 objects: vec![variable("x", place.clone())],
1973 };
1974 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1975 .expect("object");
1976 let file = object::File::parse(&bytes[..]).expect("a readable object");
1977 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1978 let section = file.section_by_name(wanted).expect("the section it named");
1979 assert_eq!(section.size(), 4, "{place:?}");
1980 let carried = section.data().expect("the bytes").len();
1983 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1984 }
1985 }
1986
1987 #[test]
1991 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1992 let sections =
1993 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1994 let named = Place::Named(".init_array".to_owned());
1995 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1996 let bytes = write(
1997 &Text::default(),
1998 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1999 &[],
2000 &target(),
2001 sections,
2002 &Info::default(),
2003 )
2004 .expect("object");
2005 let file = object::File::parse(&bytes[..]).expect("a readable object");
2006 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2007 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2008 assert_eq!(lives_in(&file, "n"), ".init_array");
2009 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2010 }
2011
2012 #[test]
2016 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2017 let sections =
2018 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2019 let pointer = Object {
2020 bytes: vec![0; 8],
2021 size: 8,
2022 align: 8,
2023 relocs: vec![Reloc {
2024 at: 0,
2025 symbol: "y".to_owned(),
2026 kind: Reference::Address { bytes: 8 },
2027 addend: 0,
2028 after: 0,
2029 }],
2030 ..variable("p", Place::Written)
2031 };
2032 let objects = vec![variable("first", Place::Written), pointer];
2033 let bytes = write(
2034 &Text::default(),
2035 &Data { apart: Vec::new(), weak: Vec::new(), objects },
2036 &[],
2037 &target(),
2038 sections,
2039 &Info::default(),
2040 )
2041 .expect("object");
2042 let file = object::File::parse(&bytes[..]).expect("a readable object");
2043 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2044 let (offset, reloc) = section.relocations().next().expect("one relocation");
2045 assert_eq!(offset, 0);
2048 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2049 }
2050
2051 #[test]
2059 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2060 let place = Place::RelocReadOnly { local: true };
2061 let data = Data {
2062 apart: Vec::new(),
2063 weak: Vec::new(),
2064 objects: vec![variable("first", place.clone()), variable("second", place)],
2065 };
2066 let bytes =
2067 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2068 .expect("an object");
2069 let file = object::File::parse(&bytes[..]).expect("a readable object");
2070 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2071 assert_eq!(named, 1, "one section holding both, not one each");
2072 }
2073
2074 #[test]
2075 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2076 let mut data = Data {
2077 apart: Vec::new(),
2078 weak: Vec::new(),
2079 objects: vec![variable("first", Place::Written)],
2080 };
2081 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2082 let bytes =
2083 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2084 .expect("an object");
2085 let file = object::File::parse(&bytes[..]).expect("a readable object");
2086 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2087 assert_eq!(second.kind(), SymbolKind::Data);
2088 assert_eq!(second.size(), 4);
2089 assert_eq!(second.address(), 16);
2093 }
2094
2095 #[test]
2096 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2097 for (binding, global, weak) in [
2098 (Binding::Global, true, false),
2099 (Binding::Local, false, false),
2100 (Binding::Weak, true, true),
2101 ] {
2102 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2103 let file = object::File::parse(&bytes[..]).expect("a readable object");
2104 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2105 assert_eq!(x.is_global(), global, "{binding:?}");
2106 assert_eq!(x.is_weak(), weak, "{binding:?}");
2107 }
2108 }
2109
2110 #[test]
2111 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2112 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2113 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2114 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2115 assert!(x.is_common(), "the linker merges every definition of this name into one");
2116 assert_eq!(x.size(), 4);
2117 assert_eq!(x.address(), 0);
2121 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2122 }
2123
2124 #[test]
2125 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2126 let object = Object {
2127 bytes: vec![0; 8],
2128 size: 8,
2129 align: 8,
2130 relocs: vec![Reloc {
2131 at: 0,
2132 symbol: "y".to_owned(),
2133 kind: Reference::Address { bytes: 8 },
2134 addend: 16,
2135 after: 0,
2136 }],
2137 ..variable("p", Place::Written)
2138 };
2139 let bytes = holding(object);
2140 let file = object::File::parse(&bytes[..]).expect("a readable object");
2141 let section = file.section_by_name(".data").expect("a data section");
2142 let (offset, reloc) = section.relocations().next().expect("one relocation");
2143 assert_eq!(offset, 0);
2144 assert_eq!(reloc.addend(), 16);
2145 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2146 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2147 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2148 }
2149
2150 #[test]
2157 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2158 let mut text = Text::default();
2159 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2160 text.bytes.resize(8, 0x90);
2161 text.relocs.push(Reloc {
2162 at: 1,
2163 symbol: "hook".to_owned(),
2164 kind: Reference::Call,
2165 addend: -4,
2166 after: 0,
2167 });
2168 let data = Data {
2169 apart: Vec::new(),
2170 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2171 objects: vec![],
2172 };
2173 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2174 .expect("an object");
2175 let file = object::File::parse(&bytes[..]).expect("a readable object");
2176
2177 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2178 assert!(hook.is_undefined(), "nothing here defines it");
2179 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2180
2181 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2185 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2186 }
2187
2188 #[test]
2203 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2204 let mut text = Text::default();
2205 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2206 text.bytes.resize(16, 0x90);
2207 text.relocs.push(Reloc {
2208 at: 3,
2209 symbol: "flags".to_owned(),
2210 kind: Reference::Thread,
2211 addend: -4,
2212 after: 0,
2213 });
2214 text.relocs.push(Reloc {
2217 at: 10,
2218 symbol: "shared".to_owned(),
2219 kind: Reference::Got,
2220 addend: -4,
2221 after: 0,
2222 });
2223 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![] };
2224 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2225 .expect("an object");
2226 let file = object::File::parse(&bytes[..]).expect("a readable object");
2227
2228 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2229 assert!(flags.is_undefined(), "nothing here defines it");
2230 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2231
2232 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2233 assert!(shared.is_undefined(), "nothing here defines this one either");
2234 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2235 }
2236
2237 #[test]
2239 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2240 let mut data = Data {
2241 apart: Vec::new(),
2242 weak: Vec::new(),
2243 objects: vec![variable("first", Place::Written)],
2244 };
2245 data.objects.push(Object {
2246 bytes: vec![0; 16],
2247 size: 16,
2248 align: 8,
2249 relocs: vec![Reloc {
2250 at: 8,
2251 symbol: "y".to_owned(),
2252 kind: Reference::Address { bytes: 8 },
2253 addend: 0,
2254 after: 0,
2255 }],
2256 ..variable("second", Place::Written)
2257 });
2258 let bytes =
2259 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2260 .expect("an object");
2261 let file = object::File::parse(&bytes[..]).expect("a readable object");
2262 let section = file.section_by_name(".data").expect("a data section");
2263 let (offset, _) = section.relocations().next().expect("one relocation");
2264 assert_eq!(offset, 16);
2267 }
2268
2269 #[test]
2270 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2271 let data = Data {
2272 apart: Vec::new(),
2273 weak: Vec::new(),
2274 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2275 };
2276 let aliases = [Alias {
2277 name: "b".to_owned(),
2278 target: "a".to_owned(),
2279 binding: Binding::Global,
2280 visibility: Visibility::Default,
2281 }];
2282 let bytes = write(
2283 &Text::default(),
2284 &data,
2285 &aliases,
2286 &target(),
2287 Output::default(),
2288 &Info::default(),
2289 )
2290 .expect("an object");
2291 let file = object::File::parse(&bytes[..]).expect("a readable object");
2292 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2293 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2294 assert_eq!(b.address(), a.address(), "the same place");
2295 assert_eq!(b.size(), a.size());
2296 assert_eq!(b.section_index(), a.section_index());
2297 assert!(a.is_local(), "the target was written `static`");
2300 assert!(b.is_global(), "and the name given to it was not");
2301 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2303 }
2304
2305 #[test]
2306 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2307 let text = calling("puts");
2308 let aliases = [Alias {
2309 name: "g".to_owned(),
2310 target: "f".to_owned(),
2311 binding: Binding::Weak,
2312 visibility: Visibility::Default,
2313 }];
2314 let bytes = write(
2315 &text,
2316 &Data::default(),
2317 &aliases,
2318 &target(),
2319 Output::default(),
2320 &Info::default(),
2321 )
2322 .expect("an object");
2323 let file = object::File::parse(&bytes[..]).expect("a readable object");
2324 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2325 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2326 assert_eq!(g.address(), f.address());
2327 assert_eq!(g.size(), f.size());
2328 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2329 assert!(g.is_weak(), "so that a program may define the name itself instead");
2330 }
2331
2332 #[test]
2335 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2336 let aliases = [Alias {
2337 name: "b".to_owned(),
2338 target: "a".to_owned(),
2339 binding: Binding::Global,
2340 visibility: Visibility::Default,
2341 }];
2342 let error = write(
2343 &Text::default(),
2344 &Data::default(),
2345 &aliases,
2346 &target(),
2347 Output::default(),
2348 &Info::default(),
2349 )
2350 .expect_err("nothing to point at");
2351 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2352 }
2353
2354 #[test]
2355 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2356 let text = calling("puts");
2357 for triple in [
2358 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2359 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2360 ] {
2361 let error = write(
2362 &text,
2363 &Data::default(),
2364 &[],
2365 &TargetInfo::new(triple),
2366 Output::default(),
2367 &Info::default(),
2368 )
2369 .expect_err("no writer");
2370 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2371 }
2372 }
2373
2374 #[test]
2380 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2381 let mut text = calling("puts");
2382 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2383 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2384 text.bytes.resize(33, 0x90);
2385 let data = Data {
2386 apart: Vec::new(),
2387 weak: Vec::new(),
2388 objects: vec![variable("seen", Place::Written), {
2389 let mut quiet = variable("quiet", Place::Zero);
2390 quiet.binding = Binding::Local;
2391 quiet
2392 }],
2393 };
2394 let aliases = [Alias {
2395 name: "second".to_owned(),
2396 target: "f".to_owned(),
2397 binding: Binding::Global,
2398 visibility: Visibility::Default,
2399 }];
2400
2401 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2402 assert_eq!(names, ["f", "shared", "seen", "second"]);
2403
2404 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2405 .expect("an object");
2406 let file = object::File::parse(&bytes[..]).expect("a readable object");
2407 let found: Vec<String> = file
2408 .symbols()
2409 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2410 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2411 .collect();
2412 let mut sorted = names.clone();
2413 sorted.sort();
2414 let mut theirs = found;
2415 theirs.sort();
2416 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2417 }
2418
2419 fn windows() -> TargetInfo {
2421 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2422 }
2423
2424 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2426 let file = object::File::parse(bytes).expect("a readable object");
2427 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2428 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2429 }
2430
2431 #[test]
2432 fn a_windows_target_is_written_rather_than_refused() {
2433 let text = calling("puts");
2434 let bytes =
2435 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2436 .expect("an object");
2437 let file = object::File::parse(&bytes[..]).expect("a readable object");
2438 assert_eq!(file.format(), BinaryFormat::Coff);
2439 let section = file.section_by_name(".text").expect("a text section");
2440 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2441 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2442 assert!(names.contains(&"f"), "{names:?}");
2443 assert!(names.contains(&"puts"), "{names:?}");
2444 }
2445
2446 #[test]
2454 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2455 for (after, typ) in [
2456 (0, pe::IMAGE_REL_AMD64_REL32),
2457 (1, pe::IMAGE_REL_AMD64_REL32_1),
2458 (4, pe::IMAGE_REL_AMD64_REL32_4),
2459 (5, pe::IMAGE_REL_AMD64_REL32_5),
2460 ] {
2461 let mut text = calling("puts");
2462 text.relocs[0].addend = -4 - i64::from(after);
2465 text.relocs[0].after = after;
2466 text.bytes.resize(6 + after as usize, 0x90);
2467 text.funcs[0].len = text.bytes.len();
2468 let bytes = write(
2469 &text,
2470 &Data::default(),
2471 &[],
2472 &windows(),
2473 Output::default(),
2474 &Info::default(),
2475 )
2476 .expect("an object");
2477 let file = object::File::parse(&bytes[..]).expect("a readable object");
2478 let section = file.section_by_name(".text").expect("a text section");
2479 let (_, reloc) = section.relocations().next().expect("the relocation");
2480 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2481 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2484 }
2485 }
2486
2487 #[test]
2490 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2491 let mut text = calling("puts");
2492 text.relocs[0].addend = 12;
2493 let bytes =
2494 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2495 .expect("an object");
2496 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2497 }
2498
2499 #[test]
2500 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2501 let object = Object {
2502 bytes: vec![0; 8],
2503 size: 8,
2504 align: 8,
2505 relocs: vec![Reloc {
2506 at: 0,
2507 symbol: "y".to_owned(),
2508 kind: Reference::Address { bytes: 8 },
2509 addend: 0,
2510 after: 0,
2511 }],
2512 ..variable("p", Place::Written)
2513 };
2514 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
2515 let bytes =
2516 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2517 .expect("an object");
2518 let file = object::File::parse(&bytes[..]).expect("a readable object");
2519 let section = file.section_by_name(".data").expect("a data section");
2520 let (_, reloc) = section.relocations().next().expect("the relocation");
2521 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2522 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2523 }
2524
2525 #[test]
2528 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2529 for local in [false, true] {
2530 let data = Data {
2531 apart: Vec::new(),
2532 weak: Vec::new(),
2533 objects: vec![variable("p", Place::RelocReadOnly { local })],
2534 };
2535 let bytes = write(
2536 &Text::default(),
2537 &data,
2538 &[],
2539 &windows(),
2540 Output::default(),
2541 &Info::default(),
2542 )
2543 .expect("an object");
2544 let file = object::File::parse(&bytes[..]).expect("a readable object");
2545 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2546 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2547 }
2548 }
2549
2550 #[test]
2553 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2554 let text = calling("puts");
2555 let output = Output { property: Property { features: 3 }, ..Output::default() };
2556 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2557 .expect("an object");
2558 let file = object::File::parse(&bytes[..]).expect("a readable object");
2559 assert!(file.section_by_name(".note.GNU-stack").is_none());
2560 assert!(file.section_by_name(".note.gnu.property").is_none());
2561 }
2562
2563 #[test]
2568 fn what_this_format_cannot_say_is_refused_by_name() {
2569 let ordinary = Text::default();
2570 let empty = Data::default();
2571
2572 let mut thread = Data::default();
2573 thread.objects.push(variable("t", Place::Thread { zero: true }));
2574
2575 let mut gathered = Data::default();
2576 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2577
2578 let mut table = calling("puts");
2579 table.relocs[0].kind = Reference::Got;
2580
2581 let mut room = calling("puts");
2582 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2583
2584 let cases: [(&str, &Text, &Data); 4] = [
2585 ("thread-local", &ordinary, &thread),
2586 ("startup", &ordinary, &gathered),
2587 ("table", &table, &empty),
2588 ("patcher", &room, &empty),
2589 ];
2590 for (what, text, data) in cases {
2591 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2592 .expect_err("something this format cannot write");
2593 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2594 }
2595 }
2596
2597 #[test]
2600 fn a_thread_local_variable_goes_in_the_tls_section() {
2601 let mut thread = Data::default();
2602 thread.objects.push(variable("t", Place::Thread { zero: false }));
2603 let bytes =
2604 write(&Text::default(), &thread, &[], &windows(), Output::default(), &Info::default())
2605 .expect("an object");
2606 let file = object::File::parse(&bytes[..]).expect("a readable object");
2607 assert!(file.section_by_name(".tls$").is_some());
2608 }
2609
2610 #[test]
2614 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2615 let mut text = calling("puts");
2616 text.funcs[0].visibility = Visibility::Hidden;
2617 let bytes =
2618 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2619 .expect("an object");
2620 let file = object::File::parse(&bytes[..]).expect("a readable object");
2621 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2622 assert!(symbol.is_global(), "a name others may use either way");
2623 }
2624
2625 #[test]
2626 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2627 let text = calling("puts");
2628 let data = Data {
2629 apart: Vec::new(),
2630 weak: Vec::new(),
2631 objects: vec![variable("shared", Place::Written)],
2632 };
2633 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2634 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2635 }
2636
2637 #[test]
2641 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2642 let text = calling("puts");
2643 for triple in [
2644 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2645 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2646 ] {
2647 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2648 .expect_err("no writer");
2649 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2650 }
2651 }
2652}