1use object::write::{
29 Object as Writer, Relocation, StandardSection, Symbol, SymbolId, SymbolSection,
30};
31use object::{
32 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionFlags, SectionKind,
33 SymbolFlags, SymbolKind, SymbolScope, elf,
34};
35use rucc_target::{ObjectFormat, TargetInfo};
36use rucc_tuple::Arch;
37
38use crate::section::{
39 Alias, Binding, Data, Object, Output, Place, Property, Reference, Reloc, Sections, Text,
40 Visibility,
41};
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum Error {
46 Format {
48 triple: String,
50 },
51 Refused {
53 why: String,
55 },
56}
57
58impl std::fmt::Display for Error {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 Error::Format { triple } => {
62 write!(f, "there is no object writer for {triple} in this compiler yet")
63 }
64 Error::Refused { why } => {
65 write!(f, "the object writer refused what it was given: {why}")
66 }
67 }
68 }
69}
70
71impl std::error::Error for Error {}
72
73pub fn write(
82 text: &Text,
83 data: &Data,
84 aliases: &[Alias],
85 target: &TargetInfo,
86 output: Output,
87) -> Result<Vec<u8>, Error> {
88 let Output { sections, property } = output;
89 if target.tuple.arch() != Arch::X86_64 || target.object_format != ObjectFormat::Elf {
90 return Err(Error::Format { triple: target.tuple.to_string() });
91 }
92 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
93 let whole = obj.section_id(StandardSection::Text);
97 if !sections.functions {
98 obj.append_section_data(whole, &text.bytes, u64::from(text.align));
99 }
100
101 let mut symbols = std::collections::BTreeMap::new();
105 let mut split: Vec<(object::write::SectionId, u64)> = Vec::with_capacity(text.funcs.len());
110 let mut ordered: Vec<String> = Vec::new();
113 for func in &text.funcs {
114 let ahead = func.patch.map_or(0, |patch| patch.before);
124 let (section, at) = if sections.functions {
125 let name = format!(".text.{}", func.name).into_bytes();
126 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
127 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
128 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
129 (id, ahead as u64)
130 } else {
131 (whole, func.start as u64)
132 };
133 if let Some(patch) = func.patch {
148 let base = if sections.functions { func.start - ahead } else { 0 };
149 let name = PATCHABLE.as_bytes().to_vec();
150 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
151 obj.section_mut(id).flags = SectionFlags::Elf {
152 sh_type: elf::SHT_PROGBITS,
153 sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER,
154 };
155 obj.append_section_data(id, &[0; 8], 8);
156 let symbol = obj.section_symbol(section);
157 obj.add_relocation(
158 id,
159 Relocation {
160 offset: 0,
161 symbol,
162 addend: (patch.at - base) as i64,
163 flags: RelocationFlags::Elf { r_type: elf::R_X86_64_64 },
164 },
165 )
166 .map_err(|why| Error::Refused { why: why.to_string() })?;
167 ordered.push(if sections.functions {
168 format!(".text.{}", func.name)
169 } else {
170 ".text".to_owned()
171 });
172 }
173 let id = obj.add_symbol(Symbol {
174 name: func.name.clone().into_bytes(),
175 value: at,
176 size: func.len as u64,
177 kind: SymbolKind::Text,
178 scope: scope_of(func.binding),
179 weak: func.binding == Binding::Weak,
180 section: SymbolSection::Section(section),
181 flags: SymbolFlags::None,
182 });
183 see(&mut obj, id, func.binding, func.visibility);
184 symbols.insert(func.name.clone(), id);
185 split.push((section, at));
186 }
187
188 let mut placed = Vec::with_capacity(data.objects.len());
193 let mut local = None;
197 for object in &data.objects {
198 let (section, offset) = put(&mut obj, object, &mut local, sections);
199 let id = obj.add_symbol(Symbol {
200 name: object.name.clone().into_bytes(),
201 value: if object.place == Place::Merged { object.align } else { offset },
204 size: object.size,
205 kind: SymbolKind::Data,
206 scope: scope_of(object.binding),
207 weak: object.binding == Binding::Weak,
208 section,
209 flags: SymbolFlags::None,
210 });
211 see(&mut obj, id, object.binding, object.visibility);
212 symbols.insert(object.name.clone(), id);
213 placed.push((section.id(), offset));
214 }
215
216 for alias in aliases {
222 let Some(&id) = symbols.get(&alias.target) else {
223 let why =
224 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
225 return Err(Error::Refused { why });
226 };
227 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
228 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
229 let id = obj.add_symbol(Symbol {
230 name: alias.name.clone().into_bytes(),
231 value,
232 size,
233 kind,
234 scope: scope_of(alias.binding),
235 weak: alias.binding == Binding::Weak,
236 section,
237 flags: SymbolFlags::None,
238 });
239 see(&mut obj, id, alias.binding, alias.visibility);
240 symbols.insert(alias.name.clone(), id);
241 }
242
243 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
247 for reloc in wanted {
248 if symbols.contains_key(&reloc.symbol) {
249 continue;
250 }
251 let id = obj.add_symbol(Symbol {
252 name: reloc.symbol.clone().into_bytes(),
253 value: 0,
254 size: 0,
255 kind: SymbolKind::Unknown,
259 scope: SymbolScope::Dynamic,
260 weak: false,
261 section: SymbolSection::Undefined,
262 flags: SymbolFlags::None,
263 });
264 symbols.insert(reloc.symbol.clone(), id);
265 }
266
267 for reloc in &text.relocs {
268 let (section, at) = if sections.functions {
273 let after = text.funcs.partition_point(|func| func.start <= reloc.at);
274 let Some(func) = after.checked_sub(1).map(|i| &text.funcs[i]) else {
275 let why = format!("a relocation at {} is in front of every function", reloc.at);
276 return Err(Error::Refused { why });
277 };
278 let base = func.start - func.patch.map_or(0, |patch| patch.before);
281 (split[after - 1].0, (reloc.at - base) as u64)
282 } else {
283 (whole, reloc.at as u64)
284 };
285 add(&mut obj, section, at, reloc, &symbols)?;
286 }
287
288 if !text.unwind.bytes.is_empty() {
294 let frames = obj.add_section(Vec::new(), b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
295 obj.append_section_data(frames, &text.unwind.bytes, 8);
296 for reloc in &text.unwind.relocs {
297 let found = text.funcs.iter().position(|func| func.name == reloc.symbol);
310 let Some((section, at)) = found.map(|i| split[i]) else {
311 let why =
312 format!("'{}' has an unwind record and is not a function here", reloc.symbol);
313 return Err(Error::Refused { why });
314 };
315 let symbol = obj.section_symbol(section);
316 let r_type = r_type(reloc.kind).ok_or_else(|| Error::Refused {
317 why: format!("no relocation is {:?}", reloc.kind),
318 })?;
319 let record = Relocation {
320 offset: reloc.at as u64,
321 symbol,
322 addend: reloc.addend + at as i64,
326 flags: RelocationFlags::Elf { r_type },
327 };
328 obj.add_relocation(frames, record)
329 .map_err(|why| Error::Refused { why: why.to_string() })?;
330 }
331 }
332 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
333 let Some(section) = section else { continue };
334 for reloc in &object.relocs {
335 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols)?;
336 }
337 }
338
339 if property.any() {
343 let note = obj.section_id(StandardSection::GnuProperty);
344 obj.append_section_data(note, &record(property), 8);
345 }
346
347 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
350
351 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
352 link(&mut bytes, &ordered);
353 Ok(bytes)
354}
355
356const PATCHABLE: &str = "__patchable_function_entries";
358
359fn link(bytes: &mut [u8], ordered: &[String]) {
374 if ordered.is_empty() {
375 return;
376 }
377 let word = |bytes: &[u8], at: usize| u64::from_le_bytes(bytes[at..at + 8].try_into().unwrap());
378 let short = |bytes: &[u8], at: usize| u16::from_le_bytes(bytes[at..at + 2].try_into().unwrap());
379 let long = |bytes: &[u8], at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap());
380 let headers = word(bytes, 0x28) as usize;
385 let step = short(bytes, 0x3a) as usize;
386 let count = short(bytes, 0x3c) as usize;
387 let strings = word(bytes, headers + short(bytes, 0x3e) as usize * step + 24) as usize;
388 let name = |bytes: &[u8], header: usize| {
389 let at = strings + long(bytes, header) as usize;
390 let end = bytes[at..].iter().position(|byte| *byte == 0).map_or(at, |len| at + len);
391 String::from_utf8_lossy(&bytes[at..end]).into_owned()
392 };
393 let names: Vec<String> = (0..count).map(|i| name(bytes, headers + i * step)).collect();
394 let mut wanted = ordered.iter();
395 for (i, section) in names.iter().enumerate() {
396 if section != PATCHABLE {
397 continue;
398 }
399 let Some(target) = wanted.next() else { break };
400 let Some(at) = names.iter().position(|name| name == target) else { continue };
401 let at = u32::try_from(at).expect("a file with this many sections in it");
402 let sh_link = headers + i * step + 40;
403 bytes[sh_link..sh_link + 4].copy_from_slice(&at.to_le_bytes());
404 }
405 debug_assert!(wanted.next().is_none(), "a record whose header nothing found");
406}
407
408fn record(property: Property) -> Vec<u8> {
419 let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
422 let desc = [Property::X86_FEATURES, 4, property.features, 0];
423 let mut out = Vec::with_capacity(32);
424 for word in head {
425 out.extend_from_slice(&word.to_le_bytes());
426 }
427 out.extend_from_slice(b"GNU\0");
430 for word in desc {
431 out.extend_from_slice(&word.to_le_bytes());
432 }
433 out
434}
435
436fn put(
443 obj: &mut Writer<'_>,
444 object: &Object,
445 local: &mut Option<object::write::SectionId>,
446 sections: Sections,
447) -> (SymbolSection, u64) {
448 if sections.data {
454 if let Some(name) = object.place.split(&object.name) {
455 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
456 let offset = if object.place == Place::Zero {
457 obj.append_section_bss(section, object.size, object.align)
458 } else {
459 obj.append_section_data(section, &object.bytes, object.align)
460 };
461 return (SymbolSection::Section(section), offset);
462 }
463 }
464 let section = match &object.place {
465 Place::Written => obj.section_id(StandardSection::Data),
466 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
467 Place::RelocReadOnly { local: false } => {
473 obj.section_id(StandardSection::ReadOnlyDataWithRel)
474 }
475 Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
476 obj.add_section(
477 Vec::new(),
478 b".data.rel.ro.local".to_vec(),
479 SectionKind::ReadOnlyDataWithRel,
480 )
481 }),
482 Place::Zero => obj.section_id(StandardSection::UninitializedData),
483 Place::Merged => return (SymbolSection::Common, 0),
484 Place::Named(name) => {
488 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
489 }
490 };
491 let offset = if object.place == Place::Zero {
492 obj.append_section_bss(section, object.size, object.align)
493 } else {
494 obj.append_section_data(section, &object.bytes, object.align)
495 };
496 (SymbolSection::Section(section), offset)
497}
498
499fn kind_of(place: &Place) -> SectionKind {
508 match place {
509 Place::ReadOnly => SectionKind::ReadOnlyData,
510 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
511 Place::Zero => SectionKind::UninitializedData,
512 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
513 }
514}
515
516fn add(
523 obj: &mut Writer<'_>,
524 section: object::write::SectionId,
525 at: u64,
526 reloc: &Reloc,
527 symbols: &std::collections::BTreeMap<String, SymbolId>,
528) -> Result<(), Error> {
529 let r_type = r_type(reloc.kind)
530 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
531 obj.add_relocation(
532 section,
533 Relocation {
534 offset: at,
535 symbol: symbols[&reloc.symbol],
536 addend: reloc.addend,
537 flags: RelocationFlags::Elf { r_type },
538 },
539 )
540 .map_err(|why| Error::Refused { why: why.to_string() })
541}
542
543fn scope_of(binding: Binding) -> SymbolScope {
556 match binding {
557 Binding::Local => SymbolScope::Compilation,
558 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
559 }
560}
561
562fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
574 if binding == Binding::Local {
575 return;
576 }
577 let wanted = match visibility {
578 Visibility::Default => elf::STV_DEFAULT,
579 Visibility::Hidden => elf::STV_HIDDEN,
580 Visibility::Protected => elf::STV_PROTECTED,
581 };
582 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
583 *st_other = st_other.with_visibility(wanted);
584 }
585}
586
587fn r_type(reference: Reference) -> Option<elf::RelocationType> {
598 Some(match reference {
599 Reference::Call => elf::R_X86_64_PLT32,
600 Reference::Data => elf::R_X86_64_PC32,
601 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
602 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
603 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
604 Reference::Address { .. } => return None,
605 })
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611
612 use object::read::elf::Sym as _;
613 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
614 use rucc_target::{Arch, Env, Os, Triple};
615
616 use crate::section::{Extent, Patch, Reloc};
617
618 fn target() -> TargetInfo {
620 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
621 }
622
623 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
628 Extent {
629 name,
630 start,
631 len,
632 align: crate::FUNC_ALIGN,
633 binding,
634 visibility: Visibility::Default,
635 patch: None,
636 }
637 }
638
639 fn calling(name: &str) -> Text {
641 Text {
642 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
643 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
644 relocs: vec![Reloc {
645 at: 1,
646 symbol: name.to_owned(),
647 kind: Reference::Call,
648 addend: -4,
649 }],
650 ..Text::default()
651 }
652 }
653
654 #[test]
655 fn the_bytes_come_back_out_of_the_section_they_went_into() {
656 let text = calling("puts");
657 let bytes =
658 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
659 let file = object::File::parse(&bytes[..]).expect("a readable object");
660 let section = file.section_by_name(".text").expect("a text section");
661 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
662 }
663
664 #[test]
665 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
666 let mut text = calling("puts");
667 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
668 text.bytes.resize(17, 0x90);
669 let bytes =
670 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
671 let file = object::File::parse(&bytes[..]).expect("a readable object");
672 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
673 assert_eq!(g.address(), 16);
674 assert_eq!(g.size(), 1);
675 assert_eq!(g.kind(), SymbolKind::Text);
676 assert!(g.is_global(), "nothing said otherwise about this one");
677 }
678
679 #[test]
680 fn a_function_no_other_file_can_see_is_a_local_symbol() {
681 let mut text = calling("puts");
682 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
683 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
684 text.bytes.resize(33, 0x90);
685 let bytes =
686 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
687 let file = object::File::parse(&bytes[..]).expect("a readable object");
688 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
689 assert!(hidden.is_local(), "a static function must not be offered to the linker");
692 assert!(!hidden.is_weak());
693 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
694 assert!(shared.is_weak(), "a weak function has to be able to lose");
695 assert!(shared.is_global());
696 }
697
698 #[test]
715 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
716 let mut text = calling("puts");
717 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
718 text.funcs[0].start = 3;
719 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
720 text.relocs[0].at = 4;
721 let bytes =
722 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
723 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
724 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
725 assert_eq!(section.size(), 8, "one address, and this file defines one function");
726 assert_eq!(section.align(), 8);
727 let header = section.elf_section_header();
728 assert_eq!(
729 header.sh_flags.get(Endianness::Little),
730 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
731 );
732 let index = file.section_by_name(".text").expect("a text section").index().0;
735 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
736 assert_ne!(index, 0);
737
738 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
740 panic!("one address in the record")
741 };
742 assert_eq!(*at, 0);
743 assert_eq!(reloc.addend(), 0);
744 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
745 }
746
747 #[test]
749 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
750 let text = calling("puts");
751 let bytes =
752 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
753 let file = object::File::parse(&bytes[..]).expect("a readable object");
754 assert!(file.section_by_name(PATCHABLE).is_none());
755 }
756
757 #[test]
763 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
764 let mut text = calling("puts");
765 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
766 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
767 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
768 text.bytes.resize(17, 0x90);
769 let output =
770 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
771 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
772 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
773 let links: Vec<usize> = file
774 .sections()
775 .filter(|section| section.name() == Ok(PATCHABLE))
776 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
777 .collect();
778 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
779 assert_eq!(links, [index(".text.f"), index(".text.g")]);
780 }
781
782 #[test]
783 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
784 let mut text = calling("puts");
785 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
786 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
787 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
788 text.bytes.resize(49, 0x90);
789 let bytes =
790 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
791 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
792 let visibility = |name: &str| {
793 file.symbols()
794 .find(|s| s.name() == Ok(name))
795 .expect("the function")
796 .elf_symbol()
797 .st_visibility()
798 };
799 assert_eq!(visibility("g"), elf::STV_DEFAULT);
801 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
802 assert_eq!(visibility("s"), elf::STV_DEFAULT);
805 }
806
807 #[test]
817 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
818 let mut text = calling("puts");
819 for (index, (name, seen)) in
820 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
821 {
822 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
823 func.visibility = seen;
824 text.funcs.push(func);
825 }
826 text.bytes.resize(49, 0x90);
827 let mut data = Data::default();
828 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
829 let mut object = variable(name, Place::Written);
830 object.visibility = seen;
831 data.objects.push(object);
832 }
833 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
834 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
835 let visibility = |name: &str| {
836 file.symbols()
837 .find(|s| s.name() == Ok(name))
838 .expect("the symbol")
839 .elf_symbol()
840 .st_visibility()
841 };
842 assert_eq!(visibility("h"), elf::STV_HIDDEN);
843 assert_eq!(visibility("p"), elf::STV_PROTECTED);
844 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
845 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
846 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
849 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
850 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
851 }
852
853 #[test]
854 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
855 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
856 .expect("an object");
857 let file = object::File::parse(&bytes[..]).expect("a readable object");
858 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
859 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
860 }
861
862 #[test]
863 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
864 for (reference, wanted) in [
865 (Reference::Call, elf::R_X86_64_PLT32),
866 (Reference::Data, elf::R_X86_64_PC32),
867 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
868 ] {
869 let mut text = calling("puts");
870 text.relocs[0].kind = reference;
871 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
872 .expect("an object");
873 let file = object::File::parse(&bytes[..]).expect("a readable object");
874 let section = file.section_by_name(".text").expect("a text section");
875 let (offset, reloc) = section.relocations().next().expect("one relocation");
876 assert_eq!(offset, 1);
877 assert_eq!(reloc.addend(), -4);
878 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
879 }
880 }
881
882 #[test]
883 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
884 let mut text = calling("puts");
885 text.relocs.push(Reloc {
886 at: 1,
887 symbol: "puts".to_owned(),
888 kind: Reference::Call,
889 addend: -4,
890 });
891 let bytes =
892 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
893 let file = object::File::parse(&bytes[..]).expect("a readable object");
894 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
895 }
896
897 #[test]
898 fn a_function_that_is_also_called_is_not_a_second_symbol() {
899 let text = calling("f");
900 let bytes =
901 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
902 let file = object::File::parse(&bytes[..]).expect("a readable object");
903 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
904 let f = found.next().expect("the function");
905 assert!(!f.is_undefined(), "the file defines it");
906 assert!(found.next().is_none(), "and defines it once");
907 }
908
909 #[test]
910 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
911 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
912 .expect("an object");
913 let file = object::File::parse(&bytes[..]).expect("a readable object");
914 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
915 assert!(note.data().expect("no bytes").is_empty());
916 }
917
918 #[test]
925 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
926 let property = Property { features: Property::IBT | Property::SHSTK };
927 let output = Output { property, ..Output::default() };
928 let bytes =
929 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
930 let file = object::File::parse(&bytes[..]).expect("a readable object");
931 let note = file.section_by_name(".note.gnu.property").expect("the note");
932 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
933 let want: Vec<u8> = [
934 4u32,
935 16,
936 5,
937 u32::from_le_bytes(*b"GNU\0"),
938 Property::X86_FEATURES,
939 4,
940 Property::IBT | Property::SHSTK,
941 0,
942 ]
943 .iter()
944 .flat_map(|word| word.to_le_bytes())
945 .collect();
946 assert_eq!(note.data().expect("the bytes"), &want[..]);
947 }
948
949 #[test]
955 fn a_file_built_to_have_nothing_checked_says_nothing() {
956 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
957 .expect("an object");
958 let file = object::File::parse(&bytes[..]).expect("a readable object");
959 assert!(file.section_by_name(".note.gnu.property").is_none());
960 }
961
962 #[test]
971 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
972 let mut text = calling("puts");
973 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
974 text.bytes.resize(17, 0x90);
975 text.unwind.bytes = vec![0; 64];
978 for (at, name) in [(32usize, "f"), (48usize, "g")] {
979 text.unwind.relocs.push(Reloc {
980 at,
981 symbol: name.to_owned(),
982 kind: Reference::Address { bytes: 8 },
983 addend: 0,
984 });
985 }
986 let bytes =
987 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
988 let file = object::File::parse(&bytes[..]).expect("a readable object");
989 let mut found = points_at(&file);
990 found.sort_unstable();
991 assert_eq!(found, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
992 }
993
994 fn points_at(file: &object::File<'_>) -> Vec<(u64, String, i64)> {
997 let frames = file.section_by_name(".eh_frame").expect("the table");
998 frames
999 .relocations()
1000 .map(|(offset, reloc)| {
1001 let object::RelocationTarget::Symbol(index) = reloc.target() else {
1002 panic!("a record points at something that is not a symbol");
1003 };
1004 let symbol = file.symbol_by_index(index).expect("a symbol that is in the table");
1005 assert_eq!(symbol.kind(), SymbolKind::Section, "a record names a section");
1006 let section = symbol.section_index().expect("a section symbol is in one");
1007 let name = file.section_by_index(section).expect("a readable section");
1008 (offset, name.name().expect("a named section").to_owned(), reloc.addend())
1009 })
1010 .collect()
1011 }
1012
1013 #[test]
1026 fn a_record_reaches_its_function_through_the_section_it_is_in() {
1027 let mut text = two();
1028 text.unwind.bytes = vec![0; 64];
1029 for (at, name) in [(32usize, "f"), (48usize, "g")] {
1030 text.unwind.relocs.push(Reloc {
1031 at,
1032 symbol: name.to_owned(),
1033 kind: Reference::Data,
1034 addend: 0,
1035 });
1036 }
1037 let bytes =
1038 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
1039 let file = object::File::parse(&bytes[..]).expect("a readable object");
1040 let mut whole = points_at(&file);
1041 whole.sort_unstable();
1042 assert_eq!(whole, [(32, ".text".to_owned(), 0), (48, ".text".to_owned(), 16)]);
1043
1044 let sections =
1045 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1046 let bytes = write(&text, &Data::default(), &[], &target(), sections).expect("an object");
1047 let file = object::File::parse(&bytes[..]).expect("a readable object");
1048 let mut split = points_at(&file);
1049 split.sort_unstable();
1050 assert_eq!(split, [(32, ".text.f".to_owned(), 0), (48, ".text.g".to_owned(), 0)]);
1051 }
1052
1053 #[test]
1060 fn a_record_about_something_this_file_does_not_define_is_refused() {
1061 let mut text = calling("puts");
1062 text.unwind.bytes = vec![0; 64];
1063 text.unwind.relocs.push(Reloc {
1064 at: 32,
1065 symbol: "puts".to_owned(),
1066 kind: Reference::Data,
1067 addend: 0,
1068 });
1069 let why = write(&text, &Data::default(), &[], &target(), Output::default())
1070 .expect_err("a record about a name from somewhere else");
1071 assert!(why.to_string().contains("puts"), "{why}");
1072 }
1073
1074 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
1076 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
1077 let index = symbol.section_index().expect("a section to be defined in");
1078 let section = file.section_by_index(index).expect("a readable section");
1079 section.name().expect("a named section").to_owned()
1080 }
1081
1082 fn two() -> Text {
1084 let mut text = calling("puts");
1085 text.bytes.resize(16, 0x90);
1088 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
1089 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
1090 text.relocs.push(Reloc {
1091 at: 17,
1092 symbol: "puts".to_owned(),
1093 kind: Reference::Call,
1094 addend: -4,
1095 });
1096 text
1097 }
1098
1099 #[test]
1106 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1107 let sections =
1108 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1109 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1110 let file = object::File::parse(&bytes[..]).expect("a readable object");
1111 assert_eq!(lives_in(&file, "f"), ".text.f");
1112 assert_eq!(lives_in(&file, "g"), ".text.g");
1113 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1114 for name in ["f", "g"] {
1117 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1118 assert_eq!(symbol.address(), 0, "{name}");
1119 assert_eq!(symbol.size(), 6, "{name}");
1120 }
1121 let section = file.section_by_name(".text.g").expect("the second function");
1122 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1123 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1126 }
1127
1128 #[test]
1134 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1135 let sections =
1136 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1137 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1138 let file = object::File::parse(&bytes[..]).expect("a readable object");
1139 for name in [".text.f", ".text.g"] {
1140 let section = file.section_by_name(name).expect("a function");
1141 let (offset, _) = section.relocations().next().expect("the call in it");
1142 assert_eq!(offset, 1, "{name}");
1145 assert_eq!(section.relocations().count(), 1, "{name}");
1146 }
1147 }
1148
1149 fn variable(name: &str, place: Place) -> Object {
1151 Object {
1152 name: name.to_owned(),
1153 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
1154 size: 4,
1155 align: 4,
1156 place,
1157 binding: Binding::Global,
1158 visibility: Visibility::Default,
1159 relocs: Vec::new(),
1160 }
1161 }
1162
1163 fn holding(object: Object) -> Vec<u8> {
1165 let data = Data { objects: vec![object] };
1166 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1167 }
1168
1169 #[test]
1170 fn what_a_variable_is_decides_which_section_it_goes_in() {
1171 for (place, wanted) in [
1172 (Place::Written, ".data"),
1173 (Place::ReadOnly, ".rodata"),
1174 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1175 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1176 (Place::Zero, ".bss"),
1177 (Place::Named(".init_array".to_owned()), ".init_array"),
1178 ] {
1179 let bytes = holding(variable("x", place.clone()));
1180 let file = object::File::parse(&bytes[..]).expect("a readable object");
1181 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1182 assert_eq!(section.size(), 4, "{place:?}");
1183 let carried = section.data().expect("the bytes").len();
1186 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
1187 }
1188 }
1189
1190 #[test]
1194 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1195 let sections =
1196 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1197 for (place, wanted) in [
1198 (Place::Written, ".data.x"),
1199 (Place::ReadOnly, ".rodata.x"),
1200 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1201 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1202 (Place::Zero, ".bss.x"),
1203 ] {
1204 let data = Data { objects: vec![variable("x", place.clone())] };
1205 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1206 let file = object::File::parse(&bytes[..]).expect("a readable object");
1207 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1208 let section = file.section_by_name(wanted).expect("the section it named");
1209 assert_eq!(section.size(), 4, "{place:?}");
1210 let carried = section.data().expect("the bytes").len();
1213 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
1214 }
1215 }
1216
1217 #[test]
1221 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1222 let sections =
1223 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1224 let named = Place::Named(".init_array".to_owned());
1225 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1226 let bytes =
1227 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1228 let file = object::File::parse(&bytes[..]).expect("a readable object");
1229 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1230 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1231 assert_eq!(lives_in(&file, "n"), ".init_array");
1232 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1233 }
1234
1235 #[test]
1239 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1240 let sections =
1241 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1242 let pointer = Object {
1243 bytes: vec![0; 8],
1244 size: 8,
1245 align: 8,
1246 relocs: vec![Reloc {
1247 at: 0,
1248 symbol: "y".to_owned(),
1249 kind: Reference::Address { bytes: 8 },
1250 addend: 0,
1251 }],
1252 ..variable("p", Place::Written)
1253 };
1254 let objects = vec![variable("first", Place::Written), pointer];
1255 let bytes =
1256 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1257 let file = object::File::parse(&bytes[..]).expect("a readable object");
1258 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1259 let (offset, reloc) = section.relocations().next().expect("one relocation");
1260 assert_eq!(offset, 0);
1263 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1264 }
1265
1266 #[test]
1274 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1275 let place = Place::RelocReadOnly { local: true };
1276 let data =
1277 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
1278 let bytes =
1279 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1280 let file = object::File::parse(&bytes[..]).expect("a readable object");
1281 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1282 assert_eq!(named, 1, "one section holding both, not one each");
1283 }
1284
1285 #[test]
1286 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1287 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1288 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1289 let bytes =
1290 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1291 let file = object::File::parse(&bytes[..]).expect("a readable object");
1292 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1293 assert_eq!(second.kind(), SymbolKind::Data);
1294 assert_eq!(second.size(), 4);
1295 assert_eq!(second.address(), 16);
1299 }
1300
1301 #[test]
1302 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1303 for (binding, global, weak) in [
1304 (Binding::Global, true, false),
1305 (Binding::Local, false, false),
1306 (Binding::Weak, true, true),
1307 ] {
1308 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1309 let file = object::File::parse(&bytes[..]).expect("a readable object");
1310 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1311 assert_eq!(x.is_global(), global, "{binding:?}");
1312 assert_eq!(x.is_weak(), weak, "{binding:?}");
1313 }
1314 }
1315
1316 #[test]
1317 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1318 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1319 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1320 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1321 assert!(x.is_common(), "the linker merges every definition of this name into one");
1322 assert_eq!(x.size(), 4);
1323 assert_eq!(x.address(), 0);
1327 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1328 }
1329
1330 #[test]
1331 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1332 let object = Object {
1333 bytes: vec![0; 8],
1334 size: 8,
1335 align: 8,
1336 relocs: vec![Reloc {
1337 at: 0,
1338 symbol: "y".to_owned(),
1339 kind: Reference::Address { bytes: 8 },
1340 addend: 16,
1341 }],
1342 ..variable("p", Place::Written)
1343 };
1344 let bytes = holding(object);
1345 let file = object::File::parse(&bytes[..]).expect("a readable object");
1346 let section = file.section_by_name(".data").expect("a data section");
1347 let (offset, reloc) = section.relocations().next().expect("one relocation");
1348 assert_eq!(offset, 0);
1349 assert_eq!(reloc.addend(), 16);
1350 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1351 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1352 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1353 }
1354
1355 #[test]
1357 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1358 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1359 data.objects.push(Object {
1360 bytes: vec![0; 16],
1361 size: 16,
1362 align: 8,
1363 relocs: vec![Reloc {
1364 at: 8,
1365 symbol: "y".to_owned(),
1366 kind: Reference::Address { bytes: 8 },
1367 addend: 0,
1368 }],
1369 ..variable("second", Place::Written)
1370 });
1371 let bytes =
1372 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1373 let file = object::File::parse(&bytes[..]).expect("a readable object");
1374 let section = file.section_by_name(".data").expect("a data section");
1375 let (offset, _) = section.relocations().next().expect("one relocation");
1376 assert_eq!(offset, 16);
1379 }
1380
1381 #[test]
1382 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1383 let data = Data {
1384 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1385 };
1386 let aliases = [Alias {
1387 name: "b".to_owned(),
1388 target: "a".to_owned(),
1389 binding: Binding::Global,
1390 visibility: Visibility::Default,
1391 }];
1392 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1393 .expect("an object");
1394 let file = object::File::parse(&bytes[..]).expect("a readable object");
1395 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1396 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1397 assert_eq!(b.address(), a.address(), "the same place");
1398 assert_eq!(b.size(), a.size());
1399 assert_eq!(b.section_index(), a.section_index());
1400 assert!(a.is_local(), "the target was written `static`");
1403 assert!(b.is_global(), "and the name given to it was not");
1404 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1406 }
1407
1408 #[test]
1409 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1410 let text = calling("puts");
1411 let aliases = [Alias {
1412 name: "g".to_owned(),
1413 target: "f".to_owned(),
1414 binding: Binding::Weak,
1415 visibility: Visibility::Default,
1416 }];
1417 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1418 .expect("an object");
1419 let file = object::File::parse(&bytes[..]).expect("a readable object");
1420 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1421 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1422 assert_eq!(g.address(), f.address());
1423 assert_eq!(g.size(), f.size());
1424 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1425 assert!(g.is_weak(), "so that a program may define the name itself instead");
1426 }
1427
1428 #[test]
1431 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1432 let aliases = [Alias {
1433 name: "b".to_owned(),
1434 target: "a".to_owned(),
1435 binding: Binding::Global,
1436 visibility: Visibility::Default,
1437 }];
1438 let error =
1439 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1440 .expect_err("nothing to point at");
1441 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1442 }
1443
1444 #[test]
1445 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1446 let text = calling("puts");
1447 for triple in [
1448 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1449 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1450 ] {
1451 let error =
1452 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1453 .expect_err("no writer");
1454 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1455 }
1456 }
1457}