1use std::collections::HashMap;
29
30use object::write::{
31 Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
32};
33use object::{
34 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionFlags, SectionKind,
35 SymbolFlags, SymbolKind, SymbolScope, elf,
36};
37use rucc_target::{ObjectFormat, TargetInfo};
38use rucc_tuple::Arch;
39
40use crate::section::{
41 Alias, Array, Binding, Data, Object, Output, Place, Property, Reference, Reloc, Sections, Text,
42 Visibility,
43};
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum Error {
48 Format {
50 triple: String,
52 },
53 Refused {
55 why: String,
57 },
58}
59
60impl std::fmt::Display for Error {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 Error::Format { triple } => {
64 write!(f, "there is no object writer for {triple} in this compiler yet")
65 }
66 Error::Refused { why } => {
67 write!(f, "the object writer refused what it was given: {why}")
68 }
69 }
70 }
71}
72
73impl std::error::Error for Error {}
74
75pub fn write(
84 text: &Text,
85 data: &Data,
86 aliases: &[Alias],
87 target: &TargetInfo,
88 output: Output,
89) -> Result<Vec<u8>, Error> {
90 let Output { sections, property } = output;
91 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
92 return Err(Error::Format { triple: target.tuple.to_string() });
93 }
94 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
95 let whole = obj.section_id(StandardSection::Text);
99 if !sections.functions {
100 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
101 }
102
103 let mut symbols = std::collections::BTreeMap::new();
107 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
112 let mut ordered: Vec<String> = Vec::new();
115 for func in &text.funcs {
116 let ahead = func.patch.map_or(0, |patch| patch.before);
126 let (section, at) = if sections.functions {
127 let name = format!(".text.{}", func.name).into_bytes();
128 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
129 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
130 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
131 (id, ahead as u64)
132 } else {
133 (whole, func.start as u64)
134 };
135 if let Some(patch) = func.patch {
150 let base = if sections.functions { func.start - ahead } else { 0 };
151 let name = PATCHABLE.as_bytes().to_vec();
152 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
153 obj.section_mut(id).flags = SectionFlags::Elf {
154 sh_type: elf::SHT_PROGBITS,
155 sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER,
156 };
157 obj.append_section_data(id, &[0; 8], 8);
158 let symbol = obj.section_symbol(section);
159 obj.add_relocation(
160 id,
161 Relocation {
162 offset: 0,
163 symbol,
164 addend: (patch.at - base) as i64,
165 flags: RelocationFlags::Elf { r_type: elf::R_X86_64_64 },
166 },
167 )
168 .map_err(|why| Error::Refused { why: why.to_string() })?;
169 ordered.push(if sections.functions {
170 format!(".text.{}", func.name)
171 } else {
172 ".text".to_owned()
173 });
174 }
175 let id = obj.add_symbol(Symbol {
176 name: func.name.clone().into_bytes(),
177 value: at,
178 size: func.len as u64,
179 kind: SymbolKind::Text,
180 scope: scope_of(func.binding),
181 weak: func.binding == Binding::Weak,
182 section: SymbolSection::Section(section),
183 flags: SymbolFlags::None,
184 });
185 see(&mut obj, id, func.binding, func.visibility);
186 symbols.insert(func.name.clone(), id);
187 split.push((section, at));
188 }
189
190 let mut placed = Vec::with_capacity(data.objects.len());
195 let mut named = HashMap::new();
199 for object in &data.objects {
200 let (section, offset) = put(&mut obj, object, &mut named, sections);
201 let id = obj.add_symbol(Symbol {
202 name: object.name.clone().into_bytes(),
203 value: if object.place == Place::Merged { object.align } else { offset },
206 size: object.size,
207 kind: match object.place {
212 Place::Thread { .. } => SymbolKind::Tls,
213 _ => SymbolKind::Data,
214 },
215 scope: scope_of(object.binding),
216 weak: object.binding == Binding::Weak,
217 section,
218 flags: SymbolFlags::None,
219 });
220 see(&mut obj, id, object.binding, object.visibility);
221 symbols.insert(object.name.clone(), id);
222 placed.push((section.id(), offset));
223 }
224
225 for alias in aliases {
231 let Some(&id) = symbols.get(&alias.target) else {
232 let why =
233 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
234 return Err(Error::Refused { why });
235 };
236 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
237 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
238 let id = obj.add_symbol(Symbol {
239 name: alias.name.clone().into_bytes(),
240 value,
241 size,
242 kind,
243 scope: scope_of(alias.binding),
244 weak: alias.binding == Binding::Weak,
245 section,
246 flags: SymbolFlags::None,
247 });
248 see(&mut obj, id, alias.binding, alias.visibility);
249 symbols.insert(alias.name.clone(), id);
250 }
251
252 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
256 for reloc in wanted {
257 if symbols.contains_key(&reloc.symbol) {
258 continue;
259 }
260 let id = obj.add_symbol(Symbol {
261 name: reloc.symbol.clone().into_bytes(),
262 value: 0,
263 size: 0,
264 kind: SymbolKind::Unknown,
268 scope: SymbolScope::Dynamic,
269 weak: false,
270 section: SymbolSection::Undefined,
271 flags: SymbolFlags::None,
272 });
273 symbols.insert(reloc.symbol.clone(), id);
274 }
275
276 for reloc in &text.relocs {
277 let (section, at) = if sections.functions {
282 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
283 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
284 let why = format!("a relocation at {} is in front of every function", reloc.at);
285 return Err(Error::Refused { why });
286 };
287 let base = func.start - func.patch.map_or(0, |patch| patch.before);
290 (split[after - 1].0, (reloc.at - base) as u64)
291 } else {
292 (whole, reloc.at as u64)
293 };
294 add(&mut obj, section, at, reloc, &symbols)?;
295 }
296
297 if !text.unwind.bytes.is_empty() {
303 let frames = obj.add_section(Vec::new(), b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
304 obj.append_section_data(frames, &text.unwind.bytes, 8);
305 for reloc in &text.unwind.relocs {
306 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
319 let Some((section, at)) = found.map(|i| split[i]) else {
320 let why =
321 format!("'{}' has an unwind record and is not a function here", reloc.symbol);
322 return Err(Error::Refused { why });
323 };
324 let symbol = obj.section_symbol(section);
325 let r_type = r_type(reloc.kind).ok_or_else(|| Error::Refused {
326 why: format!("no relocation is {:?}", reloc.kind),
327 })?;
328 let record = Relocation {
329 offset: reloc.at as u64,
330 symbol,
331 addend: reloc.addend + at as i64,
335 flags: RelocationFlags::Elf { r_type },
336 };
337 obj.add_relocation(frames, record)
338 .map_err(|why| Error::Refused { why: why.to_string() })?;
339 }
340 }
341 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
342 let Some(section) = section else { continue };
343 for reloc in &object.relocs {
344 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols)?;
345 }
346 }
347
348 if property.any() {
352 let note = obj.section_id(StandardSection::GnuProperty);
353 obj.append_section_data(note, &record(property), 8);
354 }
355
356 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
359
360 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
361 link(&mut bytes, &ordered);
362 Ok(bytes)
363}
364
365pub fn defines(
389 text: &Text,
390 data: &Data,
391 aliases: &[Alias],
392 target: &TargetInfo,
393) -> Result<Vec<String>, Error> {
394 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
395 return Err(Error::Format { triple: target.tuple.to_string() });
396 }
397 let names = text
398 .funcs
399 .iter()
400 .filter(|func| func.binding != Binding::Local)
401 .map(|func| func.name.clone())
402 .chain(
403 data.objects
404 .iter()
405 .filter(|object| object.binding != Binding::Local)
406 .map(|object| object.name.clone()),
407 )
408 .chain(
409 aliases
410 .iter()
411 .filter(|alias| alias.binding != Binding::Local)
412 .map(|alias| alias.name.clone()),
413 )
414 .collect();
415 Ok(names)
416}
417
418const PATCHABLE: &str = "__patchable_function_entries";
420
421fn link(bytes: &mut [u8], ordered: &[String]) {
436 if ordered.is_empty() {
437 return;
438 }
439 let word = |bytes: &[u8], at: usize| u64::from_le_bytes(bytes[at..at + 8].try_into().unwrap());
440 let short = |bytes: &[u8], at: usize| u16::from_le_bytes(bytes[at..at + 2].try_into().unwrap());
441 let long = |bytes: &[u8], at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap());
442 let headers = word(bytes, 0x28) as usize;
447 let step = short(bytes, 0x3a) as usize;
448 let count = short(bytes, 0x3c) as usize;
449 let strings = word(bytes, headers + short(bytes, 0x3e) as usize * step + 24) as usize;
450 let name = |bytes: &[u8], header: usize| {
451 let at = strings + long(bytes, header) as usize;
452 let end = bytes[at..].iter().position(|byte| *byte == 0).map_or(at, |len| at + len);
453 String::from_utf8_lossy(&bytes[at..end]).into_owned()
454 };
455 let names: Vec<String> = (0..count).map(|i| name(bytes, headers + i * step)).collect();
456 let mut wanted = ordered.iter();
457 for (i, section) in names.iter().enumerate() {
458 if section != PATCHABLE {
459 continue;
460 }
461 let Some(target) = wanted.next() else { break };
462 let Some(at) = names.iter().position(|name| name == target) else { continue };
463 let at = u32::try_from(at).expect("a file with this many sections in it");
464 let sh_link = headers + i * step + 40;
465 bytes[sh_link..sh_link + 4].copy_from_slice(&at.to_le_bytes());
466 }
467 debug_assert!(wanted.next().is_none(), "a record whose header nothing found");
468}
469
470fn record(property: Property) -> Vec<u8> {
481 let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
484 let desc = [Property::X86_FEATURES, 4, property.features, 0];
485 let mut out = Vec::with_capacity(32);
486 for word in head {
487 out.extend_from_slice(&word.to_le_bytes());
488 }
489 out.extend_from_slice(b"GNU\0");
492 for word in desc {
493 out.extend_from_slice(&word.to_le_bytes());
494 }
495 out
496}
497
498fn put(
505 obj: &mut Writer<'_>,
506 object: &Object,
507 named: &mut HashMap<String, object::write::SectionId>,
508 sections: Sections,
509) -> (SymbolSection, u64) {
510 if sections.data {
516 if let Some(name) = object.place.split(&object.name) {
517 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
518 let offset = if carries_no_bytes(&object.place) {
519 obj.append_section_bss(section, object.size, object.align)
520 } else {
521 obj.append_section_data(section, &object.bytes, object.align)
522 };
523 return (SymbolSection::Section(section), offset);
524 }
525 }
526 let section = match &object.place {
527 Place::Written => obj.section_id(StandardSection::Data),
528 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
529 Place::RelocReadOnly { local: false } => {
535 obj.section_id(StandardSection::ReadOnlyDataWithRel)
536 }
537 Place::RelocReadOnly { local: true } => {
538 made(obj, named, ".data.rel.ro.local", SectionKind::ReadOnlyDataWithRel)
539 }
540 Place::Zero => obj.section_id(StandardSection::UninitializedData),
541 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
542 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
543 Place::Merged => return (SymbolSection::Common, 0),
544 Place::Named(name) => {
550 let section = made(obj, named, name, SectionKind::Data);
551 if let Some(array) = Array::of(name) {
552 obj.section_mut(section).flags = SectionFlags::Elf {
553 sh_type: match array {
554 Array::Init => elf::SHT_INIT_ARRAY,
555 Array::Fini => elf::SHT_FINI_ARRAY,
556 Array::Preinit => elf::SHT_PREINIT_ARRAY,
557 },
558 sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE,
559 };
560 }
561 section
562 }
563 };
564 let offset = if carries_no_bytes(&object.place) {
565 obj.append_section_bss(section, object.size, object.align)
566 } else {
567 obj.append_section_data(section, &object.bytes, object.align)
568 };
569 (SymbolSection::Section(section), offset)
570}
571
572fn carries_no_bytes(place: &Place) -> bool {
578 matches!(place, Place::Zero | Place::Thread { zero: true })
579}
580
581fn made(
589 obj: &mut Writer<'_>,
590 named: &mut HashMap<String, object::write::SectionId>,
591 name: &str,
592 kind: SectionKind,
593) -> object::write::SectionId {
594 if let Some(section) = named.get(name) {
595 return *section;
596 }
597 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
598 named.insert(name.to_owned(), section);
599 section
600}
601
602fn kind_of(place: &Place) -> SectionKind {
611 match place {
612 Place::ReadOnly => SectionKind::ReadOnlyData,
613 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
614 Place::Zero => SectionKind::UninitializedData,
615 Place::Thread { zero: false } => SectionKind::Tls,
616 Place::Thread { zero: true } => SectionKind::UninitializedTls,
617 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
618 }
619}
620
621fn add(
628 obj: &mut Writer<'_>,
629 section: object::write::SectionId,
630 at: u64,
631 reloc: &Reloc,
632 symbols: &std::collections::BTreeMap<String, SymbolId>,
633) -> Result<(), Error> {
634 let r_type = r_type(reloc.kind)
635 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
636 obj.add_relocation(
637 section,
638 Relocation {
639 offset: at,
640 symbol: symbols[&reloc.symbol],
641 addend: reloc.addend,
642 flags: RelocationFlags::Elf { r_type },
643 },
644 )
645 .map_err(|why| Error::Refused { why: why.to_string() })
646}
647
648fn scope_of(binding: Binding) -> SymbolScope {
661 match binding {
662 Binding::Local => SymbolScope::Compilation,
663 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
664 }
665}
666
667fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
679 if binding == Binding::Local {
680 return;
681 }
682 let wanted = match visibility {
683 Visibility::Default => elf::STV_DEFAULT,
684 Visibility::Hidden => elf::STV_HIDDEN,
685 Visibility::Protected => elf::STV_PROTECTED,
686 };
687 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
688 *st_other = st_other.with_visibility(wanted);
689 }
690}
691
692fn r_type(reference: Reference) -> Option<elf::RelocationType> {
704 Some(match reference {
705 Reference::Call => elf::R_X86_64_PLT32,
706 Reference::Data => elf::R_X86_64_PC32,
707 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
708 Reference::Thread => elf::R_X86_64_GOTTPOFF,
709 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
710 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
711 Reference::Address { .. } => return None,
712 })
713}
714
715#[cfg(test)]
716mod tests {
717 use super::*;
718
719 use object::read::elf::Sym as _;
720 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
721 use rucc_target::{Arch, Env, Os, Triple};
722
723 use crate::section::{Extent, Patch, Reloc};
724
725 fn target() -> TargetInfo {
727 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
728 }
729
730 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
735 Extent {
736 name,
737 start,
738 len,
739 align: crate::FUNC_ALIGN,
740 binding,
741 visibility: Visibility::Default,
742 patch: None,
743 }
744 }
745
746 fn calling(name: &str) -> Text {
748 Text {
749 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
750 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
751 relocs: vec![Reloc {
752 at: 1,
753 symbol: name.to_owned(),
754 kind: Reference::Call,
755 addend: -4,
756 }],
757 ..Text::default()
758 }
759 }
760
761 #[test]
762 fn the_bytes_come_back_out_of_the_section_they_went_into() {
763 let text = calling("puts");
764 let bytes =
765 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
766 let file = object::File::parse(&bytes[..]).expect("a readable object");
767 let section = file.section_by_name(".text").expect("a text section");
768 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
769 }
770
771 #[test]
772 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
773 let mut text = calling("puts");
774 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
775 text.bytes.resize(17, 0x90);
776 let bytes =
777 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
778 let file = object::File::parse(&bytes[..]).expect("a readable object");
779 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
780 assert_eq!(g.address(), 16);
781 assert_eq!(g.size(), 1);
782 assert_eq!(g.kind(), SymbolKind::Text);
783 assert!(g.is_global(), "nothing said otherwise about this one");
784 }
785
786 #[test]
787 fn a_function_no_other_file_can_see_is_a_local_symbol() {
788 let mut text = calling("puts");
789 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
790 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
791 text.bytes.resize(33, 0x90);
792 let bytes =
793 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
794 let file = object::File::parse(&bytes[..]).expect("a readable object");
795 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
796 assert!(hidden.is_local(), "a static function must not be offered to the linker");
799 assert!(!hidden.is_weak());
800 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
801 assert!(shared.is_weak(), "a weak function has to be able to lose");
802 assert!(shared.is_global());
803 }
804
805 #[test]
822 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
823 let mut text = calling("puts");
824 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
825 text.funcs[0].start = 3;
826 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
827 text.relocs[0].at = 4;
828 let bytes =
829 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
830 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
831 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
832 assert_eq!(section.size(), 8, "one address, and this file defines one function");
833 assert_eq!(section.align(), 8);
834 let header = section.elf_section_header();
835 assert_eq!(
836 header.sh_flags.get(Endianness::Little),
837 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
838 );
839 let index = file.section_by_name(".text").expect("a text section").index().0;
842 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
843 assert_ne!(index, 0);
844
845 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
847 panic!("one address in the record")
848 };
849 assert_eq!(*at, 0);
850 assert_eq!(reloc.addend(), 0);
851 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
852 }
853
854 #[test]
856 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
857 let text = calling("puts");
858 let bytes =
859 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
860 let file = object::File::parse(&bytes[..]).expect("a readable object");
861 assert!(file.section_by_name(PATCHABLE).is_none());
862 }
863
864 #[test]
870 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
871 let mut text = calling("puts");
872 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
873 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
874 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
875 text.bytes.resize(17, 0x90);
876 let output =
877 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
878 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
879 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
880 let links: Vec<usize> = file
881 .sections()
882 .filter(|section| section.name() == Ok(PATCHABLE))
883 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
884 .collect();
885 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
886 assert_eq!(links, [index(".text.f"), index(".text.g")]);
887 }
888
889 #[test]
890 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
891 let mut text = calling("puts");
892 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
893 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
894 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
895 text.bytes.resize(49, 0x90);
896 let bytes =
897 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
898 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
899 let visibility = |name: &str| {
900 file.symbols()
901 .find(|s| s.name() == Ok(name))
902 .expect("the function")
903 .elf_symbol()
904 .st_visibility()
905 };
906 assert_eq!(visibility("g"), elf::STV_DEFAULT);
908 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
909 assert_eq!(visibility("s"), elf::STV_DEFAULT);
912 }
913
914 #[test]
924 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
925 let mut text = calling("puts");
926 for (index, (name, seen)) in
927 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
928 {
929 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
930 func.visibility = seen;
931 text.funcs.push(func);
932 }
933 text.bytes.resize(49, 0x90);
934 let mut data = Data::default();
935 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
936 let mut object = variable(name, Place::Written);
937 object.visibility = seen;
938 data.objects.push(object);
939 }
940 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
941 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
942 let visibility = |name: &str| {
943 file.symbols()
944 .find(|s| s.name() == Ok(name))
945 .expect("the symbol")
946 .elf_symbol()
947 .st_visibility()
948 };
949 assert_eq!(visibility("h"), elf::STV_HIDDEN);
950 assert_eq!(visibility("p"), elf::STV_PROTECTED);
951 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
952 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
953 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
956 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
957 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
958 }
959
960 #[test]
961 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
962 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
963 .expect("an object");
964 let file = object::File::parse(&bytes[..]).expect("a readable object");
965 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
966 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
967 }
968
969 #[test]
970 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
971 for (reference, wanted) in [
972 (Reference::Call, elf::R_X86_64_PLT32),
973 (Reference::Data, elf::R_X86_64_PC32),
974 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
975 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
976 ] {
977 let mut text = calling("puts");
978 text.relocs[0].kind = reference;
979 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
980 .expect("an object");
981 let file = object::File::parse(&bytes[..]).expect("a readable object");
982 let section = file.section_by_name(".text").expect("a text section");
983 let (offset, reloc) = section.relocations().next().expect("one relocation");
984 assert_eq!(offset, 1);
985 assert_eq!(reloc.addend(), -4);
986 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
987 }
988 }
989
990 #[test]
991 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
992 let mut text = calling("puts");
993 text.relocs.push(Reloc {
994 at: 1,
995 symbol: "puts".to_owned(),
996 kind: Reference::Call,
997 addend: -4,
998 });
999 let bytes =
1000 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1001 let file = object::File::parse(&bytes[..]).expect("a readable object");
1002 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1003 }
1004
1005 #[test]
1006 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1007 let text = calling("f");
1008 let bytes =
1009 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1010 let file = object::File::parse(&bytes[..]).expect("a readable object");
1011 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1012 let f = found.next().expect("the function");
1013 assert!(!f.is_undefined(), "the file defines it");
1014 assert!(found.next().is_none(), "and defines it once");
1015 }
1016
1017 #[test]
1018 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1019 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1020 .expect("an object");
1021 let file = object::File::parse(&bytes[..]).expect("a readable object");
1022 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1023 assert!(note.data().expect("no bytes").is_empty());
1024 }
1025
1026 #[test]
1033 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1034 let property = Property { features: Property::IBT | Property::SHSTK };
1035 let output = Output { property, ..Output::default() };
1036 let bytes =
1037 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
1038 let file = object::File::parse(&bytes[..]).expect("a readable object");
1039 let note = file.section_by_name(".note.gnu.property").expect("the note");
1040 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1041 let want: Vec<u8> = [
1042 4u32,
1043 16,
1044 5,
1045 u32::from_le_bytes(*b"GNU\0"),
1046 Property::X86_FEATURES,
1047 4,
1048 Property::IBT | Property::SHSTK,
1049 0,
1050 ]
1051 .iter()
1052 .flat_map(|word| word.to_le_bytes())
1053 .collect();
1054 assert_eq!(note.data().expect("the bytes"), &want[..]);
1055 }
1056
1057 #[test]
1063 fn a_file_built_to_have_nothing_checked_says_nothing() {
1064 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1065 .expect("an object");
1066 let file = object::File::parse(&bytes[..]).expect("a readable object");
1067 assert!(file.section_by_name(".note.gnu.property").is_none());
1068 }
1069
1070 #[test]
1079 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1080 let mut text = calling("puts");
1081 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1082 text.bytes.resize(17, 0x90);
1083 text.unwind.bytes = vec![0; 64];
1086 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1087 text.unwind.relocs.push(Reloc {
1088 at,
1089 symbol: name.to_owned(),
1090 kind: Reference::Address { bytes: 8 },
1091 addend: 0,
1092 });
1093 }
1094 let bytes =
1095 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1096 let file = object::File::parse(&bytes[..]).expect("a readable object");
1097 let mut found = points_at(&file);
1098 found.sort_unstable();
1099 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1100 }
1101
1102 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1105 let frames = file.section_by_name(".eh_frame").expect("the table");
1106 frames
1107 .relocations()
1108 .map(|(offset, reloc)| {
1109 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1110 panic!("a record points at something that is not a symbol");
1111 };
1112 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1113 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1114 let section = symbol.section_index().expect("a section symbol is in one");
1115 let name = file.section_by_index(section).expect("a readable section");
1116 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1117 })
1118 .collect()
1119 }
1120
1121 #[test]
1134 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1135 let mut text = two();
1136 text.unwind.bytes = vec![0; 64];
1137 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1138 text.unwind.relocs.push(Reloc {
1139 at,
1140 symbol: name.to_owned(),
1141 kind: Reference::Data,
1142 addend: 0,
1143 });
1144 }
1145 let bytes =
1146 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1147 let file = object::File::parse(&bytes[..]).expect("a readable object");
1148 let mut whole = points_at(&file);
1149 whole.sort_unstable();
1150 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1151
1152 let sections =
1153 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1154 let bytes = write(&text, &Data::default(), &[], &target(), sections).expect("an object");
1155 let file = object::File::parse(&bytes[..]).expect("a readable object");
1156 let mut split = points_at(&file);
1157 split.sort_unstable();
1158 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1159 }
1160
1161 #[test]
1168 fn a_record_about_something_this_file_does_not_define_is_refused() {
1169 let mut text = calling("puts");
1170 text.unwind.bytes = vec![0; 64];
1171 text.unwind.relocs.push(Reloc {
1172 at: 32,
1173 symbol: "puts".to_owned(),
1174 kind: Reference::Data,
1175 addend: 0,
1176 });
1177 let why = write(&text, &Data::default(), &[], &target(), Output::default())
1178 .expect_err("a record about a name from somewhere else");
1179 assert!(why.to_string().contains("puts"), "{why}");
1180 }
1181
1182 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1184 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1185 let index = symbol.section_index().expect("a section to be defined in");
1186 let section = file.section_by_index(index).expect("a readable section");
1187 section.name().expect("a named section").to_owned()
1188 }
1189
1190 fn two() -> Text {
1192 let mut text = calling("puts");
1193 text.bytes.resize(16, 0x90);
1196 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1197 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1198 text.relocs.push(Reloc {
1199 at: 17,
1200 symbol: "puts".to_owned(),
1201 kind: Reference::Call,
1202 addend: -4,
1203 });
1204 text
1205 }
1206
1207 #[test]
1214 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1215 let sections =
1216 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1217 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1218 let file = object::File::parse(&bytes[..]).expect("a readable object");
1219 assert_eq!(lives_in(&file, "f"), ".text.f");
1220 assert_eq!(lives_in(&file, "g"), ".text.g");
1221 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1222 for name in ["f", "g"] {
1225 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1226 assert_eq!(symbol.address(), 0, "{name}");
1227 assert_eq!(symbol.size(), 6, "{name}");
1228 }
1229 let section = file.section_by_name(".text.g").expect("the second function");
1230 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1231 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1234 }
1235
1236 #[test]
1242 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1243 let sections =
1244 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1245 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1246 let file = object::File::parse(&bytes[..]).expect("a readable object");
1247 for name in [".text.f", ".text.g"] {
1248 let section = file.section_by_name(name).expect("a function");
1249 let (offset, _) = section.relocations().next().expect("the call in it");
1250 assert_eq!(offset, 1, "{name}");
1253 assert_eq!(section.relocations().count(), 1, "{name}");
1254 }
1255 }
1256
1257 fn variable(name: &str, place: Place) -> Object {
1259 Object {
1260 name: name.to_owned(),
1261 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1262 size: 4,
1263 align: 4,
1264 place,
1265 binding: Binding::Global,
1266 visibility: Visibility::Default,
1267 relocs: Vec::new(),
1268 }
1269 }
1270
1271 fn holding(object: Object) -> Vec<u8> {
1273 let data = Data { objects: vec![object] };
1274 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1275 }
1276
1277 #[test]
1278 fn what_a_variable_is_decides_which_section_it_goes_in() {
1279 for (place, wanted) in [
1280 (Place::Written, ".data"),
1281 (Place::ReadOnly, ".rodata"),
1282 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1283 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1284 (Place::Zero, ".bss"),
1285 (Place::Thread { zero: false }, ".tdata"),
1286 (Place::Thread { zero: true }, ".tbss"),
1287 (Place::Named(".init_array".to_owned()), ".init_array"),
1288 ] {
1289 let bytes = holding(variable("x", place.clone()));
1290 let file = object::File::parse(&bytes[..]).expect("a readable object");
1291 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1292 assert_eq!(section.size(), 4, "{place:?}");
1293 let carried = section.data().expect("the bytes").len();
1296 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1297 }
1298 }
1299
1300 #[test]
1306 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1307 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1308 let bytes = holding(variable("counter", place.clone()));
1309 let file = object::File::parse(&bytes[..]).expect("a readable object");
1310 let symbol = file
1311 .symbols()
1312 .find(|symbol| symbol.name() == Ok("counter"))
1313 .unwrap_or_else(|| panic!("{place:?}"));
1314 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1315 }
1316 }
1317
1318 #[test]
1324 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1325 for (name, wanted) in [
1326 (".init_array", elf::SHT_INIT_ARRAY),
1327 (".init_array.00101", elf::SHT_INIT_ARRAY),
1328 (".fini_array", elf::SHT_FINI_ARRAY),
1329 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1330 (".init_arrays", elf::SHT_PROGBITS),
1331 ] {
1332 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1333 let file = object::File::parse(&bytes[..]).expect("a readable object");
1334 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1335 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1336 panic!("{name} is not an elf section");
1337 };
1338 assert_eq!(sh_type, wanted, "{name}");
1339 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1340 }
1341 }
1342
1343 #[test]
1349 fn two_variables_in_one_named_section_share_it() {
1350 let objects = vec![
1351 variable("x", Place::Named(".init_array".to_owned())),
1352 variable("y", Place::Named(".init_array".to_owned())),
1353 ];
1354 let data = Data { objects };
1355 let bytes =
1356 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1357 let file = object::File::parse(&bytes[..]).expect("a readable object");
1358 let named: Vec<_> =
1359 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1360 assert_eq!(named.len(), 1);
1361 assert_eq!(named[0].size(), 8);
1362 }
1363
1364 #[test]
1368 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1369 let sections =
1370 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1371 for (place, wanted) in [
1372 (Place::Written, ".data.x"),
1373 (Place::ReadOnly, ".rodata.x"),
1374 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1375 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1376 (Place::Zero, ".bss.x"),
1377 (Place::Thread { zero: false }, ".tdata.x"),
1378 (Place::Thread { zero: true }, ".tbss.x"),
1379 ] {
1380 let data = Data { objects: vec![variable("x", place.clone())] };
1381 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1382 let file = object::File::parse(&bytes[..]).expect("a readable object");
1383 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1384 let section = file.section_by_name(wanted).expect("the section it named");
1385 assert_eq!(section.size(), 4, "{place:?}");
1386 let carried = section.data().expect("the bytes").len();
1389 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1390 }
1391 }
1392
1393 #[test]
1397 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1398 let sections =
1399 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1400 let named = Place::Named(".init_array".to_owned());
1401 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1402 let bytes =
1403 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1404 let file = object::File::parse(&bytes[..]).expect("a readable object");
1405 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1406 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1407 assert_eq!(lives_in(&file, "n"), ".init_array");
1408 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1409 }
1410
1411 #[test]
1415 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1416 let sections =
1417 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1418 let pointer = Object {
1419 bytes: vec![0; 8],
1420 size: 8,
1421 align: 8,
1422 relocs: vec![Reloc {
1423 at: 0,
1424 symbol: "y".to_owned(),
1425 kind: Reference::Address { bytes: 8 },
1426 addend: 0,
1427 }],
1428 ..variable("p", Place::Written)
1429 };
1430 let objects = vec![variable("first", Place::Written), pointer];
1431 let bytes =
1432 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1433 let file = object::File::parse(&bytes[..]).expect("a readable object");
1434 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1435 let (offset, reloc) = section.relocations().next().expect("one relocation");
1436 assert_eq!(offset, 0);
1439 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1440 }
1441
1442 #[test]
1450 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1451 let place = Place::RelocReadOnly { local: true };
1452 let data =
1453 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
1454 let bytes =
1455 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1456 let file = object::File::parse(&bytes[..]).expect("a readable object");
1457 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1458 assert_eq!(named, 1, "one section holding both, not one each");
1459 }
1460
1461 #[test]
1462 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1463 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1464 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1465 let bytes =
1466 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1467 let file = object::File::parse(&bytes[..]).expect("a readable object");
1468 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1469 assert_eq!(second.kind(), SymbolKind::Data);
1470 assert_eq!(second.size(), 4);
1471 assert_eq!(second.address(), 16);
1475 }
1476
1477 #[test]
1478 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1479 for (binding, global, weak) in [
1480 (Binding::Global, true, false),
1481 (Binding::Local, false, false),
1482 (Binding::Weak, true, true),
1483 ] {
1484 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1485 let file = object::File::parse(&bytes[..]).expect("a readable object");
1486 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1487 assert_eq!(x.is_global(), global, "{binding:?}");
1488 assert_eq!(x.is_weak(), weak, "{binding:?}");
1489 }
1490 }
1491
1492 #[test]
1493 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1494 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1495 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1496 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1497 assert!(x.is_common(), "the linker merges every definition of this name into one");
1498 assert_eq!(x.size(), 4);
1499 assert_eq!(x.address(), 0);
1503 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1504 }
1505
1506 #[test]
1507 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1508 let object = Object {
1509 bytes: vec![0; 8],
1510 size: 8,
1511 align: 8,
1512 relocs: vec![Reloc {
1513 at: 0,
1514 symbol: "y".to_owned(),
1515 kind: Reference::Address { bytes: 8 },
1516 addend: 16,
1517 }],
1518 ..variable("p", Place::Written)
1519 };
1520 let bytes = holding(object);
1521 let file = object::File::parse(&bytes[..]).expect("a readable object");
1522 let section = file.section_by_name(".data").expect("a data section");
1523 let (offset, reloc) = section.relocations().next().expect("one relocation");
1524 assert_eq!(offset, 0);
1525 assert_eq!(reloc.addend(), 16);
1526 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1527 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1528 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1529 }
1530
1531 #[test]
1533 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1534 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1535 data.objects.push(Object {
1536 bytes: vec![0; 16],
1537 size: 16,
1538 align: 8,
1539 relocs: vec![Reloc {
1540 at: 8,
1541 symbol: "y".to_owned(),
1542 kind: Reference::Address { bytes: 8 },
1543 addend: 0,
1544 }],
1545 ..variable("second", Place::Written)
1546 });
1547 let bytes =
1548 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1549 let file = object::File::parse(&bytes[..]).expect("a readable object");
1550 let section = file.section_by_name(".data").expect("a data section");
1551 let (offset, _) = section.relocations().next().expect("one relocation");
1552 assert_eq!(offset, 16);
1555 }
1556
1557 #[test]
1558 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1559 let data = Data {
1560 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1561 };
1562 let aliases = [Alias {
1563 name: "b".to_owned(),
1564 target: "a".to_owned(),
1565 binding: Binding::Global,
1566 visibility: Visibility::Default,
1567 }];
1568 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1569 .expect("an object");
1570 let file = object::File::parse(&bytes[..]).expect("a readable object");
1571 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1572 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1573 assert_eq!(b.address(), a.address(), "the same place");
1574 assert_eq!(b.size(), a.size());
1575 assert_eq!(b.section_index(), a.section_index());
1576 assert!(a.is_local(), "the target was written `static`");
1579 assert!(b.is_global(), "and the name given to it was not");
1580 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1582 }
1583
1584 #[test]
1585 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1586 let text = calling("puts");
1587 let aliases = [Alias {
1588 name: "g".to_owned(),
1589 target: "f".to_owned(),
1590 binding: Binding::Weak,
1591 visibility: Visibility::Default,
1592 }];
1593 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1594 .expect("an object");
1595 let file = object::File::parse(&bytes[..]).expect("a readable object");
1596 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1597 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1598 assert_eq!(g.address(), f.address());
1599 assert_eq!(g.size(), f.size());
1600 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1601 assert!(g.is_weak(), "so that a program may define the name itself instead");
1602 }
1603
1604 #[test]
1607 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1608 let aliases = [Alias {
1609 name: "b".to_owned(),
1610 target: "a".to_owned(),
1611 binding: Binding::Global,
1612 visibility: Visibility::Default,
1613 }];
1614 let error =
1615 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1616 .expect_err("nothing to point at");
1617 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1618 }
1619
1620 #[test]
1621 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1622 let text = calling("puts");
1623 for triple in [
1624 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1625 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1626 ] {
1627 let error =
1628 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1629 .expect_err("no writer");
1630 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1631 }
1632 }
1633
1634 #[test]
1640 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
1641 let mut text = calling("puts");
1642 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1643 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1644 text.bytes.resize(33, 0x90);
1645 let data = Data {
1646 objects: vec![variable("seen", Place::Written), {
1647 let mut quiet = variable("quiet", Place::Zero);
1648 quiet.binding = Binding::Local;
1649 quiet
1650 }],
1651 };
1652 let aliases = [Alias {
1653 name: "second".to_owned(),
1654 target: "f".to_owned(),
1655 binding: Binding::Global,
1656 visibility: Visibility::Default,
1657 }];
1658
1659 let names = defines(&text, &data, &aliases, &target()).expect("a list");
1660 assert_eq!(names, ["f", "shared", "seen", "second"]);
1661
1662 let bytes = write(&text, &data, &aliases, &target(), Output::default()).expect("an object");
1663 let file = object::File::parse(&bytes[..]).expect("a readable object");
1664 let found: Vec<String> = file
1665 .symbols()
1666 .filter(|symbol| symbol.is_global() && symbol.is_definition())
1667 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
1668 .collect();
1669 let mut sorted = names.clone();
1670 sorted.sort();
1671 let mut theirs = found;
1672 theirs.sort();
1673 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
1674 }
1675
1676 #[test]
1680 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
1681 let text = calling("puts");
1682 for triple in [
1683 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1684 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1685 ] {
1686 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
1687 .expect_err("no writer");
1688 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1689 }
1690 }
1691}