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 { .. }) {
834 return why(format!("'{}' is thread-local and this format is not", object.name));
835 }
836 let Place::Named(name) = &object.place else { continue };
837 if Array::of(name).is_some() {
838 return why(format!("'{name}' is not a list the startup code here gathers"));
839 }
840 }
841 Ok(())
842}
843
844pub fn defines(
868 text: &Text,
869 data: &Data,
870 aliases: &[Alias],
871 target: &TargetInfo,
872) -> Result<Vec<String>, Error> {
873 if target.tuple.arch() != Arch::X86_64 || Flavour::of(target).is_none() {
874 return Err(Error::Format { triple: target.tuple.to_string() });
875 }
876 let names = text
877 .funcs
878 .iter()
879 .filter(|func| func.binding != Binding::Local)
880 .map(|func| func.name.clone())
881 .chain(
882 data.objects
883 .iter()
884 .filter(|object| object.binding != Binding::Local)
885 .map(|object| object.name.clone()),
886 )
887 .chain(
888 aliases
889 .iter()
890 .filter(|alias| alias.binding != Binding::Local)
891 .map(|alias| alias.name.clone()),
892 )
893 .collect();
894 Ok(names)
895}
896
897fn put(
904 obj: &mut Writer<'_>,
905 object: &Object,
906 named: &mut HashMap<String, object::write::SectionId>,
907 sections: Sections,
908 flavour: Flavour,
909) -> (SymbolSection, u64) {
910 if sections.data {
916 if let Some(name) = object.place.split(&object.name) {
917 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
918 let offset = if carries_no_bytes(&object.place) {
919 obj.append_section_bss(section, object.size, object.align)
920 } else {
921 obj.append_section_data(section, &object.bytes, object.align)
922 };
923 return (SymbolSection::Section(section), offset);
924 }
925 }
926 let section = match &object.place {
927 Place::Written => obj.section_id(StandardSection::Data),
928 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
929 Place::RelocReadOnly { local } => match flavour.rel_ro_local().filter(|_| *local) {
935 Some(name) => made(obj, named, name, SectionKind::ReadOnlyDataWithRel),
936 None => obj.section_id(StandardSection::ReadOnlyDataWithRel),
937 },
938 Place::Zero => obj.section_id(StandardSection::UninitializedData),
939 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
940 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
941 Place::Merged => return (SymbolSection::Common, 0),
942 Place::Named(name) => {
948 let section = made(obj, named, name, SectionKind::Data);
949 if let Some(flags) = Array::of(name).and_then(|array| flavour.gathered(array)) {
950 obj.section_mut(section).flags = flags;
951 }
952 section
953 }
954 };
955 let offset = if carries_no_bytes(&object.place) {
956 obj.append_section_bss(section, object.size, object.align)
957 } else {
958 obj.append_section_data(section, &object.bytes, object.align)
959 };
960 (SymbolSection::Section(section), offset)
961}
962
963fn tables(
976 obj: &mut Writer<'_>,
977 text: &Text,
978 split: &[(object::write::SectionId, u64)],
979 named: &mut HashMap<String, object::write::SectionId>,
980 sections: Sections,
981 flavour: Flavour,
982) -> Result<HashMap<String, (object::write::SectionId, u64)>, Error> {
983 let mut placed = HashMap::new();
984 if text.tables.is_empty() {
985 return Ok(placed);
986 }
987 if flavour != Flavour::Elf {
988 let why = "a jump table outside the code is written on ELF only".to_owned();
989 return Err(Error::Refused { why });
990 }
991 let flags = flavour.reloc(Reference::Away, 0).ok_or_else(|| Error::Refused {
992 why: "no relocation is a distance from where it is written".to_owned(),
993 })?;
994 for table in &text.tables {
995 let func = text.funcs.get(table.func).ok_or_else(|| Error::Refused {
996 why: format!("'{}' belongs to function {}, which is not here", table.name, table.func),
997 })?;
998 let section = if sections.data {
999 let name = format!(".rodata.{}", func.name);
1000 made(obj, named, &name, SectionKind::ReadOnlyData)
1001 } else {
1002 obj.section_id(StandardSection::ReadOnlyData)
1003 };
1004 let offset = obj.append_section_data(section, &vec![0; 4 * table.cells.len()], 4);
1005 placed.insert(table.name.clone(), (section, offset));
1006 let (code, at) = split[table.func];
1007 let symbol = obj.section_symbol(code);
1008 for (index, &cell) in table.cells.iter().enumerate() {
1009 let place = 4 * index as u64;
1010 let addend = at as i64 + cell as i64 + place as i64;
1011 let record = Relocation { offset: offset + place, symbol, addend, flags };
1012 obj.add_relocation(section, record)
1013 .map_err(|why| Error::Refused { why: why.to_string() })?;
1014 }
1015 }
1016 Ok(placed)
1017}
1018
1019fn carries_no_bytes(place: &Place) -> bool {
1025 matches!(place, Place::Zero | Place::Thread { zero: true })
1026}
1027
1028fn made(
1036 obj: &mut Writer<'_>,
1037 named: &mut HashMap<String, object::write::SectionId>,
1038 name: &str,
1039 kind: SectionKind,
1040) -> object::write::SectionId {
1041 if let Some(section) = named.get(name) {
1042 return *section;
1043 }
1044 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
1045 named.insert(name.to_owned(), section);
1046 section
1047}
1048
1049fn kind_of(place: &Place) -> SectionKind {
1058 match place {
1059 Place::ReadOnly => SectionKind::ReadOnlyData,
1060 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
1061 Place::Zero => SectionKind::UninitializedData,
1062 Place::Thread { zero: false } => SectionKind::Tls,
1063 Place::Thread { zero: true } => SectionKind::UninitializedTls,
1064 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
1065 }
1066}
1067
1068fn add(
1075 obj: &mut Writer<'_>,
1076 section: object::write::SectionId,
1077 at: u64,
1078 reloc: &Reloc,
1079 symbols: &BTreeMap<String, SymbolId>,
1080 flavour: Flavour,
1081) -> Result<(), Error> {
1082 let flags = flavour
1083 .reloc(reloc.kind, reloc.after)
1084 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
1085 obj.add_relocation(
1086 section,
1087 Relocation { offset: at, symbol: symbols[&reloc.symbol], addend: reloc.addend, flags },
1088 )
1089 .map_err(|why| Error::Refused { why: why.to_string() })
1090}
1091
1092pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
1105 match binding {
1106 Binding::Local => SymbolScope::Compilation,
1107 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
1108 }
1109}
1110
1111#[cfg(test)]
1112mod tests {
1113 use super::*;
1114
1115 use object::read::elf::Sym as _;
1116 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
1117 use object::{elf, pe};
1118 use rucc_target::{Arch, Env, Os, Triple};
1119
1120 use crate::elf::PATCHABLE;
1121 use crate::section::{Extent, Marker, Patch, Reloc};
1122
1123 fn target() -> TargetInfo {
1125 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1126 }
1127
1128 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
1133 Extent {
1134 name,
1135 start,
1136 len,
1137 align: crate::FUNC_ALIGN,
1138 binding,
1139 visibility: Visibility::Default,
1140 patch: None,
1141 landings: Vec::new(),
1142 }
1143 }
1144
1145 fn calling(name: &str) -> Text {
1147 Text {
1148 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
1149 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
1150 relocs: vec![Reloc {
1151 at: 1,
1152 symbol: name.to_owned(),
1153 kind: Reference::Call,
1154 addend: -4,
1155 after: 0,
1156 }],
1157 ..Text::default()
1158 }
1159 }
1160
1161 #[test]
1162 fn the_bytes_come_back_out_of_the_section_they_went_into() {
1163 let text = calling("puts");
1164 let bytes =
1165 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1166 .expect("an object");
1167 let file = object::File::parse(&bytes[..]).expect("a readable object");
1168 let section = file.section_by_name(".text").expect("a text section");
1169 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
1170 }
1171
1172 #[test]
1173 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1174 let mut text = calling("puts");
1175 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1176 text.bytes.resize(17, 0x90);
1177 let bytes =
1178 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1179 .expect("an object");
1180 let file = object::File::parse(&bytes[..]).expect("a readable object");
1181 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
1182 assert_eq!(g.address(), 16);
1183 assert_eq!(g.size(), 1);
1184 assert_eq!(g.kind(), SymbolKind::Text);
1185 assert!(g.is_global(), "nothing said otherwise about this one");
1186 }
1187
1188 #[test]
1189 fn a_function_no_other_file_can_see_is_a_local_symbol() {
1190 let mut text = calling("puts");
1191 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1192 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1193 text.bytes.resize(33, 0x90);
1194 let bytes =
1195 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1196 .expect("an object");
1197 let file = object::File::parse(&bytes[..]).expect("a readable object");
1198 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
1199 assert!(hidden.is_local(), "a static function must not be offered to the linker");
1202 assert!(!hidden.is_weak());
1203 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
1204 assert!(shared.is_weak(), "a weak function has to be able to lose");
1205 assert!(shared.is_global());
1206 }
1207
1208 #[test]
1225 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
1226 let mut text = calling("puts");
1227 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
1228 text.funcs[0].start = 3;
1229 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
1230 text.relocs[0].at = 4;
1231 let bytes =
1232 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1233 .expect("an object");
1234 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1235 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
1236 assert_eq!(section.size(), 8, "one address, and this file defines one function");
1237 assert_eq!(section.align(), 8);
1238 let header = section.elf_section_header();
1239 assert_eq!(
1240 header.sh_flags.get(Endianness::Little),
1241 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
1242 );
1243 let index = file.section_by_name(".text").expect("a text section").index().0;
1246 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
1247 assert_ne!(index, 0);
1248
1249 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
1251 panic!("one address in the record")
1252 };
1253 assert_eq!(*at, 0);
1254 assert_eq!(reloc.addend(), 0);
1255 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1256 }
1257
1258 #[test]
1260 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
1261 let text = calling("puts");
1262 let bytes =
1263 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1264 .expect("an object");
1265 let file = object::File::parse(&bytes[..]).expect("a readable object");
1266 assert!(file.section_by_name(PATCHABLE).is_none());
1267 }
1268
1269 #[test]
1275 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
1276 let mut text = calling("puts");
1277 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
1278 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1279 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
1280 text.bytes.resize(17, 0x90);
1281 let output =
1282 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1283 let bytes = write(&text, &Data::default(), &[], &target(), output, &Info::default())
1284 .expect("an object");
1285 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1286 let links: Vec<usize> = file
1287 .sections()
1288 .filter(|section| section.name() == Ok(PATCHABLE))
1289 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
1290 .collect();
1291 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
1292 assert_eq!(links, [index(".text.f"), index(".text.g")]);
1293 }
1294
1295 #[test]
1296 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
1297 let mut text = calling("puts");
1298 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1299 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
1300 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
1301 text.bytes.resize(49, 0x90);
1302 let bytes =
1303 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1304 .expect("an object");
1305 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1306 let visibility = |name: &str| {
1307 file.symbols()
1308 .find(|s| s.name() == Ok(name))
1309 .expect("the function")
1310 .elf_symbol()
1311 .st_visibility()
1312 };
1313 assert_eq!(visibility("g"), elf::STV_DEFAULT);
1315 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
1316 assert_eq!(visibility("s"), elf::STV_DEFAULT);
1319 }
1320
1321 #[test]
1331 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
1332 let mut text = calling("puts");
1333 for (index, (name, seen)) in
1334 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
1335 {
1336 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
1337 func.visibility = seen;
1338 text.funcs.push(func);
1339 }
1340 text.bytes.resize(49, 0x90);
1341 let mut data = Data::default();
1342 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
1343 let mut object = variable(name, Place::Written);
1344 object.visibility = seen;
1345 data.objects.push(object);
1346 }
1347 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1348 .expect("an object");
1349 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1350 let visibility = |name: &str| {
1351 file.symbols()
1352 .find(|s| s.name() == Ok(name))
1353 .expect("the symbol")
1354 .elf_symbol()
1355 .st_visibility()
1356 };
1357 assert_eq!(visibility("h"), elf::STV_HIDDEN);
1358 assert_eq!(visibility("p"), elf::STV_PROTECTED);
1359 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
1360 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
1361 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
1364 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
1365 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
1366 }
1367
1368 #[test]
1369 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
1370 let bytes = write(
1371 &calling("puts"),
1372 &Data::default(),
1373 &[],
1374 &target(),
1375 Output::default(),
1376 &Info::default(),
1377 )
1378 .expect("an object");
1379 let file = object::File::parse(&bytes[..]).expect("a readable object");
1380 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1381 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1382 }
1383
1384 #[test]
1385 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1386 for (reference, wanted) in [
1387 (Reference::Call, elf::R_X86_64_PLT32),
1388 (Reference::Data, elf::R_X86_64_PC32),
1389 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1390 (Reference::GotBare, elf::R_X86_64_GOTPCRELX),
1391 (Reference::GotKept, elf::R_X86_64_GOTPCREL),
1392 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1393 ] {
1394 let mut text = calling("puts");
1395 text.relocs[0].kind = reference;
1396 let bytes =
1397 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1398 .expect("an object");
1399 let file = object::File::parse(&bytes[..]).expect("a readable object");
1400 let section = file.section_by_name(".text").expect("a text section");
1401 let (offset, reloc) = section.relocations().next().expect("one relocation");
1402 assert_eq!(offset, 1);
1403 assert_eq!(reloc.addend(), -4);
1404 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1405 }
1406 }
1407
1408 #[test]
1409 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1410 let mut text = calling("puts");
1411 text.relocs.push(Reloc {
1412 at: 1,
1413 symbol: "puts".to_owned(),
1414 kind: Reference::Call,
1415 addend: -4,
1416 after: 0,
1417 });
1418 let bytes =
1419 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1420 .expect("an object");
1421 let file = object::File::parse(&bytes[..]).expect("a readable object");
1422 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1423 }
1424
1425 #[test]
1426 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1427 let text = calling("f");
1428 let bytes =
1429 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1430 .expect("an object");
1431 let file = object::File::parse(&bytes[..]).expect("a readable object");
1432 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1433 let f = found.next().expect("the function");
1434 assert!(!f.is_undefined(), "the file defines it");
1435 assert!(found.next().is_none(), "and defines it once");
1436 }
1437
1438 #[test]
1439 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1440 let bytes = write(
1441 &calling("puts"),
1442 &Data::default(),
1443 &[],
1444 &target(),
1445 Output::default(),
1446 &Info::default(),
1447 )
1448 .expect("an object");
1449 let file = object::File::parse(&bytes[..]).expect("a readable object");
1450 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1451 assert!(note.data().expect("no bytes").is_empty());
1452 }
1453
1454 #[test]
1461 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1462 let property = Property { features: Property::IBT | Property::SHSTK };
1463 let output = Output { property, ..Output::default() };
1464 let bytes =
1465 write(&calling("puts"), &Data::default(), &[], &target(), output, &Info::default())
1466 .expect("an object");
1467 let file = object::File::parse(&bytes[..]).expect("a readable object");
1468 let note = file.section_by_name(".note.gnu.property").expect("the note");
1469 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1470 let want: Vec<u8> = [
1471 4u32,
1472 16,
1473 5,
1474 u32::from_le_bytes(*b"GNU\0"),
1475 Property::X86_FEATURES,
1476 4,
1477 Property::IBT | Property::SHSTK,
1478 0,
1479 ]
1480 .iter()
1481 .flat_map(|word| word.to_le_bytes())
1482 .collect();
1483 assert_eq!(note.data().expect("the bytes"), &want[..]);
1484 }
1485
1486 #[test]
1492 fn a_file_built_to_have_nothing_checked_says_nothing() {
1493 let bytes = write(
1494 &calling("puts"),
1495 &Data::default(),
1496 &[],
1497 &target(),
1498 Output::default(),
1499 &Info::default(),
1500 )
1501 .expect("an object");
1502 let file = object::File::parse(&bytes[..]).expect("a readable object");
1503 assert!(file.section_by_name(".note.gnu.property").is_none());
1504 }
1505
1506 #[test]
1515 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1516 let mut text = calling("puts");
1517 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1518 text.bytes.resize(17, 0x90);
1519 text.unwind.bytes = vec![0; 64];
1522 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1523 text.unwind.relocs.push(Reloc {
1524 at,
1525 symbol: name.to_owned(),
1526 kind: Reference::Address { bytes: 8 },
1527 addend: 0,
1528 after: 0,
1529 });
1530 }
1531 let bytes =
1532 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1533 .expect("an object");
1534 let file = object::File::parse(&bytes[..]).expect("a readable object");
1535 let mut found = points_at(&file);
1536 found.sort_unstable();
1537 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1538 }
1539
1540 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1543 let frames = file.section_by_name(".eh_frame").expect("the table");
1544 frames
1545 .relocations()
1546 .map(|(offset, reloc)| {
1547 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1548 panic!("a record points at something that is not a symbol");
1549 };
1550 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1551 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1552 let section = symbol.section_index().expect("a section symbol is in one");
1553 let name = file.section_by_index(section).expect("a readable section");
1554 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1555 })
1556 .collect()
1557 }
1558
1559 #[test]
1572 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1573 let mut text = two();
1574 text.unwind.bytes = vec![0; 64];
1575 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1576 text.unwind.relocs.push(Reloc {
1577 at,
1578 symbol: name.to_owned(),
1579 kind: Reference::Data,
1580 addend: 0,
1581 after: 0,
1582 });
1583 }
1584 let bytes =
1585 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1586 .expect("an object");
1587 let file = object::File::parse(&bytes[..]).expect("a readable object");
1588 let mut whole = points_at(&file);
1589 whole.sort_unstable();
1590 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1591
1592 let sections =
1593 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1594 let bytes = write(&text, &Data::default(), &[], &target(), sections, &Info::default())
1595 .expect("an object");
1596 let file = object::File::parse(&bytes[..]).expect("a readable object");
1597 let mut split = points_at(&file);
1598 split.sort_unstable();
1599 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1600 }
1601
1602 #[test]
1609 fn a_record_about_something_this_file_does_not_define_is_refused() {
1610 let mut text = calling("puts");
1611 text.unwind.bytes = vec![0; 64];
1612 text.unwind.relocs.push(Reloc {
1613 at: 32,
1614 symbol: "puts".to_owned(),
1615 kind: Reference::Data,
1616 addend: 0,
1617 after: 0,
1618 });
1619 let why =
1620 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1621 .expect_err("a record about a name from somewhere else");
1622 assert!(why.to_string().contains("puts"), "{why}");
1623 }
1624
1625 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1627 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1628 let index = symbol.section_index().expect("a section to be defined in");
1629 let section = file.section_by_index(index).expect("a readable section");
1630 section.name().expect("a named section").to_owned()
1631 }
1632
1633 fn two() -> Text {
1635 let mut text = calling("puts");
1636 text.bytes.resize(16, 0x90);
1639 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1640 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1641 text.relocs.push(Reloc {
1642 at: 17,
1643 symbol: "puts".to_owned(),
1644 kind: Reference::Call,
1645 addend: -4,
1646 after: 0,
1647 });
1648 text
1649 }
1650
1651 #[test]
1658 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1659 let sections =
1660 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1661 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1662 .expect("an object");
1663 let file = object::File::parse(&bytes[..]).expect("a readable object");
1664 assert_eq!(lives_in(&file, "f"), ".text.f");
1665 assert_eq!(lives_in(&file, "g"), ".text.g");
1666 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1667 for name in ["f", "g"] {
1670 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1671 assert_eq!(symbol.address(), 0, "{name}");
1672 assert_eq!(symbol.size(), 6, "{name}");
1673 }
1674 let section = file.section_by_name(".text.g").expect("the second function");
1675 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1676 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1679 }
1680
1681 #[test]
1687 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1688 let sections =
1689 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1690 let bytes = write(&two(), &Data::default(), &[], &target(), sections, &Info::default())
1691 .expect("an object");
1692 let file = object::File::parse(&bytes[..]).expect("a readable object");
1693 for name in [".text.f", ".text.g"] {
1694 let section = file.section_by_name(name).expect("a function");
1695 let (offset, _) = section.relocations().next().expect("the call in it");
1696 assert_eq!(offset, 1, "{name}");
1699 assert_eq!(section.relocations().count(), 1, "{name}");
1700 }
1701 }
1702
1703 fn switching() -> Text {
1705 let mut text = two();
1706 let name = ".Lg_j0".to_owned();
1707 text.tables.push(crate::Table { name, func: 1, cells: vec![0, 5] });
1708 text
1709 }
1710
1711 fn cells(file: &object::File<'_>, section: &str) -> Vec<(u64, String, i64)> {
1713 let section = file.section_by_name(section).expect("the table's section");
1714 section
1715 .relocations()
1716 .map(|(offset, reloc)| {
1717 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_PC32 });
1718 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1719 panic!("a cell against something that is not a symbol");
1720 };
1721 let symbol = file.symbol_by_index(index).expect("a symbol");
1722 assert_eq!(symbol.kind(), SymbolKind::Section);
1723 let at = symbol.section_index().expect("a section symbol is in one");
1724 let name = file.section_by_index(at).expect("a section").name().expect("a name");
1725 (offset, name.to_owned(), reloc.addend())
1726 })
1727 .collect()
1728 }
1729
1730 #[test]
1731 fn a_jump_table_is_read_only_data_whose_cells_the_linker_fills_in() {
1732 let mut text = switching();
1735 text.relocs[1].symbol = ".Lg_j0".to_owned();
1736 text.relocs[1].kind = Reference::Data;
1737 let bytes =
1738 write(&text, &Data::default(), &[], &target(), Output::default(), &Info::default())
1739 .expect("an object");
1740 let file = object::File::parse(&bytes[..]).expect("a readable object");
1741 let rodata = file.section_by_name(".rodata").expect("the table's section");
1742 assert_eq!(rodata.data().expect("the bytes"), &[0; 8]);
1743 assert_eq!(rodata.kind(), SectionKind::ReadOnlyData);
1744 assert!(file.symbols().all(|s| s.name() != Ok(".Lg_j0")), "a table leaves no name behind");
1745 let (at, reloc) = file
1746 .section_by_name(".text")
1747 .expect("the code")
1748 .relocations()
1749 .find(|(at, _)| *at == 17)
1750 .expect("the reference to the table");
1751 assert_eq!((at, reloc.addend()), (17, -4));
1752 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1753 panic!("a reference against something that is not a symbol");
1754 };
1755 let symbol = file.symbol_by_index(index).expect("a symbol");
1756 assert_eq!(symbol.section_index(), Some(rodata.index()));
1757 assert_eq!(symbol.kind(), SymbolKind::Section);
1758 assert_eq!(
1761 cells(&file, ".rodata"),
1762 [(0, ".text".to_owned(), 16), (4, ".text".to_owned(), 25)]
1763 );
1764 }
1765
1766 #[test]
1767 fn a_jump_table_under_data_sections_is_in_a_section_named_after_its_function() {
1768 let sections =
1769 Output { sections: Sections { functions: true, data: true }, ..Output::default() };
1770 let bytes =
1771 write(&switching(), &Data::default(), &[], &target(), sections, &Info::default())
1772 .expect("an object");
1773 let file = object::File::parse(&bytes[..]).expect("a readable object");
1774 assert_eq!(
1776 cells(&file, ".rodata.g"),
1777 [(0, ".text.g".to_owned(), 0), (4, ".text.g".to_owned(), 9)]
1778 );
1779 }
1780
1781 #[test]
1782 fn a_jump_table_outside_the_code_is_refused_on_windows() {
1783 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1784 let written = write(
1785 &switching(),
1786 &Data::default(),
1787 &[],
1788 &target,
1789 Output::default(),
1790 &Info::default(),
1791 );
1792 assert!(matches!(written, Err(Error::Refused { .. })), "{written:?}");
1793 }
1794
1795 fn variable(name: &str, place: Place) -> Object {
1797 Object {
1798 name: name.to_owned(),
1799 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1800 size: 4,
1801 align: 4,
1802 place,
1803 binding: Binding::Global,
1804 visibility: Visibility::Default,
1805 relocs: Vec::new(),
1806 }
1807 }
1808
1809 fn measured() -> (Text, Data) {
1811 let mut text = calling("puts");
1812 text.labels.push(Marker { name: ".L0".to_owned(), at: 1 });
1813 text.labels.push(Marker { name: ".L1".to_owned(), at: 5 });
1814 let mut table = variable("table", Place::ReadOnly);
1815 table.bytes = vec![0; 8];
1816 table.size = 8;
1817 let apart = |at, to: &str, from: &str| Apart {
1818 object: 0,
1819 at,
1820 to: to.to_owned(),
1821 from: from.to_owned(),
1822 addend: 0,
1823 bytes: 4,
1824 };
1825 let apart = vec![apart(0, ".L1", ".L0"), apart(4, ".L0", ".L1")];
1826 (text, Data { apart, weak: Vec::new(), objects: vec![table] })
1827 }
1828
1829 #[test]
1830 fn a_distance_between_two_labels_is_a_number_and_not_a_relocation() {
1831 let (text, data) = measured();
1832 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
1833 .expect("an object");
1834 let file = object::File::parse(&bytes[..]).expect("a readable object");
1835 let section = file.section_by_name(".rodata").expect("a read only section");
1836 assert_eq!(section.relocations().count(), 0);
1837 let image = section.data().expect("the image");
1838 assert_eq!(image[..8], [4, 0, 0, 0, 0xfc, 0xff, 0xff, 0xff]);
1839 }
1840
1841 #[test]
1842 fn a_distance_between_labels_in_two_sections_is_refused() {
1843 let (mut text, data) = measured();
1846 text.bytes.resize(22, 0x90);
1847 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1848 text.labels[1].at = 17;
1849 let output =
1850 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1851 let refused = write(&text, &data, &[], &target(), output, &Info::default());
1852 assert!(matches!(refused, Err(Error::Refused { .. })), "{refused:?}");
1853 }
1854
1855 fn holding(object: Object) -> Vec<u8> {
1857 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
1858 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1859 .expect("an object")
1860 }
1861
1862 #[test]
1863 fn what_a_variable_is_decides_which_section_it_goes_in() {
1864 for (place, wanted) in [
1865 (Place::Written, ".data"),
1866 (Place::ReadOnly, ".rodata"),
1867 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1868 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1869 (Place::Zero, ".bss"),
1870 (Place::Thread { zero: false }, ".tdata"),
1871 (Place::Thread { zero: true }, ".tbss"),
1872 (Place::Named(".init_array".to_owned()), ".init_array"),
1873 ] {
1874 let bytes = holding(variable("x", place.clone()));
1875 let file = object::File::parse(&bytes[..]).expect("a readable object");
1876 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1877 assert_eq!(section.size(), 4, "{place:?}");
1878 let carried = section.data().expect("the bytes").len();
1881 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1882 }
1883 }
1884
1885 #[test]
1891 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1892 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1893 let bytes = holding(variable("counter", place.clone()));
1894 let file = object::File::parse(&bytes[..]).expect("a readable object");
1895 let symbol = file
1896 .symbols()
1897 .find(|symbol| symbol.name() == Ok("counter"))
1898 .unwrap_or_else(|| panic!("{place:?}"));
1899 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1900 }
1901 }
1902
1903 #[test]
1909 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1910 for (name, wanted) in [
1911 (".init_array", elf::SHT_INIT_ARRAY),
1912 (".init_array.00101", elf::SHT_INIT_ARRAY),
1913 (".fini_array", elf::SHT_FINI_ARRAY),
1914 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1915 (".init_arrays", elf::SHT_PROGBITS),
1916 ] {
1917 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1918 let file = object::File::parse(&bytes[..]).expect("a readable object");
1919 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1920 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1921 panic!("{name} is not an elf section");
1922 };
1923 assert_eq!(sh_type, wanted, "{name}");
1924 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1925 }
1926 }
1927
1928 #[test]
1934 fn two_variables_in_one_named_section_share_it() {
1935 let objects = vec![
1936 variable("x", Place::Named(".init_array".to_owned())),
1937 variable("y", Place::Named(".init_array".to_owned())),
1938 ];
1939 let data = Data { apart: Vec::new(), weak: Vec::new(), objects };
1940 let bytes =
1941 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
1942 .expect("an object");
1943 let file = object::File::parse(&bytes[..]).expect("a readable object");
1944 let named: Vec<_> =
1945 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1946 assert_eq!(named.len(), 1);
1947 assert_eq!(named[0].size(), 8);
1948 }
1949
1950 #[test]
1954 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1955 let sections =
1956 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1957 for (place, wanted) in [
1958 (Place::Written, ".data.x"),
1959 (Place::ReadOnly, ".rodata.x"),
1960 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1961 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1962 (Place::Zero, ".bss.x"),
1963 (Place::Thread { zero: false }, ".tdata.x"),
1964 (Place::Thread { zero: true }, ".tbss.x"),
1965 ] {
1966 let data = Data {
1967 apart: Vec::new(),
1968 weak: Vec::new(),
1969 objects: vec![variable("x", place.clone())],
1970 };
1971 let bytes = write(&Text::default(), &data, &[], &target(), sections, &Info::default())
1972 .expect("object");
1973 let file = object::File::parse(&bytes[..]).expect("a readable object");
1974 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1975 let section = file.section_by_name(wanted).expect("the section it named");
1976 assert_eq!(section.size(), 4, "{place:?}");
1977 let carried = section.data().expect("the bytes").len();
1980 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1981 }
1982 }
1983
1984 #[test]
1988 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1989 let sections =
1990 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1991 let named = Place::Named(".init_array".to_owned());
1992 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1993 let bytes = write(
1994 &Text::default(),
1995 &Data { apart: Vec::new(), weak: Vec::new(), objects },
1996 &[],
1997 &target(),
1998 sections,
1999 &Info::default(),
2000 )
2001 .expect("object");
2002 let file = object::File::parse(&bytes[..]).expect("a readable object");
2003 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
2004 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
2005 assert_eq!(lives_in(&file, "n"), ".init_array");
2006 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
2007 }
2008
2009 #[test]
2013 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
2014 let sections =
2015 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
2016 let pointer = Object {
2017 bytes: vec![0; 8],
2018 size: 8,
2019 align: 8,
2020 relocs: vec![Reloc {
2021 at: 0,
2022 symbol: "y".to_owned(),
2023 kind: Reference::Address { bytes: 8 },
2024 addend: 0,
2025 after: 0,
2026 }],
2027 ..variable("p", Place::Written)
2028 };
2029 let objects = vec![variable("first", Place::Written), pointer];
2030 let bytes = write(
2031 &Text::default(),
2032 &Data { apart: Vec::new(), weak: Vec::new(), objects },
2033 &[],
2034 &target(),
2035 sections,
2036 &Info::default(),
2037 )
2038 .expect("object");
2039 let file = object::File::parse(&bytes[..]).expect("a readable object");
2040 let section = file.section_by_name(".data.p").expect("the pointer's own section");
2041 let (offset, reloc) = section.relocations().next().expect("one relocation");
2042 assert_eq!(offset, 0);
2045 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2046 }
2047
2048 #[test]
2056 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
2057 let place = Place::RelocReadOnly { local: true };
2058 let data = Data {
2059 apart: Vec::new(),
2060 weak: Vec::new(),
2061 objects: vec![variable("first", place.clone()), variable("second", place)],
2062 };
2063 let bytes =
2064 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2065 .expect("an object");
2066 let file = object::File::parse(&bytes[..]).expect("a readable object");
2067 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
2068 assert_eq!(named, 1, "one section holding both, not one each");
2069 }
2070
2071 #[test]
2072 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
2073 let mut data = Data {
2074 apart: Vec::new(),
2075 weak: Vec::new(),
2076 objects: vec![variable("first", Place::Written)],
2077 };
2078 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
2079 let bytes =
2080 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2081 .expect("an object");
2082 let file = object::File::parse(&bytes[..]).expect("a readable object");
2083 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
2084 assert_eq!(second.kind(), SymbolKind::Data);
2085 assert_eq!(second.size(), 4);
2086 assert_eq!(second.address(), 16);
2090 }
2091
2092 #[test]
2093 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
2094 for (binding, global, weak) in [
2095 (Binding::Global, true, false),
2096 (Binding::Local, false, false),
2097 (Binding::Weak, true, true),
2098 ] {
2099 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
2100 let file = object::File::parse(&bytes[..]).expect("a readable object");
2101 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2102 assert_eq!(x.is_global(), global, "{binding:?}");
2103 assert_eq!(x.is_weak(), weak, "{binding:?}");
2104 }
2105 }
2106
2107 #[test]
2108 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
2109 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
2110 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
2111 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
2112 assert!(x.is_common(), "the linker merges every definition of this name into one");
2113 assert_eq!(x.size(), 4);
2114 assert_eq!(x.address(), 0);
2118 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
2119 }
2120
2121 #[test]
2122 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
2123 let object = Object {
2124 bytes: vec![0; 8],
2125 size: 8,
2126 align: 8,
2127 relocs: vec![Reloc {
2128 at: 0,
2129 symbol: "y".to_owned(),
2130 kind: Reference::Address { bytes: 8 },
2131 addend: 16,
2132 after: 0,
2133 }],
2134 ..variable("p", Place::Written)
2135 };
2136 let bytes = holding(object);
2137 let file = object::File::parse(&bytes[..]).expect("a readable object");
2138 let section = file.section_by_name(".data").expect("a data section");
2139 let (offset, reloc) = section.relocations().next().expect("one relocation");
2140 assert_eq!(offset, 0);
2141 assert_eq!(reloc.addend(), 16);
2142 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
2143 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
2144 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
2145 }
2146
2147 #[test]
2154 fn a_weak_undefined_name_is_one_the_link_may_leave_unfound() {
2155 let mut text = Text::default();
2156 text.funcs.push(extent("caller".to_owned(), 0, 8, Binding::Global));
2157 text.bytes.resize(8, 0x90);
2158 text.relocs.push(Reloc {
2159 at: 1,
2160 symbol: "hook".to_owned(),
2161 kind: Reference::Call,
2162 addend: -4,
2163 after: 0,
2164 });
2165 let data = Data {
2166 apart: Vec::new(),
2167 weak: vec!["hook".to_owned(), "never_called".to_owned()],
2168 objects: vec![],
2169 };
2170 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2171 .expect("an object");
2172 let file = object::File::parse(&bytes[..]).expect("a readable object");
2173
2174 let hook = file.symbols().find(|s| s.name() == Ok("hook")).expect("the one called");
2175 assert!(hook.is_undefined(), "nothing here defines it");
2176 assert!(hook.is_weak(), "so the link may leave it alone rather than fail");
2177
2178 let quiet = file.symbols().find(|s| s.name() == Ok("never_called")).expect("the other");
2182 assert!(quiet.is_undefined() && quiet.is_weak(), "{:?}", quiet.flags());
2183 }
2184
2185 #[test]
2200 fn a_thread_local_name_this_file_only_reads_is_still_written_down_as_thread_local() {
2201 let mut text = Text::default();
2202 text.funcs.push(extent("reader".to_owned(), 0, 16, Binding::Global));
2203 text.bytes.resize(16, 0x90);
2204 text.relocs.push(Reloc {
2205 at: 3,
2206 symbol: "flags".to_owned(),
2207 kind: Reference::Thread,
2208 addend: -4,
2209 after: 0,
2210 });
2211 text.relocs.push(Reloc {
2214 at: 10,
2215 symbol: "shared".to_owned(),
2216 kind: Reference::Got,
2217 addend: -4,
2218 after: 0,
2219 });
2220 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![] };
2221 let bytes = write(&text, &data, &[], &target(), Output::default(), &Info::default())
2222 .expect("an object");
2223 let file = object::File::parse(&bytes[..]).expect("a readable object");
2224
2225 let flags = file.symbols().find(|s| s.name() == Ok("flags")).expect("the thread-local one");
2226 assert!(flags.is_undefined(), "nothing here defines it");
2227 assert_eq!(flags.kind(), SymbolKind::Tls, "which is what the linker refuses to guess");
2228
2229 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the ordinary one");
2230 assert!(shared.is_undefined(), "nothing here defines this one either");
2231 assert_eq!(shared.kind(), SymbolKind::Unknown, "and there is nothing to say about it");
2232 }
2233
2234 #[test]
2236 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
2237 let mut data = Data {
2238 apart: Vec::new(),
2239 weak: Vec::new(),
2240 objects: vec![variable("first", Place::Written)],
2241 };
2242 data.objects.push(Object {
2243 bytes: vec![0; 16],
2244 size: 16,
2245 align: 8,
2246 relocs: vec![Reloc {
2247 at: 8,
2248 symbol: "y".to_owned(),
2249 kind: Reference::Address { bytes: 8 },
2250 addend: 0,
2251 after: 0,
2252 }],
2253 ..variable("second", Place::Written)
2254 });
2255 let bytes =
2256 write(&Text::default(), &data, &[], &target(), Output::default(), &Info::default())
2257 .expect("an object");
2258 let file = object::File::parse(&bytes[..]).expect("a readable object");
2259 let section = file.section_by_name(".data").expect("a data section");
2260 let (offset, _) = section.relocations().next().expect("one relocation");
2261 assert_eq!(offset, 16);
2264 }
2265
2266 #[test]
2267 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
2268 let data = Data {
2269 apart: Vec::new(),
2270 weak: Vec::new(),
2271 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
2272 };
2273 let aliases = [Alias {
2274 name: "b".to_owned(),
2275 target: "a".to_owned(),
2276 binding: Binding::Global,
2277 visibility: Visibility::Default,
2278 }];
2279 let bytes = write(
2280 &Text::default(),
2281 &data,
2282 &aliases,
2283 &target(),
2284 Output::default(),
2285 &Info::default(),
2286 )
2287 .expect("an object");
2288 let file = object::File::parse(&bytes[..]).expect("a readable object");
2289 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
2290 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
2291 assert_eq!(b.address(), a.address(), "the same place");
2292 assert_eq!(b.size(), a.size());
2293 assert_eq!(b.section_index(), a.section_index());
2294 assert!(a.is_local(), "the target was written `static`");
2297 assert!(b.is_global(), "and the name given to it was not");
2298 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
2300 }
2301
2302 #[test]
2303 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
2304 let text = calling("puts");
2305 let aliases = [Alias {
2306 name: "g".to_owned(),
2307 target: "f".to_owned(),
2308 binding: Binding::Weak,
2309 visibility: Visibility::Default,
2310 }];
2311 let bytes = write(
2312 &text,
2313 &Data::default(),
2314 &aliases,
2315 &target(),
2316 Output::default(),
2317 &Info::default(),
2318 )
2319 .expect("an object");
2320 let file = object::File::parse(&bytes[..]).expect("a readable object");
2321 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
2322 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
2323 assert_eq!(g.address(), f.address());
2324 assert_eq!(g.size(), f.size());
2325 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
2326 assert!(g.is_weak(), "so that a program may define the name itself instead");
2327 }
2328
2329 #[test]
2332 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
2333 let aliases = [Alias {
2334 name: "b".to_owned(),
2335 target: "a".to_owned(),
2336 binding: Binding::Global,
2337 visibility: Visibility::Default,
2338 }];
2339 let error = write(
2340 &Text::default(),
2341 &Data::default(),
2342 &aliases,
2343 &target(),
2344 Output::default(),
2345 &Info::default(),
2346 )
2347 .expect_err("nothing to point at");
2348 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
2349 }
2350
2351 #[test]
2352 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
2353 let text = calling("puts");
2354 for triple in [
2355 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2356 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2357 ] {
2358 let error = write(
2359 &text,
2360 &Data::default(),
2361 &[],
2362 &TargetInfo::new(triple),
2363 Output::default(),
2364 &Info::default(),
2365 )
2366 .expect_err("no writer");
2367 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2368 }
2369 }
2370
2371 #[test]
2377 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
2378 let mut text = calling("puts");
2379 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
2380 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
2381 text.bytes.resize(33, 0x90);
2382 let data = Data {
2383 apart: Vec::new(),
2384 weak: Vec::new(),
2385 objects: vec![variable("seen", Place::Written), {
2386 let mut quiet = variable("quiet", Place::Zero);
2387 quiet.binding = Binding::Local;
2388 quiet
2389 }],
2390 };
2391 let aliases = [Alias {
2392 name: "second".to_owned(),
2393 target: "f".to_owned(),
2394 binding: Binding::Global,
2395 visibility: Visibility::Default,
2396 }];
2397
2398 let names = defines(&text, &data, &aliases, &target()).expect("a list");
2399 assert_eq!(names, ["f", "shared", "seen", "second"]);
2400
2401 let bytes = write(&text, &data, &aliases, &target(), Output::default(), &Info::default())
2402 .expect("an object");
2403 let file = object::File::parse(&bytes[..]).expect("a readable object");
2404 let found: Vec<String> = file
2405 .symbols()
2406 .filter(|symbol| symbol.is_global() && symbol.is_definition())
2407 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
2408 .collect();
2409 let mut sorted = names.clone();
2410 sorted.sort();
2411 let mut theirs = found;
2412 theirs.sort();
2413 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
2414 }
2415
2416 fn windows() -> TargetInfo {
2418 TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu))
2419 }
2420
2421 fn inline(bytes: &[u8], section: &str, at: usize) -> i32 {
2423 let file = object::File::parse(bytes).expect("a readable object");
2424 let found = file.section_by_name(section).expect("the section").data().expect("the bytes");
2425 i32::from_le_bytes(found[at..at + 4].try_into().expect("four bytes"))
2426 }
2427
2428 #[test]
2429 fn a_windows_target_is_written_rather_than_refused() {
2430 let text = calling("puts");
2431 let bytes =
2432 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2433 .expect("an object");
2434 let file = object::File::parse(&bytes[..]).expect("a readable object");
2435 assert_eq!(file.format(), BinaryFormat::Coff);
2436 let section = file.section_by_name(".text").expect("a text section");
2437 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
2438 let names: Vec<&str> = file.symbols().filter_map(|symbol| symbol.name().ok()).collect();
2439 assert!(names.contains(&"f"), "{names:?}");
2440 assert!(names.contains(&"puts"), "{names:?}");
2441 }
2442
2443 #[test]
2451 fn how_far_the_instruction_runs_past_the_hole_is_in_the_relocation_type() {
2452 for (after, typ) in [
2453 (0, pe::IMAGE_REL_AMD64_REL32),
2454 (1, pe::IMAGE_REL_AMD64_REL32_1),
2455 (4, pe::IMAGE_REL_AMD64_REL32_4),
2456 (5, pe::IMAGE_REL_AMD64_REL32_5),
2457 ] {
2458 let mut text = calling("puts");
2459 text.relocs[0].addend = -4 - i64::from(after);
2462 text.relocs[0].after = after;
2463 text.bytes.resize(6 + after as usize, 0x90);
2464 text.funcs[0].len = text.bytes.len();
2465 let bytes = write(
2466 &text,
2467 &Data::default(),
2468 &[],
2469 &windows(),
2470 Output::default(),
2471 &Info::default(),
2472 )
2473 .expect("an object");
2474 let file = object::File::parse(&bytes[..]).expect("a readable object");
2475 let section = file.section_by_name(".text").expect("a text section");
2476 let (_, reloc) = section.relocations().next().expect("the relocation");
2477 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ }, "{after}");
2478 assert_eq!(inline(&bytes, ".text", 1), 0, "{after}");
2481 }
2482 }
2483
2484 #[test]
2487 fn a_distance_the_instruction_did_not_ask_for_stays_in_the_bytes() {
2488 let mut text = calling("puts");
2489 text.relocs[0].addend = 12;
2490 let bytes =
2491 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2492 .expect("an object");
2493 assert_eq!(inline(&bytes, ".text", 1), 16, "twelve past the end, which is four past here");
2494 }
2495
2496 #[test]
2497 fn an_address_written_into_an_image_is_the_wide_relocation_here_too() {
2498 let object = Object {
2499 bytes: vec![0; 8],
2500 size: 8,
2501 align: 8,
2502 relocs: vec![Reloc {
2503 at: 0,
2504 symbol: "y".to_owned(),
2505 kind: Reference::Address { bytes: 8 },
2506 addend: 0,
2507 after: 0,
2508 }],
2509 ..variable("p", Place::Written)
2510 };
2511 let data = Data { apart: Vec::new(), weak: Vec::new(), objects: vec![object] };
2512 let bytes =
2513 write(&Text::default(), &data, &[], &windows(), Output::default(), &Info::default())
2514 .expect("an object");
2515 let file = object::File::parse(&bytes[..]).expect("a readable object");
2516 let section = file.section_by_name(".data").expect("a data section");
2517 let (_, reloc) = section.relocations().next().expect("the relocation");
2518 let typ = pe::IMAGE_REL_AMD64_ADDR64;
2519 assert_eq!(reloc.flags(), RelocationFlags::Coff { typ });
2520 }
2521
2522 #[test]
2525 fn a_variable_the_loader_writes_into_is_read_only_data_here() {
2526 for local in [false, true] {
2527 let data = Data {
2528 apart: Vec::new(),
2529 weak: Vec::new(),
2530 objects: vec![variable("p", Place::RelocReadOnly { local })],
2531 };
2532 let bytes = write(
2533 &Text::default(),
2534 &data,
2535 &[],
2536 &windows(),
2537 Output::default(),
2538 &Info::default(),
2539 )
2540 .expect("an object");
2541 let file = object::File::parse(&bytes[..]).expect("a readable object");
2542 assert!(file.section_by_name(".rdata").is_some(), "{local}");
2543 assert!(file.section_by_name(".data.rel.ro.local").is_none(), "{local}");
2544 }
2545 }
2546
2547 #[test]
2550 fn the_sections_only_elf_reads_are_left_out_rather_than_written_empty() {
2551 let text = calling("puts");
2552 let output = Output { property: Property { features: 3 }, ..Output::default() };
2553 let bytes = write(&text, &Data::default(), &[], &windows(), output, &Info::default())
2554 .expect("an object");
2555 let file = object::File::parse(&bytes[..]).expect("a readable object");
2556 assert!(file.section_by_name(".note.GNU-stack").is_none());
2557 assert!(file.section_by_name(".note.gnu.property").is_none());
2558 }
2559
2560 #[test]
2565 fn what_this_format_cannot_say_is_refused_by_name() {
2566 let ordinary = Text::default();
2567 let empty = Data::default();
2568
2569 let mut thread = Data::default();
2570 thread.objects.push(variable("t", Place::Thread { zero: false }));
2571
2572 let mut gathered = Data::default();
2573 gathered.objects.push(variable("c", Place::Named(".init_array".to_owned())));
2574
2575 let mut table = calling("puts");
2576 table.relocs[0].kind = Reference::Got;
2577
2578 let mut room = calling("puts");
2579 room.funcs[0].patch = Some(Patch { at: 0, before: 0 });
2580
2581 let cases: [(&str, &Text, &Data); 4] = [
2582 ("thread-local", &ordinary, &thread),
2583 ("startup", &ordinary, &gathered),
2584 ("table", &table, &empty),
2585 ("patcher", &room, &empty),
2586 ];
2587 for (what, text, data) in cases {
2588 let error = write(text, data, &[], &windows(), Output::default(), &Info::default())
2589 .expect_err("something this format cannot write");
2590 assert!(matches!(error, Error::Refused { .. }), "{what}: {error:?}");
2591 }
2592 }
2593
2594 #[test]
2598 fn a_visibility_this_format_cannot_keep_changes_nothing_rather_than_failing() {
2599 let mut text = calling("puts");
2600 text.funcs[0].visibility = Visibility::Hidden;
2601 let bytes =
2602 write(&text, &Data::default(), &[], &windows(), Output::default(), &Info::default())
2603 .expect("an object");
2604 let file = object::File::parse(&bytes[..]).expect("a readable object");
2605 let symbol = file.symbols().find(|symbol| symbol.name() == Ok("f")).expect("the function");
2606 assert!(symbol.is_global(), "a name others may use either way");
2607 }
2608
2609 #[test]
2610 fn the_names_a_linker_can_find_are_the_same_list_on_either_format() {
2611 let text = calling("puts");
2612 let data = Data {
2613 apart: Vec::new(),
2614 weak: Vec::new(),
2615 objects: vec![variable("shared", Place::Written)],
2616 };
2617 let theirs = defines(&text, &data, &[], &windows()).expect("a list");
2618 assert_eq!(theirs, defines(&text, &data, &[], &target()).expect("a list"));
2619 }
2620
2621 #[test]
2625 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
2626 let text = calling("puts");
2627 for triple in [
2628 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
2629 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
2630 ] {
2631 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
2632 .expect_err("no writer");
2633 assert!(matches!(error, Error::Format { .. }), "{error:?}");
2634 }
2635 }
2636}