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 for label in &text.labels {
194 let after = text.funcs.partition_point(|func| func.start <= label.at);
195 let Some(index) = after.checked_sub(1) else {
196 let why = format!("'{}' is at {} and in front of every function", label.name, label.at);
197 return Err(Error::Refused { why });
198 };
199 let func = &text.funcs[index];
200 let (section, at) = if sections.functions {
201 let base = func.start - func.patch.map_or(0, |patch| patch.before);
204 (split[index].0, (label.at - base) as u64)
205 } else {
206 (whole, label.at as u64)
207 };
208 let id = obj.add_symbol(Symbol {
209 name: label.name.clone().into_bytes(),
210 value: at,
211 size: 0,
214 kind: SymbolKind::Label,
215 scope: SymbolScope::Compilation,
219 weak: false,
220 section: SymbolSection::Section(section),
221 flags: SymbolFlags::None,
222 });
223 symbols.insert(label.name.clone(), id);
224 }
225
226 let mut placed = Vec::with_capacity(data.objects.len());
231 let mut named = HashMap::new();
235 for object in &data.objects {
236 let (section, offset) = put(&mut obj, object, &mut named, sections);
237 let id = obj.add_symbol(Symbol {
238 name: object.name.clone().into_bytes(),
239 value: if object.place == Place::Merged { object.align } else { offset },
242 size: object.size,
243 kind: match object.place {
248 Place::Thread { .. } => SymbolKind::Tls,
249 _ => SymbolKind::Data,
250 },
251 scope: scope_of(object.binding),
252 weak: object.binding == Binding::Weak,
253 section,
254 flags: SymbolFlags::None,
255 });
256 see(&mut obj, id, object.binding, object.visibility);
257 symbols.insert(object.name.clone(), id);
258 placed.push((section.id(), offset));
259 }
260
261 for alias in aliases {
267 let Some(&id) = symbols.get(&alias.target) else {
268 let why =
269 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
270 return Err(Error::Refused { why });
271 };
272 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
273 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
274 let id = obj.add_symbol(Symbol {
275 name: alias.name.clone().into_bytes(),
276 value,
277 size,
278 kind,
279 scope: scope_of(alias.binding),
280 weak: alias.binding == Binding::Weak,
281 section,
282 flags: SymbolFlags::None,
283 });
284 see(&mut obj, id, alias.binding, alias.visibility);
285 symbols.insert(alias.name.clone(), id);
286 }
287
288 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
292 for reloc in wanted {
293 if symbols.contains_key(&reloc.symbol) {
294 continue;
295 }
296 let id = obj.add_symbol(Symbol {
297 name: reloc.symbol.clone().into_bytes(),
298 value: 0,
299 size: 0,
300 kind: SymbolKind::Unknown,
304 scope: SymbolScope::Dynamic,
305 weak: false,
306 section: SymbolSection::Undefined,
307 flags: SymbolFlags::None,
308 });
309 symbols.insert(reloc.symbol.clone(), id);
310 }
311
312 for reloc in &text.relocs {
313 let (section, at) = if sections.functions {
318 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
319 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
320 let why = format!("a relocation at {} is in front of every function", reloc.at);
321 return Err(Error::Refused { why });
322 };
323 let base = func.start - func.patch.map_or(0, |patch| patch.before);
326 (split[after - 1].0, (reloc.at - base) as u64)
327 } else {
328 (whole, reloc.at as u64)
329 };
330 add(&mut obj, section, at, reloc, &symbols)?;
331 }
332
333 if !text.unwind.bytes.is_empty() {
339 let frames = obj.add_section(Vec::new(), b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
340 obj.append_section_data(frames, &text.unwind.bytes, 8);
341 for reloc in &text.unwind.relocs {
342 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
355 let Some((section, at)) = found.map(|i| split[i]) else {
356 let why =
357 format!("'{}' has an unwind record and is not a function here", reloc.symbol);
358 return Err(Error::Refused { why });
359 };
360 let symbol = obj.section_symbol(section);
361 let r_type = r_type(reloc.kind).ok_or_else(|| Error::Refused {
362 why: format!("no relocation is {:?}", reloc.kind),
363 })?;
364 let record = Relocation {
365 offset: reloc.at as u64,
366 symbol,
367 addend: reloc.addend + at as i64,
371 flags: RelocationFlags::Elf { r_type },
372 };
373 obj.add_relocation(frames, record)
374 .map_err(|why| Error::Refused { why: why.to_string() })?;
375 }
376 }
377 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
378 let Some(section) = section else { continue };
379 for reloc in &object.relocs {
380 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols)?;
381 }
382 }
383
384 if property.any() {
388 let note = obj.section_id(StandardSection::GnuProperty);
389 obj.append_section_data(note, &record(property), 8);
390 }
391
392 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
395
396 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
397 link(&mut bytes, &ordered);
398 Ok(bytes)
399}
400
401pub fn defines(
425 text: &Text,
426 data: &Data,
427 aliases: &[Alias],
428 target: &TargetInfo,
429) -> Result<Vec<String>, Error> {
430 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
431 return Err(Error::Format { triple: target.tuple.to_string() });
432 }
433 let names = text
434 .funcs
435 .iter()
436 .filter(|func| func.binding != Binding::Local)
437 .map(|func| func.name.clone())
438 .chain(
439 data.objects
440 .iter()
441 .filter(|object| object.binding != Binding::Local)
442 .map(|object| object.name.clone()),
443 )
444 .chain(
445 aliases
446 .iter()
447 .filter(|alias| alias.binding != Binding::Local)
448 .map(|alias| alias.name.clone()),
449 )
450 .collect();
451 Ok(names)
452}
453
454const PATCHABLE: &str = "__patchable_function_entries";
456
457fn link(bytes: &mut [u8], ordered: &[String]) {
472 if ordered.is_empty() {
473 return;
474 }
475 let word = |bytes: &[u8], at: usize| u64::from_le_bytes(bytes[at..at + 8].try_into().unwrap());
476 let short = |bytes: &[u8], at: usize| u16::from_le_bytes(bytes[at..at + 2].try_into().unwrap());
477 let long = |bytes: &[u8], at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap());
478 let headers = word(bytes, 0x28) as usize;
483 let step = short(bytes, 0x3a) as usize;
484 let count = short(bytes, 0x3c) as usize;
485 let strings = word(bytes, headers + short(bytes, 0x3e) as usize * step + 24) as usize;
486 let name = |bytes: &[u8], header: usize| {
487 let at = strings + long(bytes, header) as usize;
488 let end = bytes[at..].iter().position(|byte| *byte == 0).map_or(at, |len| at + len);
489 String::from_utf8_lossy(&bytes[at..end]).into_owned()
490 };
491 let names: Vec<String> = (0..count).map(|i| name(bytes, headers + i * step)).collect();
492 let mut wanted = ordered.iter();
493 for (i, section) in names.iter().enumerate() {
494 if section != PATCHABLE {
495 continue;
496 }
497 let Some(target) = wanted.next() else { break };
498 let Some(at) = names.iter().position(|name| name == target) else { continue };
499 let at = u32::try_from(at).expect("a file with this many sections in it");
500 let sh_link = headers + i * step + 40;
501 bytes[sh_link..sh_link + 4].copy_from_slice(&at.to_le_bytes());
502 }
503 debug_assert!(wanted.next().is_none(), "a record whose header nothing found");
504}
505
506fn record(property: Property) -> Vec<u8> {
517 let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
520 let desc = [Property::X86_FEATURES, 4, property.features, 0];
521 let mut out = Vec::with_capacity(32);
522 for word in head {
523 out.extend_from_slice(&word.to_le_bytes());
524 }
525 out.extend_from_slice(b"GNU\0");
528 for word in desc {
529 out.extend_from_slice(&word.to_le_bytes());
530 }
531 out
532}
533
534fn put(
541 obj: &mut Writer<'_>,
542 object: &Object,
543 named: &mut HashMap<String, object::write::SectionId>,
544 sections: Sections,
545) -> (SymbolSection, u64) {
546 if sections.data {
552 if let Some(name) = object.place.split(&object.name) {
553 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
554 let offset = if carries_no_bytes(&object.place) {
555 obj.append_section_bss(section, object.size, object.align)
556 } else {
557 obj.append_section_data(section, &object.bytes, object.align)
558 };
559 return (SymbolSection::Section(section), offset);
560 }
561 }
562 let section = match &object.place {
563 Place::Written => obj.section_id(StandardSection::Data),
564 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
565 Place::RelocReadOnly { local: false } => {
571 obj.section_id(StandardSection::ReadOnlyDataWithRel)
572 }
573 Place::RelocReadOnly { local: true } => {
574 made(obj, named, ".data.rel.ro.local", SectionKind::ReadOnlyDataWithRel)
575 }
576 Place::Zero => obj.section_id(StandardSection::UninitializedData),
577 Place::Thread { zero: false } => obj.section_id(StandardSection::Tls),
578 Place::Thread { zero: true } => obj.section_id(StandardSection::UninitializedTls),
579 Place::Merged => return (SymbolSection::Common, 0),
580 Place::Named(name) => {
586 let section = made(obj, named, name, SectionKind::Data);
587 if let Some(array) = Array::of(name) {
588 obj.section_mut(section).flags = SectionFlags::Elf {
589 sh_type: match array {
590 Array::Init => elf::SHT_INIT_ARRAY,
591 Array::Fini => elf::SHT_FINI_ARRAY,
592 Array::Preinit => elf::SHT_PREINIT_ARRAY,
593 },
594 sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE,
595 };
596 }
597 section
598 }
599 };
600 let offset = if carries_no_bytes(&object.place) {
601 obj.append_section_bss(section, object.size, object.align)
602 } else {
603 obj.append_section_data(section, &object.bytes, object.align)
604 };
605 (SymbolSection::Section(section), offset)
606}
607
608fn carries_no_bytes(place: &Place) -> bool {
614 matches!(place, Place::Zero | Place::Thread { zero: true })
615}
616
617fn made(
625 obj: &mut Writer<'_>,
626 named: &mut HashMap<String, object::write::SectionId>,
627 name: &str,
628 kind: SectionKind,
629) -> object::write::SectionId {
630 if let Some(section) = named.get(name) {
631 return *section;
632 }
633 let section = obj.add_section(Vec::new(), name.as_bytes().to_vec(), kind);
634 named.insert(name.to_owned(), section);
635 section
636}
637
638fn kind_of(place: &Place) -> SectionKind {
647 match place {
648 Place::ReadOnly => SectionKind::ReadOnlyData,
649 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
650 Place::Zero => SectionKind::UninitializedData,
651 Place::Thread { zero: false } => SectionKind::Tls,
652 Place::Thread { zero: true } => SectionKind::UninitializedTls,
653 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
654 }
655}
656
657fn add(
664 obj: &mut Writer<'_>,
665 section: object::write::SectionId,
666 at: u64,
667 reloc: &Reloc,
668 symbols: &std::collections::BTreeMap<String, SymbolId>,
669) -> Result<(), Error> {
670 let r_type = r_type(reloc.kind)
671 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
672 obj.add_relocation(
673 section,
674 Relocation {
675 offset: at,
676 symbol: symbols[&reloc.symbol],
677 addend: reloc.addend,
678 flags: RelocationFlags::Elf { r_type },
679 },
680 )
681 .map_err(|why| Error::Refused { why: why.to_string() })
682}
683
684pub(crate) fn scope_of(binding: Binding) -> SymbolScope {
697 match binding {
698 Binding::Local => SymbolScope::Compilation,
699 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
700 }
701}
702
703pub(crate) fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
715 if binding == Binding::Local {
716 return;
717 }
718 let wanted = match visibility {
719 Visibility::Default => elf::STV_DEFAULT,
720 Visibility::Hidden => elf::STV_HIDDEN,
721 Visibility::Protected => elf::STV_PROTECTED,
722 };
723 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
724 *st_other = st_other.with_visibility(wanted);
725 }
726}
727
728pub(crate) fn r_type(reference: Reference) -> Option<elf::RelocationType> {
740 Some(match reference {
741 Reference::Call => elf::R_X86_64_PLT32,
742 Reference::Data => elf::R_X86_64_PC32,
743 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
744 Reference::Thread => elf::R_X86_64_GOTTPOFF,
745 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
746 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
747 Reference::Address { .. } => return None,
748 })
749}
750
751#[cfg(test)]
752mod tests {
753 use super::*;
754
755 use object::read::elf::Sym as _;
756 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
757 use rucc_target::{Arch, Env, Os, Triple};
758
759 use crate::section::{Extent, Patch, Reloc};
760
761 fn target() -> TargetInfo {
763 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
764 }
765
766 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
771 Extent {
772 name,
773 start,
774 len,
775 align: crate::FUNC_ALIGN,
776 binding,
777 visibility: Visibility::Default,
778 patch: None,
779 }
780 }
781
782 fn calling(name: &str) -> Text {
784 Text {
785 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
786 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
787 relocs: vec![Reloc {
788 at: 1,
789 symbol: name.to_owned(),
790 kind: Reference::Call,
791 addend: -4,
792 }],
793 ..Text::default()
794 }
795 }
796
797 #[test]
798 fn the_bytes_come_back_out_of_the_section_they_went_into() {
799 let text = calling("puts");
800 let bytes =
801 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
802 let file = object::File::parse(&bytes[..]).expect("a readable object");
803 let section = file.section_by_name(".text").expect("a text section");
804 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
805 }
806
807 #[test]
808 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
809 let mut text = calling("puts");
810 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
811 text.bytes.resize(17, 0x90);
812 let bytes =
813 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
814 let file = object::File::parse(&bytes[..]).expect("a readable object");
815 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
816 assert_eq!(g.address(), 16);
817 assert_eq!(g.size(), 1);
818 assert_eq!(g.kind(), SymbolKind::Text);
819 assert!(g.is_global(), "nothing said otherwise about this one");
820 }
821
822 #[test]
823 fn a_function_no_other_file_can_see_is_a_local_symbol() {
824 let mut text = calling("puts");
825 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
826 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
827 text.bytes.resize(33, 0x90);
828 let bytes =
829 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
830 let file = object::File::parse(&bytes[..]).expect("a readable object");
831 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
832 assert!(hidden.is_local(), "a static function must not be offered to the linker");
835 assert!(!hidden.is_weak());
836 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
837 assert!(shared.is_weak(), "a weak function has to be able to lose");
838 assert!(shared.is_global());
839 }
840
841 #[test]
858 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
859 let mut text = calling("puts");
860 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
861 text.funcs[0].start = 3;
862 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
863 text.relocs[0].at = 4;
864 let bytes =
865 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
866 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
867 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
868 assert_eq!(section.size(), 8, "one address, and this file defines one function");
869 assert_eq!(section.align(), 8);
870 let header = section.elf_section_header();
871 assert_eq!(
872 header.sh_flags.get(Endianness::Little),
873 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
874 );
875 let index = file.section_by_name(".text").expect("a text section").index().0;
878 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
879 assert_ne!(index, 0);
880
881 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
883 panic!("one address in the record")
884 };
885 assert_eq!(*at, 0);
886 assert_eq!(reloc.addend(), 0);
887 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
888 }
889
890 #[test]
892 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
893 let text = calling("puts");
894 let bytes =
895 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
896 let file = object::File::parse(&bytes[..]).expect("a readable object");
897 assert!(file.section_by_name(PATCHABLE).is_none());
898 }
899
900 #[test]
906 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
907 let mut text = calling("puts");
908 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
909 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
910 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
911 text.bytes.resize(17, 0x90);
912 let output =
913 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
914 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
915 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
916 let links: Vec<usize> = file
917 .sections()
918 .filter(|section| section.name() == Ok(PATCHABLE))
919 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
920 .collect();
921 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
922 assert_eq!(links, [index(".text.f"), index(".text.g")]);
923 }
924
925 #[test]
926 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
927 let mut text = calling("puts");
928 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
929 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
930 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
931 text.bytes.resize(49, 0x90);
932 let bytes =
933 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
934 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
935 let visibility = |name: &str| {
936 file.symbols()
937 .find(|s| s.name() == Ok(name))
938 .expect("the function")
939 .elf_symbol()
940 .st_visibility()
941 };
942 assert_eq!(visibility("g"), elf::STV_DEFAULT);
944 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
945 assert_eq!(visibility("s"), elf::STV_DEFAULT);
948 }
949
950 #[test]
960 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
961 let mut text = calling("puts");
962 for (index, (name, seen)) in
963 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
964 {
965 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
966 func.visibility = seen;
967 text.funcs.push(func);
968 }
969 text.bytes.resize(49, 0x90);
970 let mut data = Data::default();
971 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
972 let mut object = variable(name, Place::Written);
973 object.visibility = seen;
974 data.objects.push(object);
975 }
976 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
977 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
978 let visibility = |name: &str| {
979 file.symbols()
980 .find(|s| s.name() == Ok(name))
981 .expect("the symbol")
982 .elf_symbol()
983 .st_visibility()
984 };
985 assert_eq!(visibility("h"), elf::STV_HIDDEN);
986 assert_eq!(visibility("p"), elf::STV_PROTECTED);
987 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
988 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
989 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
992 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
993 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
994 }
995
996 #[test]
997 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
998 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
999 .expect("an object");
1000 let file = object::File::parse(&bytes[..]).expect("a readable object");
1001 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
1002 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
1003 }
1004
1005 #[test]
1006 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
1007 for (reference, wanted) in [
1008 (Reference::Call, elf::R_X86_64_PLT32),
1009 (Reference::Data, elf::R_X86_64_PC32),
1010 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
1011 (Reference::Thread, elf::R_X86_64_GOTTPOFF),
1012 ] {
1013 let mut text = calling("puts");
1014 text.relocs[0].kind = reference;
1015 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
1016 .expect("an object");
1017 let file = object::File::parse(&bytes[..]).expect("a readable object");
1018 let section = file.section_by_name(".text").expect("a text section");
1019 let (offset, reloc) = section.relocations().next().expect("one relocation");
1020 assert_eq!(offset, 1);
1021 assert_eq!(reloc.addend(), -4);
1022 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
1023 }
1024 }
1025
1026 #[test]
1027 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
1028 let mut text = calling("puts");
1029 text.relocs.push(Reloc {
1030 at: 1,
1031 symbol: "puts".to_owned(),
1032 kind: Reference::Call,
1033 addend: -4,
1034 });
1035 let bytes =
1036 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1037 let file = object::File::parse(&bytes[..]).expect("a readable object");
1038 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
1039 }
1040
1041 #[test]
1042 fn a_function_that_is_also_called_is_not_a_second_symbol() {
1043 let text = calling("f");
1044 let bytes =
1045 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1046 let file = object::File::parse(&bytes[..]).expect("a readable object");
1047 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
1048 let f = found.next().expect("the function");
1049 assert!(!f.is_undefined(), "the file defines it");
1050 assert!(found.next().is_none(), "and defines it once");
1051 }
1052
1053 #[test]
1054 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
1055 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1056 .expect("an object");
1057 let file = object::File::parse(&bytes[..]).expect("a readable object");
1058 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
1059 assert!(note.data().expect("no bytes").is_empty());
1060 }
1061
1062 #[test]
1069 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
1070 let property = Property { features: Property::IBT | Property::SHSTK };
1071 let output = Output { property, ..Output::default() };
1072 let bytes =
1073 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
1074 let file = object::File::parse(&bytes[..]).expect("a readable object");
1075 let note = file.section_by_name(".note.gnu.property").expect("the note");
1076 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
1077 let want: Vec<u8> = [
1078 4u32,
1079 16,
1080 5,
1081 u32::from_le_bytes(*b"GNU\0"),
1082 Property::X86_FEATURES,
1083 4,
1084 Property::IBT | Property::SHSTK,
1085 0,
1086 ]
1087 .iter()
1088 .flat_map(|word| word.to_le_bytes())
1089 .collect();
1090 assert_eq!(note.data().expect("the bytes"), &want[..]);
1091 }
1092
1093 #[test]
1099 fn a_file_built_to_have_nothing_checked_says_nothing() {
1100 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
1101 .expect("an object");
1102 let file = object::File::parse(&bytes[..]).expect("a readable object");
1103 assert!(file.section_by_name(".note.gnu.property").is_none());
1104 }
1105
1106 #[test]
1115 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
1116 let mut text = calling("puts");
1117 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
1118 text.bytes.resize(17, 0x90);
1119 text.unwind.bytes = vec![0; 64];
1122 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1123 text.unwind.relocs.push(Reloc {
1124 at,
1125 symbol: name.to_owned(),
1126 kind: Reference::Address { bytes: 8 },
1127 addend: 0,
1128 });
1129 }
1130 let bytes =
1131 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1132 let file = object::File::parse(&bytes[..]).expect("a readable object");
1133 let mut found = points_at(&file);
1134 found.sort_unstable();
1135 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1136 }
1137
1138 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
1141 let frames = file.section_by_name(".eh_frame").expect("the table");
1142 frames
1143 .relocations()
1144 .map(|(offset, reloc)| {
1145 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1146 panic!("a record points at something that is not a symbol");
1147 };
1148 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1149 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1150 let section = symbol.section_index().expect("a section symbol is in one");
1151 let name = file.section_by_index(section).expect("a readable section");
1152 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1153 })
1154 .collect()
1155 }
1156
1157 #[test]
1170 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1171 let mut text = two();
1172 text.unwind.bytes = vec![0; 64];
1173 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1174 text.unwind.relocs.push(Reloc {
1175 at,
1176 symbol: name.to_owned(),
1177 kind: Reference::Data,
1178 addend: 0,
1179 });
1180 }
1181 let bytes =
1182 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1183 let file = object::File::parse(&bytes[..]).expect("a readable object");
1184 let mut whole = points_at(&file);
1185 whole.sort_unstable();
1186 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1187
1188 let sections =
1189 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1190 let bytes = write(&text, &Data::default(), &[], &target(), sections).expect("an object");
1191 let file = object::File::parse(&bytes[..]).expect("a readable object");
1192 let mut split = points_at(&file);
1193 split.sort_unstable();
1194 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1195 }
1196
1197 #[test]
1204 fn a_record_about_something_this_file_does_not_define_is_refused() {
1205 let mut text = calling("puts");
1206 text.unwind.bytes = vec![0; 64];
1207 text.unwind.relocs.push(Reloc {
1208 at: 32,
1209 symbol: "puts".to_owned(),
1210 kind: Reference::Data,
1211 addend: 0,
1212 });
1213 let why = write(&text, &Data::default(), &[], &target(), Output::default())
1214 .expect_err("a record about a name from somewhere else");
1215 assert!(why.to_string().contains("puts"), "{why}");
1216 }
1217
1218 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1220 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1221 let index = symbol.section_index().expect("a section to be defined in");
1222 let section = file.section_by_index(index).expect("a readable section");
1223 section.name().expect("a named section").to_owned()
1224 }
1225
1226 fn two() -> Text {
1228 let mut text = calling("puts");
1229 text.bytes.resize(16, 0x90);
1232 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1233 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1234 text.relocs.push(Reloc {
1235 at: 17,
1236 symbol: "puts".to_owned(),
1237 kind: Reference::Call,
1238 addend: -4,
1239 });
1240 text
1241 }
1242
1243 #[test]
1250 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1251 let sections =
1252 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1253 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1254 let file = object::File::parse(&bytes[..]).expect("a readable object");
1255 assert_eq!(lives_in(&file, "f"), ".text.f");
1256 assert_eq!(lives_in(&file, "g"), ".text.g");
1257 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1258 for name in ["f", "g"] {
1261 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1262 assert_eq!(symbol.address(), 0, "{name}");
1263 assert_eq!(symbol.size(), 6, "{name}");
1264 }
1265 let section = file.section_by_name(".text.g").expect("the second function");
1266 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1267 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1270 }
1271
1272 #[test]
1278 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1279 let sections =
1280 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1281 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1282 let file = object::File::parse(&bytes[..]).expect("a readable object");
1283 for name in [".text.f", ".text.g"] {
1284 let section = file.section_by_name(name).expect("a function");
1285 let (offset, _) = section.relocations().next().expect("the call in it");
1286 assert_eq!(offset, 1, "{name}");
1289 assert_eq!(section.relocations().count(), 1, "{name}");
1290 }
1291 }
1292
1293 fn variable(name: &str, place: Place) -> Object {
1295 Object {
1296 name: name.to_owned(),
1297 bytes: if carries_no_bytes(&place) { Vec::new() } else { vec![1, 0, 0, 0] },
1298 size: 4,
1299 align: 4,
1300 place,
1301 binding: Binding::Global,
1302 visibility: Visibility::Default,
1303 relocs: Vec::new(),
1304 }
1305 }
1306
1307 fn holding(object: Object) -> Vec<u8> {
1309 let data = Data { objects: vec![object] };
1310 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1311 }
1312
1313 #[test]
1314 fn what_a_variable_is_decides_which_section_it_goes_in() {
1315 for (place, wanted) in [
1316 (Place::Written, ".data"),
1317 (Place::ReadOnly, ".rodata"),
1318 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1319 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1320 (Place::Zero, ".bss"),
1321 (Place::Thread { zero: false }, ".tdata"),
1322 (Place::Thread { zero: true }, ".tbss"),
1323 (Place::Named(".init_array".to_owned()), ".init_array"),
1324 ] {
1325 let bytes = holding(variable("x", place.clone()));
1326 let file = object::File::parse(&bytes[..]).expect("a readable object");
1327 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1328 assert_eq!(section.size(), 4, "{place:?}");
1329 let carried = section.data().expect("the bytes").len();
1332 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1333 }
1334 }
1335
1336 #[test]
1342 fn a_thread_local_variable_is_a_thread_local_symbol_and_not_only_a_thread_local_section() {
1343 for place in [Place::Thread { zero: false }, Place::Thread { zero: true }] {
1344 let bytes = holding(variable("counter", place.clone()));
1345 let file = object::File::parse(&bytes[..]).expect("a readable object");
1346 let symbol = file
1347 .symbols()
1348 .find(|symbol| symbol.name() == Ok("counter"))
1349 .unwrap_or_else(|| panic!("{place:?}"));
1350 assert_eq!(symbol.kind(), SymbolKind::Tls, "{place:?}");
1351 }
1352 }
1353
1354 #[test]
1360 fn a_section_of_function_addresses_carries_the_type_the_runtime_looks_for() {
1361 for (name, wanted) in [
1362 (".init_array", elf::SHT_INIT_ARRAY),
1363 (".init_array.00101", elf::SHT_INIT_ARRAY),
1364 (".fini_array", elf::SHT_FINI_ARRAY),
1365 (".preinit_array", elf::SHT_PREINIT_ARRAY),
1366 (".init_arrays", elf::SHT_PROGBITS),
1367 ] {
1368 let bytes = holding(variable("x", Place::Named(name.to_owned())));
1369 let file = object::File::parse(&bytes[..]).expect("a readable object");
1370 let section = file.section_by_name(name).unwrap_or_else(|| panic!("{name}"));
1371 let SectionFlags::Elf { sh_type, sh_flags } = section.flags() else {
1372 panic!("{name} is not an elf section");
1373 };
1374 assert_eq!(sh_type, wanted, "{name}");
1375 assert!(sh_flags.contains(elf::SHF_ALLOC | elf::SHF_WRITE), "{name}");
1376 }
1377 }
1378
1379 #[test]
1385 fn two_variables_in_one_named_section_share_it() {
1386 let objects = vec![
1387 variable("x", Place::Named(".init_array".to_owned())),
1388 variable("y", Place::Named(".init_array".to_owned())),
1389 ];
1390 let data = Data { objects };
1391 let bytes =
1392 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1393 let file = object::File::parse(&bytes[..]).expect("a readable object");
1394 let named: Vec<_> =
1395 file.sections().filter(|section| section.name() == Ok(".init_array")).collect();
1396 assert_eq!(named.len(), 1);
1397 assert_eq!(named[0].size(), 8);
1398 }
1399
1400 #[test]
1404 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1405 let sections =
1406 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1407 for (place, wanted) in [
1408 (Place::Written, ".data.x"),
1409 (Place::ReadOnly, ".rodata.x"),
1410 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1411 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1412 (Place::Zero, ".bss.x"),
1413 (Place::Thread { zero: false }, ".tdata.x"),
1414 (Place::Thread { zero: true }, ".tbss.x"),
1415 ] {
1416 let data = Data { objects: vec![variable("x", place.clone())] };
1417 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1418 let file = object::File::parse(&bytes[..]).expect("a readable object");
1419 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1420 let section = file.section_by_name(wanted).expect("the section it named");
1421 assert_eq!(section.size(), 4, "{place:?}");
1422 let carried = section.data().expect("the bytes").len();
1425 assert_eq!(carried, if carries_no_bytes(&place) { 0 } else { 4 }, "{place:?}");
1426 }
1427 }
1428
1429 #[test]
1433 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1434 let sections =
1435 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1436 let named = Place::Named(".init_array".to_owned());
1437 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1438 let bytes =
1439 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1440 let file = object::File::parse(&bytes[..]).expect("a readable object");
1441 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1442 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1443 assert_eq!(lives_in(&file, "n"), ".init_array");
1444 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1445 }
1446
1447 #[test]
1451 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1452 let sections =
1453 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1454 let pointer = Object {
1455 bytes: vec![0; 8],
1456 size: 8,
1457 align: 8,
1458 relocs: vec![Reloc {
1459 at: 0,
1460 symbol: "y".to_owned(),
1461 kind: Reference::Address { bytes: 8 },
1462 addend: 0,
1463 }],
1464 ..variable("p", Place::Written)
1465 };
1466 let objects = vec![variable("first", Place::Written), pointer];
1467 let bytes =
1468 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1469 let file = object::File::parse(&bytes[..]).expect("a readable object");
1470 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1471 let (offset, reloc) = section.relocations().next().expect("one relocation");
1472 assert_eq!(offset, 0);
1475 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1476 }
1477
1478 #[test]
1486 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1487 let place = Place::RelocReadOnly { local: true };
1488 let data =
1489 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
1490 let bytes =
1491 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1492 let file = object::File::parse(&bytes[..]).expect("a readable object");
1493 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1494 assert_eq!(named, 1, "one section holding both, not one each");
1495 }
1496
1497 #[test]
1498 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1499 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1500 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1501 let bytes =
1502 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1503 let file = object::File::parse(&bytes[..]).expect("a readable object");
1504 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1505 assert_eq!(second.kind(), SymbolKind::Data);
1506 assert_eq!(second.size(), 4);
1507 assert_eq!(second.address(), 16);
1511 }
1512
1513 #[test]
1514 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1515 for (binding, global, weak) in [
1516 (Binding::Global, true, false),
1517 (Binding::Local, false, false),
1518 (Binding::Weak, true, true),
1519 ] {
1520 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1521 let file = object::File::parse(&bytes[..]).expect("a readable object");
1522 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1523 assert_eq!(x.is_global(), global, "{binding:?}");
1524 assert_eq!(x.is_weak(), weak, "{binding:?}");
1525 }
1526 }
1527
1528 #[test]
1529 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1530 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1531 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1532 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1533 assert!(x.is_common(), "the linker merges every definition of this name into one");
1534 assert_eq!(x.size(), 4);
1535 assert_eq!(x.address(), 0);
1539 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1540 }
1541
1542 #[test]
1543 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1544 let object = Object {
1545 bytes: vec![0; 8],
1546 size: 8,
1547 align: 8,
1548 relocs: vec![Reloc {
1549 at: 0,
1550 symbol: "y".to_owned(),
1551 kind: Reference::Address { bytes: 8 },
1552 addend: 16,
1553 }],
1554 ..variable("p", Place::Written)
1555 };
1556 let bytes = holding(object);
1557 let file = object::File::parse(&bytes[..]).expect("a readable object");
1558 let section = file.section_by_name(".data").expect("a data section");
1559 let (offset, reloc) = section.relocations().next().expect("one relocation");
1560 assert_eq!(offset, 0);
1561 assert_eq!(reloc.addend(), 16);
1562 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1563 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1564 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1565 }
1566
1567 #[test]
1569 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1570 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1571 data.objects.push(Object {
1572 bytes: vec![0; 16],
1573 size: 16,
1574 align: 8,
1575 relocs: vec![Reloc {
1576 at: 8,
1577 symbol: "y".to_owned(),
1578 kind: Reference::Address { bytes: 8 },
1579 addend: 0,
1580 }],
1581 ..variable("second", Place::Written)
1582 });
1583 let bytes =
1584 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1585 let file = object::File::parse(&bytes[..]).expect("a readable object");
1586 let section = file.section_by_name(".data").expect("a data section");
1587 let (offset, _) = section.relocations().next().expect("one relocation");
1588 assert_eq!(offset, 16);
1591 }
1592
1593 #[test]
1594 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1595 let data = Data {
1596 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1597 };
1598 let aliases = [Alias {
1599 name: "b".to_owned(),
1600 target: "a".to_owned(),
1601 binding: Binding::Global,
1602 visibility: Visibility::Default,
1603 }];
1604 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1605 .expect("an object");
1606 let file = object::File::parse(&bytes[..]).expect("a readable object");
1607 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1608 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1609 assert_eq!(b.address(), a.address(), "the same place");
1610 assert_eq!(b.size(), a.size());
1611 assert_eq!(b.section_index(), a.section_index());
1612 assert!(a.is_local(), "the target was written `static`");
1615 assert!(b.is_global(), "and the name given to it was not");
1616 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1618 }
1619
1620 #[test]
1621 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1622 let text = calling("puts");
1623 let aliases = [Alias {
1624 name: "g".to_owned(),
1625 target: "f".to_owned(),
1626 binding: Binding::Weak,
1627 visibility: Visibility::Default,
1628 }];
1629 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1630 .expect("an object");
1631 let file = object::File::parse(&bytes[..]).expect("a readable object");
1632 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1633 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1634 assert_eq!(g.address(), f.address());
1635 assert_eq!(g.size(), f.size());
1636 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1637 assert!(g.is_weak(), "so that a program may define the name itself instead");
1638 }
1639
1640 #[test]
1643 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1644 let aliases = [Alias {
1645 name: "b".to_owned(),
1646 target: "a".to_owned(),
1647 binding: Binding::Global,
1648 visibility: Visibility::Default,
1649 }];
1650 let error =
1651 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1652 .expect_err("nothing to point at");
1653 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1654 }
1655
1656 #[test]
1657 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1658 let text = calling("puts");
1659 for triple in [
1660 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1661 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1662 ] {
1663 let error =
1664 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1665 .expect_err("no writer");
1666 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1667 }
1668 }
1669
1670 #[test]
1676 fn the_names_a_linker_can_find_are_the_names_the_list_gives() {
1677 let mut text = calling("puts");
1678 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
1679 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
1680 text.bytes.resize(33, 0x90);
1681 let data = Data {
1682 objects: vec![variable("seen", Place::Written), {
1683 let mut quiet = variable("quiet", Place::Zero);
1684 quiet.binding = Binding::Local;
1685 quiet
1686 }],
1687 };
1688 let aliases = [Alias {
1689 name: "second".to_owned(),
1690 target: "f".to_owned(),
1691 binding: Binding::Global,
1692 visibility: Visibility::Default,
1693 }];
1694
1695 let names = defines(&text, &data, &aliases, &target()).expect("a list");
1696 assert_eq!(names, ["f", "shared", "seen", "second"]);
1697
1698 let bytes = write(&text, &data, &aliases, &target(), Output::default()).expect("an object");
1699 let file = object::File::parse(&bytes[..]).expect("a readable object");
1700 let found: Vec<String> = file
1701 .symbols()
1702 .filter(|symbol| symbol.is_global() && symbol.is_definition())
1703 .map(|symbol| symbol.name().unwrap_or_default().to_owned())
1704 .collect();
1705 let mut sorted = names.clone();
1706 sorted.sort();
1707 let mut theirs = found;
1708 theirs.sort();
1709 assert_eq!(sorted, theirs, "the list and the file have to say the same thing");
1710 }
1711
1712 #[test]
1716 fn a_platform_this_does_not_write_has_no_list_of_names_either() {
1717 let text = calling("puts");
1718 for triple in [
1719 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1720 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1721 ] {
1722 let error = defines(&text, &Data::default(), &[], &TargetInfo::new(triple))
1723 .expect_err("no writer");
1724 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1725 }
1726 }
1727}