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::with_capacity(text.funcs.len());
109 let mut ordered: Vec<String> = Vec::new();
112 for func in &text.funcs {
113 let ahead = func.patch.map_or(0, |patch| patch.before);
123 let (section, at) = if sections.functions {
124 let name = format!(".text.{}", func.name).into_bytes();
125 let id = obj.add_section(Vec::new(), name, SectionKind::Text);
126 let bytes = &text.bytes[func.start - ahead..func.start + func.len];
127 obj.append_section_data(id, bytes, u64::from(func.align.max(1)));
128 (id, ahead as u64)
129 } else {
130 (whole, func.start as u64)
131 };
132 if let Some(patch) = func.patch {
147 let base = if sections.functions { func.start - ahead } else { 0 };
148 let name = PATCHABLE.as_bytes().to_vec();
149 let id = obj.add_section(Vec::new(), name, SectionKind::Data);
150 obj.section_mut(id).flags = SectionFlags::Elf {
151 sh_type: elf::SHT_PROGBITS,
152 sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER,
153 };
154 obj.append_section_data(id, &[0; 8], 8);
155 let symbol = obj.section_symbol(section);
156 obj.add_relocation(
157 id,
158 Relocation {
159 offset: 0,
160 symbol,
161 addend: (patch.at - base) as i64,
162 flags: RelocationFlags::Elf { r_type: elf::R_X86_64_64 },
163 },
164 )
165 .map_err(|why| Error::Refused { why: why.to_string() })?;
166 ordered.push(if sections.functions {
167 format!(".text.{}", func.name)
168 } else {
169 ".text".to_owned()
170 });
171 }
172 let id = obj.add_symbol(Symbol {
173 name: func.name.clone().into_bytes(),
174 value: at,
175 size: func.len as u64,
176 kind: SymbolKind::Text,
177 scope: scope_of(func.binding),
178 weak: func.binding == Binding::Weak,
179 section: SymbolSection::Section(section),
180 flags: SymbolFlags::None,
181 });
182 see(&mut obj, id, func.binding, func.visibility);
183 symbols.insert(func.name.clone(), id);
184 split.push(section);
185 }
186
187 let mut placed = Vec::with_capacity(data.objects.len());
192 let mut local = None;
196 for object in &data.objects {
197 let (section, offset) = put(&mut obj, object, &mut local, sections);
198 let id = obj.add_symbol(Symbol {
199 name: object.name.clone().into_bytes(),
200 value: if object.place == Place::Merged { object.align } else { offset },
203 size: object.size,
204 kind: SymbolKind::Data,
205 scope: scope_of(object.binding),
206 weak: object.binding == Binding::Weak,
207 section,
208 flags: SymbolFlags::None,
209 });
210 see(&mut obj, id, object.binding, object.visibility);
211 symbols.insert(object.name.clone(), id);
212 placed.push((section.id(), offset));
213 }
214
215 for alias in aliases {
221 let Some(&id) = symbols.get(&alias.target) else {
222 let why =
223 format!("'{}' is aliased to '{}', which is not here", alias.name, alias.target);
224 return Err(Error::Refused { why });
225 };
226 let (value, size) = (obj.symbol(id).value, obj.symbol(id).size);
227 let (kind, section) = (obj.symbol(id).kind, obj.symbol(id).section);
228 let id = obj.add_symbol(Symbol {
229 name: alias.name.clone().into_bytes(),
230 value,
231 size,
232 kind,
233 scope: scope_of(alias.binding),
234 weak: alias.binding == Binding::Weak,
235 section,
236 flags: SymbolFlags::None,
237 });
238 see(&mut obj, id, alias.binding, alias.visibility);
239 symbols.insert(alias.name.clone(), id);
240 }
241
242 let wanted = text
243 .relocs
244 .iter()
245 .chain(text.unwind.relocs.iter())
246 .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], (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 add(&mut obj, frames, reloc.at as u64, reloc, &symbols)?;
298 }
299 }
300 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
301 let Some(section) = section else { continue };
302 for reloc in &object.relocs {
303 add(&mut obj, section, offset + reloc.at as u64, reloc, &symbols)?;
304 }
305 }
306
307 if property.any() {
311 let note = obj.section_id(StandardSection::GnuProperty);
312 obj.append_section_data(note, &record(property), 8);
313 }
314
315 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
318
319 let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
320 link(&mut bytes, &ordered);
321 Ok(bytes)
322}
323
324const PATCHABLE: &str = "__patchable_function_entries";
326
327fn link(bytes: &mut [u8], ordered: &[String]) {
342 if ordered.is_empty() {
343 return;
344 }
345 let word = |bytes: &[u8], at: usize| u64::from_le_bytes(bytes[at..at + 8].try_into().unwrap());
346 let short = |bytes: &[u8], at: usize| u16::from_le_bytes(bytes[at..at + 2].try_into().unwrap());
347 let long = |bytes: &[u8], at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap());
348 let headers = word(bytes, 0x28) as usize;
353 let step = short(bytes, 0x3a) as usize;
354 let count = short(bytes, 0x3c) as usize;
355 let strings = word(bytes, headers + short(bytes, 0x3e) as usize * step + 24) as usize;
356 let name = |bytes: &[u8], header: usize| {
357 let at = strings + long(bytes, header) as usize;
358 let end = bytes[at..].iter().position(|byte| *byte == 0).map_or(at, |len| at + len);
359 String::from_utf8_lossy(&bytes[at..end]).into_owned()
360 };
361 let names: Vec<String> = (0..count).map(|i| name(bytes, headers + i * step)).collect();
362 let mut wanted = ordered.iter();
363 for (i, section) in names.iter().enumerate() {
364 if section != PATCHABLE {
365 continue;
366 }
367 let Some(target) = wanted.next() else { break };
368 let Some(at) = names.iter().position(|name| name == target) else { continue };
369 let at = u32::try_from(at).expect("a file with this many sections in it");
370 let sh_link = headers + i * step + 40;
371 bytes[sh_link..sh_link + 4].copy_from_slice(&at.to_le_bytes());
372 }
373 debug_assert!(wanted.next().is_none(), "a record whose header nothing found");
374}
375
376fn record(property: Property) -> Vec<u8> {
387 let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
390 let desc = [Property::X86_FEATURES, 4, property.features, 0];
391 let mut out = Vec::with_capacity(32);
392 for word in head {
393 out.extend_from_slice(&word.to_le_bytes());
394 }
395 out.extend_from_slice(b"GNU\0");
398 for word in desc {
399 out.extend_from_slice(&word.to_le_bytes());
400 }
401 out
402}
403
404fn put(
411 obj: &mut Writer<'_>,
412 object: &Object,
413 local: &mut Option<object::write::SectionId>,
414 sections: Sections,
415) -> (SymbolSection, u64) {
416 if sections.data {
422 if let Some(name) = object.place.split(&object.name) {
423 let section = obj.add_section(Vec::new(), name.into_bytes(), kind_of(&object.place));
424 let offset = if object.place == Place::Zero {
425 obj.append_section_bss(section, object.size, object.align)
426 } else {
427 obj.append_section_data(section, &object.bytes, object.align)
428 };
429 return (SymbolSection::Section(section), offset);
430 }
431 }
432 let section = match &object.place {
433 Place::Written => obj.section_id(StandardSection::Data),
434 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
435 Place::RelocReadOnly { local: false } => {
441 obj.section_id(StandardSection::ReadOnlyDataWithRel)
442 }
443 Place::RelocReadOnly { local: true } => *local.get_or_insert_with(|| {
444 obj.add_section(
445 Vec::new(),
446 b".data.rel.ro.local".to_vec(),
447 SectionKind::ReadOnlyDataWithRel,
448 )
449 }),
450 Place::Zero => obj.section_id(StandardSection::UninitializedData),
451 Place::Merged => return (SymbolSection::Common, 0),
452 Place::Named(name) => {
456 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
457 }
458 };
459 let offset = if object.place == Place::Zero {
460 obj.append_section_bss(section, object.size, object.align)
461 } else {
462 obj.append_section_data(section, &object.bytes, object.align)
463 };
464 (SymbolSection::Section(section), offset)
465}
466
467fn kind_of(place: &Place) -> SectionKind {
476 match place {
477 Place::ReadOnly => SectionKind::ReadOnlyData,
478 Place::RelocReadOnly { .. } => SectionKind::ReadOnlyDataWithRel,
479 Place::Zero => SectionKind::UninitializedData,
480 Place::Written | Place::Merged | Place::Named(_) => SectionKind::Data,
481 }
482}
483
484fn add(
491 obj: &mut Writer<'_>,
492 section: object::write::SectionId,
493 at: u64,
494 reloc: &Reloc,
495 symbols: &std::collections::BTreeMap<String, SymbolId>,
496) -> Result<(), Error> {
497 let r_type = r_type(reloc.kind)
498 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
499 obj.add_relocation(
500 section,
501 Relocation {
502 offset: at,
503 symbol: symbols[&reloc.symbol],
504 addend: reloc.addend,
505 flags: RelocationFlags::Elf { r_type },
506 },
507 )
508 .map_err(|why| Error::Refused { why: why.to_string() })
509}
510
511fn scope_of(binding: Binding) -> SymbolScope {
524 match binding {
525 Binding::Local => SymbolScope::Compilation,
526 Binding::Global | Binding::Weak => SymbolScope::Dynamic,
527 }
528}
529
530fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
542 if binding == Binding::Local {
543 return;
544 }
545 let wanted = match visibility {
546 Visibility::Default => elf::STV_DEFAULT,
547 Visibility::Hidden => elf::STV_HIDDEN,
548 Visibility::Protected => elf::STV_PROTECTED,
549 };
550 if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
551 *st_other = st_other.with_visibility(wanted);
552 }
553}
554
555fn r_type(reference: Reference) -> Option<elf::RelocationType> {
566 Some(match reference {
567 Reference::Call => elf::R_X86_64_PLT32,
568 Reference::Data => elf::R_X86_64_PC32,
569 Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
570 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
571 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
572 Reference::Address { .. } => return None,
573 })
574}
575
576#[cfg(test)]
577mod tests {
578 use super::*;
579
580 use object::read::elf::Sym as _;
581 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
582 use rucc_target::{Arch, Env, Os, Triple};
583
584 use crate::section::{Extent, Patch, Reloc};
585
586 fn target() -> TargetInfo {
588 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
589 }
590
591 fn extent(name: String, start: usize, len: usize, binding: Binding) -> Extent {
596 Extent {
597 name,
598 start,
599 len,
600 align: crate::FUNC_ALIGN,
601 binding,
602 visibility: Visibility::Default,
603 patch: None,
604 }
605 }
606
607 fn calling(name: &str) -> Text {
609 Text {
610 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
611 funcs: vec![extent("f".to_owned(), 0, 6, Binding::Global)],
612 relocs: vec![Reloc {
613 at: 1,
614 symbol: name.to_owned(),
615 kind: Reference::Call,
616 addend: -4,
617 }],
618 ..Text::default()
619 }
620 }
621
622 #[test]
623 fn the_bytes_come_back_out_of_the_section_they_went_into() {
624 let text = calling("puts");
625 let bytes =
626 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
627 let file = object::File::parse(&bytes[..]).expect("a readable object");
628 let section = file.section_by_name(".text").expect("a text section");
629 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
630 }
631
632 #[test]
633 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
634 let mut text = calling("puts");
635 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
636 text.bytes.resize(17, 0x90);
637 let bytes =
638 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
639 let file = object::File::parse(&bytes[..]).expect("a readable object");
640 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
641 assert_eq!(g.address(), 16);
642 assert_eq!(g.size(), 1);
643 assert_eq!(g.kind(), SymbolKind::Text);
644 assert!(g.is_global(), "nothing said otherwise about this one");
645 }
646
647 #[test]
648 fn a_function_no_other_file_can_see_is_a_local_symbol() {
649 let mut text = calling("puts");
650 text.funcs.push(extent("hidden".to_owned(), 16, 1, Binding::Local));
651 text.funcs.push(extent("shared".to_owned(), 32, 1, Binding::Weak));
652 text.bytes.resize(33, 0x90);
653 let bytes =
654 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
655 let file = object::File::parse(&bytes[..]).expect("a readable object");
656 let hidden = file.symbols().find(|s| s.name() == Ok("hidden")).expect("the static one");
657 assert!(hidden.is_local(), "a static function must not be offered to the linker");
660 assert!(!hidden.is_weak());
661 let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the weak one");
662 assert!(shared.is_weak(), "a weak function has to be able to lose");
663 assert!(shared.is_global());
664 }
665
666 #[test]
683 fn where_a_patcher_may_write_is_recorded_in_a_section_tied_to_the_code_it_is_about() {
684 let mut text = calling("puts");
685 text.bytes.splice(0..0, [0x90, 0x90, 0x90]);
686 text.funcs[0].start = 3;
687 text.funcs[0].patch = Some(Patch { at: 0, before: 3 });
688 text.relocs[0].at = 4;
689 let bytes =
690 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
691 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
692 let section = file.section_by_name(PATCHABLE).expect("a record of the room");
693 assert_eq!(section.size(), 8, "one address, and this file defines one function");
694 assert_eq!(section.align(), 8);
695 let header = section.elf_section_header();
696 assert_eq!(
697 header.sh_flags.get(Endianness::Little),
698 elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER
699 );
700 let index = file.section_by_name(".text").expect("a text section").index().0;
703 assert_eq!(header.sh_link.get(Endianness::Little) as usize, index);
704 assert_ne!(index, 0);
705
706 let [(at, reloc)] = §ion.relocations().collect::<Vec<_>>()[..] else {
708 panic!("one address in the record")
709 };
710 assert_eq!(*at, 0);
711 assert_eq!(reloc.addend(), 0);
712 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
713 }
714
715 #[test]
717 fn a_file_that_promised_a_patcher_nothing_records_nothing() {
718 let text = calling("puts");
719 let bytes =
720 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
721 let file = object::File::parse(&bytes[..]).expect("a readable object");
722 assert!(file.section_by_name(PATCHABLE).is_none());
723 }
724
725 #[test]
731 fn each_record_is_tied_to_its_own_function_when_they_are_split_up() {
732 let mut text = calling("puts");
733 text.funcs[0].patch = Some(Patch { at: 0, before: 0 });
734 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
735 text.funcs[1].patch = Some(Patch { at: 16, before: 0 });
736 text.bytes.resize(17, 0x90);
737 let output =
738 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
739 let bytes = write(&text, &Data::default(), &[], &target(), output).expect("an object");
740 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
741 let links: Vec<usize> = file
742 .sections()
743 .filter(|section| section.name() == Ok(PATCHABLE))
744 .map(|section| section.elf_section_header().sh_link.get(Endianness::Little) as usize)
745 .collect();
746 let index = |name: &str| file.section_by_name(name).expect("a text section").index().0;
747 assert_eq!(links, [index(".text.f"), index(".text.g")]);
748 }
749
750 #[test]
751 fn a_global_is_visible_to_the_dynamic_linker_and_a_static_one_is_not_a_symbol_at_all() {
752 let mut text = calling("puts");
753 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
754 text.funcs.push(extent("w".to_owned(), 32, 1, Binding::Weak));
755 text.funcs.push(extent("s".to_owned(), 48, 1, Binding::Local));
756 text.bytes.resize(49, 0x90);
757 let bytes =
758 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
759 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
760 let visibility = |name: &str| {
761 file.symbols()
762 .find(|s| s.name() == Ok(name))
763 .expect("the function")
764 .elf_symbol()
765 .st_visibility()
766 };
767 assert_eq!(visibility("g"), elf::STV_DEFAULT);
769 assert_eq!(visibility("w"), elf::STV_DEFAULT, "a weak one is still a name others may use");
770 assert_eq!(visibility("s"), elf::STV_DEFAULT);
773 }
774
775 #[test]
785 fn a_name_that_asked_to_be_hidden_is_hidden_and_a_protected_one_is_protected() {
786 let mut text = calling("puts");
787 for (index, (name, seen)) in
788 [("h", Visibility::Hidden), ("p", Visibility::Protected)].into_iter().enumerate()
789 {
790 let mut func = extent(name.to_owned(), 16 + index * 16, 1, Binding::Global);
791 func.visibility = seen;
792 text.funcs.push(func);
793 }
794 text.bytes.resize(49, 0x90);
795 let mut data = Data::default();
796 for (name, seen) in [("vh", Visibility::Hidden), ("vp", Visibility::Protected)] {
797 let mut object = variable(name, Place::Written);
798 object.visibility = seen;
799 data.objects.push(object);
800 }
801 let bytes = write(&text, &data, &[], &target(), Output::default()).expect("an object");
802 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
803 let visibility = |name: &str| {
804 file.symbols()
805 .find(|s| s.name() == Ok(name))
806 .expect("the symbol")
807 .elf_symbol()
808 .st_visibility()
809 };
810 assert_eq!(visibility("h"), elf::STV_HIDDEN);
811 assert_eq!(visibility("p"), elf::STV_PROTECTED);
812 assert_eq!(visibility("vh"), elf::STV_HIDDEN, "a variable goes through a second loop");
813 assert_eq!(visibility("vp"), elf::STV_PROTECTED);
814 let h = file.symbols().find(|s| s.name() == Ok("h")).expect("the function");
817 assert!(h.is_global(), "hidden is about the dynamic linker and not about the binding");
818 assert_eq!(h.size(), 1, "and it is still a function of the length it was");
819 }
820
821 #[test]
822 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
823 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
824 .expect("an object");
825 let file = object::File::parse(&bytes[..]).expect("a readable object");
826 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
827 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
828 }
829
830 #[test]
831 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
832 for (reference, wanted) in [
833 (Reference::Call, elf::R_X86_64_PLT32),
834 (Reference::Data, elf::R_X86_64_PC32),
835 (Reference::Got, elf::R_X86_64_REX_GOTPCRELX),
836 ] {
837 let mut text = calling("puts");
838 text.relocs[0].kind = reference;
839 let bytes = write(&text, &Data::default(), &[], &target(), Output::default())
840 .expect("an object");
841 let file = object::File::parse(&bytes[..]).expect("a readable object");
842 let section = file.section_by_name(".text").expect("a text section");
843 let (offset, reloc) = section.relocations().next().expect("one relocation");
844 assert_eq!(offset, 1);
845 assert_eq!(reloc.addend(), -4);
846 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
847 }
848 }
849
850 #[test]
851 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
852 let mut text = calling("puts");
853 text.relocs.push(Reloc {
854 at: 1,
855 symbol: "puts".to_owned(),
856 kind: Reference::Call,
857 addend: -4,
858 });
859 let bytes =
860 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
861 let file = object::File::parse(&bytes[..]).expect("a readable object");
862 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
863 }
864
865 #[test]
866 fn a_function_that_is_also_called_is_not_a_second_symbol() {
867 let text = calling("f");
868 let bytes =
869 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
870 let file = object::File::parse(&bytes[..]).expect("a readable object");
871 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
872 let f = found.next().expect("the function");
873 assert!(!f.is_undefined(), "the file defines it");
874 assert!(found.next().is_none(), "and defines it once");
875 }
876
877 #[test]
878 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
879 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
880 .expect("an object");
881 let file = object::File::parse(&bytes[..]).expect("a readable object");
882 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
883 assert!(note.data().expect("no bytes").is_empty());
884 }
885
886 #[test]
893 fn the_note_that_says_what_the_file_was_built_to_have_checked_is_written() {
894 let property = Property { features: Property::IBT | Property::SHSTK };
895 let output = Output { property, ..Output::default() };
896 let bytes =
897 write(&calling("puts"), &Data::default(), &[], &target(), output).expect("an object");
898 let file = object::File::parse(&bytes[..]).expect("a readable object");
899 let note = file.section_by_name(".note.gnu.property").expect("the note");
900 assert_eq!(note.align(), 8, "a note in a sixty four bit object is read a word at a time");
901 let want: Vec<u8> = [
902 4u32,
903 16,
904 5,
905 u32::from_le_bytes(*b"GNU\0"),
906 Property::X86_FEATURES,
907 4,
908 Property::IBT | Property::SHSTK,
909 0,
910 ]
911 .iter()
912 .flat_map(|word| word.to_le_bytes())
913 .collect();
914 assert_eq!(note.data().expect("the bytes"), &want[..]);
915 }
916
917 #[test]
923 fn a_file_built_to_have_nothing_checked_says_nothing() {
924 let bytes = write(&calling("puts"), &Data::default(), &[], &target(), Output::default())
925 .expect("an object");
926 let file = object::File::parse(&bytes[..]).expect("a readable object");
927 assert!(file.section_by_name(".note.gnu.property").is_none());
928 }
929
930 #[test]
939 fn an_unwind_record_names_the_function_it_is_about_and_not_the_first_one() {
940 let mut text = calling("puts");
941 text.funcs.push(extent("g".to_owned(), 16, 1, Binding::Global));
942 text.bytes.resize(17, 0x90);
943 text.unwind.bytes = vec![0; 64];
946 for (at, name) in [(32usize, "f"), (48usize, "g")] {
947 text.unwind.relocs.push(Reloc {
948 at,
949 symbol: name.to_owned(),
950 kind: Reference::Address { bytes: 8 },
951 addend: 0,
952 });
953 }
954 let bytes =
955 write(&text, &Data::default(), &[], &target(), Output::default()).expect("an object");
956 let file = object::File::parse(&bytes[..]).expect("a readable object");
957 let frames = file.section_by_name(".eh_frame").expect("the table");
958 let mut at = frames.relocations().map(|(offset, _)| offset).collect::<Vec<_>>();
959 at.sort_unstable();
960 assert_eq!(at, [32, 48]);
961 }
962
963 fn lives_in<'a>(file: &'a object::File<'a>, name: &str) -> String {
965 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the symbol");
966 let index = symbol.section_index().expect("a section to be defined in");
967 let section = file.section_by_index(index).expect("a readable section");
968 section.name().expect("a named section").to_owned()
969 }
970
971 fn two() -> Text {
973 let mut text = calling("puts");
974 text.bytes.resize(16, 0x90);
977 text.bytes.extend_from_slice(&[0xe8, 0, 0, 0, 0, 0xc3]);
978 text.funcs.push(extent("g".to_owned(), 16, 6, Binding::Global));
979 text.relocs.push(Reloc {
980 at: 17,
981 symbol: "puts".to_owned(),
982 kind: Reference::Call,
983 addend: -4,
984 });
985 text
986 }
987
988 #[test]
995 fn every_function_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
996 let sections =
997 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
998 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
999 let file = object::File::parse(&bytes[..]).expect("a readable object");
1000 assert_eq!(lives_in(&file, "f"), ".text.f");
1001 assert_eq!(lives_in(&file, "g"), ".text.g");
1002 assert!(file.section_by_name(".text").expect("the empty one").size() == 0);
1003 for name in ["f", "g"] {
1006 let symbol = file.symbols().find(|s| s.name() == Ok(name)).expect("the function");
1007 assert_eq!(symbol.address(), 0, "{name}");
1008 assert_eq!(symbol.size(), 6, "{name}");
1009 }
1010 let section = file.section_by_name(".text.g").expect("the second function");
1011 assert_eq!(section.data().expect("the bytes"), &[0xe8, 0, 0, 0, 0, 0xc3]);
1012 assert_eq!(section.align(), u64::from(crate::FUNC_ALIGN));
1015 }
1016
1017 #[test]
1023 fn a_relocation_moves_with_the_function_whose_bytes_it_is_in() {
1024 let sections =
1025 Output { sections: Sections { functions: true, data: false }, ..Output::default() };
1026 let bytes = write(&two(), &Data::default(), &[], &target(), sections).expect("an object");
1027 let file = object::File::parse(&bytes[..]).expect("a readable object");
1028 for name in [".text.f", ".text.g"] {
1029 let section = file.section_by_name(name).expect("a function");
1030 let (offset, _) = section.relocations().next().expect("the call in it");
1031 assert_eq!(offset, 1, "{name}");
1034 assert_eq!(section.relocations().count(), 1, "{name}");
1035 }
1036 }
1037
1038 fn variable(name: &str, place: Place) -> Object {
1040 Object {
1041 name: name.to_owned(),
1042 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
1043 size: 4,
1044 align: 4,
1045 place,
1046 binding: Binding::Global,
1047 visibility: Visibility::Default,
1048 relocs: Vec::new(),
1049 }
1050 }
1051
1052 fn holding(object: Object) -> Vec<u8> {
1054 let data = Data { objects: vec![object] };
1055 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object")
1056 }
1057
1058 #[test]
1059 fn what_a_variable_is_decides_which_section_it_goes_in() {
1060 for (place, wanted) in [
1061 (Place::Written, ".data"),
1062 (Place::ReadOnly, ".rodata"),
1063 (Place::RelocReadOnly { local: false }, ".data.rel.ro"),
1064 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local"),
1065 (Place::Zero, ".bss"),
1066 (Place::Named(".init_array".to_owned()), ".init_array"),
1067 ] {
1068 let bytes = holding(variable("x", place.clone()));
1069 let file = object::File::parse(&bytes[..]).expect("a readable object");
1070 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
1071 assert_eq!(section.size(), 4, "{place:?}");
1072 let carried = section.data().expect("the bytes").len();
1075 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
1076 }
1077 }
1078
1079 #[test]
1083 fn every_variable_gets_a_section_of_its_own_when_that_is_what_was_asked_for() {
1084 let sections =
1085 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1086 for (place, wanted) in [
1087 (Place::Written, ".data.x"),
1088 (Place::ReadOnly, ".rodata.x"),
1089 (Place::RelocReadOnly { local: false }, ".data.rel.ro.x"),
1090 (Place::RelocReadOnly { local: true }, ".data.rel.ro.local.x"),
1091 (Place::Zero, ".bss.x"),
1092 ] {
1093 let data = Data { objects: vec![variable("x", place.clone())] };
1094 let bytes = write(&Text::default(), &data, &[], &target(), sections).expect("object");
1095 let file = object::File::parse(&bytes[..]).expect("a readable object");
1096 assert_eq!(lives_in(&file, "x"), wanted, "{place:?}");
1097 let section = file.section_by_name(wanted).expect("the section it named");
1098 assert_eq!(section.size(), 4, "{place:?}");
1099 let carried = section.data().expect("the bytes").len();
1102 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
1103 }
1104 }
1105
1106 #[test]
1110 fn a_variable_that_has_no_section_of_its_own_to_be_given_is_left_where_it_was() {
1111 let sections =
1112 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1113 let named = Place::Named(".init_array".to_owned());
1114 let objects = vec![variable("m", Place::Merged), variable("n", named)];
1115 let bytes =
1116 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1117 let file = object::File::parse(&bytes[..]).expect("a readable object");
1118 let m = file.symbols().find(|s| s.name() == Ok("m")).expect("the tentative one");
1119 assert!(m.is_common(), "still the linker's to merge and not in a section at all");
1120 assert_eq!(lives_in(&file, "n"), ".init_array");
1121 assert!(file.section_by_name(".init_array.n").is_none(), "the source already answered");
1122 }
1123
1124 #[test]
1128 fn a_relocation_in_an_image_moves_with_the_variable_whose_image_it_is_in() {
1129 let sections =
1130 Output { sections: Sections { functions: false, data: true }, ..Output::default() };
1131 let pointer = Object {
1132 bytes: vec![0; 8],
1133 size: 8,
1134 align: 8,
1135 relocs: vec![Reloc {
1136 at: 0,
1137 symbol: "y".to_owned(),
1138 kind: Reference::Address { bytes: 8 },
1139 addend: 0,
1140 }],
1141 ..variable("p", Place::Written)
1142 };
1143 let objects = vec![variable("first", Place::Written), pointer];
1144 let bytes =
1145 write(&Text::default(), &Data { objects }, &[], &target(), sections).expect("object");
1146 let file = object::File::parse(&bytes[..]).expect("a readable object");
1147 let section = file.section_by_name(".data.p").expect("the pointer's own section");
1148 let (offset, reloc) = section.relocations().next().expect("one relocation");
1149 assert_eq!(offset, 0);
1152 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1153 }
1154
1155 #[test]
1163 fn every_variable_that_wants_the_local_relocated_section_shares_one() {
1164 let place = Place::RelocReadOnly { local: true };
1165 let data =
1166 Data { objects: vec![variable("first", place.clone()), variable("second", place)] };
1167 let bytes =
1168 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1169 let file = object::File::parse(&bytes[..]).expect("a readable object");
1170 let named = file.sections().filter(|s| s.name() == Ok(".data.rel.ro.local")).count();
1171 assert_eq!(named, 1, "one section holding both, not one each");
1172 }
1173
1174 #[test]
1175 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
1176 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1177 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
1178 let bytes =
1179 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1180 let file = object::File::parse(&bytes[..]).expect("a readable object");
1181 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
1182 assert_eq!(second.kind(), SymbolKind::Data);
1183 assert_eq!(second.size(), 4);
1184 assert_eq!(second.address(), 16);
1188 }
1189
1190 #[test]
1191 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
1192 for (binding, global, weak) in [
1193 (Binding::Global, true, false),
1194 (Binding::Local, false, false),
1195 (Binding::Weak, true, true),
1196 ] {
1197 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
1198 let file = object::File::parse(&bytes[..]).expect("a readable object");
1199 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1200 assert_eq!(x.is_global(), global, "{binding:?}");
1201 assert_eq!(x.is_weak(), weak, "{binding:?}");
1202 }
1203 }
1204
1205 #[test]
1206 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
1207 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
1208 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
1209 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
1210 assert!(x.is_common(), "the linker merges every definition of this name into one");
1211 assert_eq!(x.size(), 4);
1212 assert_eq!(x.address(), 0);
1216 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
1217 }
1218
1219 #[test]
1220 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
1221 let object = Object {
1222 bytes: vec![0; 8],
1223 size: 8,
1224 align: 8,
1225 relocs: vec![Reloc {
1226 at: 0,
1227 symbol: "y".to_owned(),
1228 kind: Reference::Address { bytes: 8 },
1229 addend: 16,
1230 }],
1231 ..variable("p", Place::Written)
1232 };
1233 let bytes = holding(object);
1234 let file = object::File::parse(&bytes[..]).expect("a readable object");
1235 let section = file.section_by_name(".data").expect("a data section");
1236 let (offset, reloc) = section.relocations().next().expect("one relocation");
1237 assert_eq!(offset, 0);
1238 assert_eq!(reloc.addend(), 16);
1239 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
1240 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
1241 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
1242 }
1243
1244 #[test]
1246 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
1247 let mut data = Data { objects: vec![variable("first", Place::Written)] };
1248 data.objects.push(Object {
1249 bytes: vec![0; 16],
1250 size: 16,
1251 align: 8,
1252 relocs: vec![Reloc {
1253 at: 8,
1254 symbol: "y".to_owned(),
1255 kind: Reference::Address { bytes: 8 },
1256 addend: 0,
1257 }],
1258 ..variable("second", Place::Written)
1259 });
1260 let bytes =
1261 write(&Text::default(), &data, &[], &target(), Output::default()).expect("an object");
1262 let file = object::File::parse(&bytes[..]).expect("a readable object");
1263 let section = file.section_by_name(".data").expect("a data section");
1264 let (offset, _) = section.relocations().next().expect("one relocation");
1265 assert_eq!(offset, 16);
1268 }
1269
1270 #[test]
1271 fn a_second_name_is_a_second_symbol_at_the_first_one_s_address_and_no_second_image() {
1272 let data = Data {
1273 objects: vec![Object { binding: Binding::Local, ..variable("a", Place::Written) }],
1274 };
1275 let aliases = [Alias {
1276 name: "b".to_owned(),
1277 target: "a".to_owned(),
1278 binding: Binding::Global,
1279 visibility: Visibility::Default,
1280 }];
1281 let bytes = write(&Text::default(), &data, &aliases, &target(), Output::default())
1282 .expect("an object");
1283 let file = object::File::parse(&bytes[..]).expect("a readable object");
1284 let a = file.symbols().find(|s| s.name() == Ok("a")).expect("the variable");
1285 let b = file.symbols().find(|s| s.name() == Ok("b")).expect("the second name");
1286 assert_eq!(b.address(), a.address(), "the same place");
1287 assert_eq!(b.size(), a.size());
1288 assert_eq!(b.section_index(), a.section_index());
1289 assert!(a.is_local(), "the target was written `static`");
1292 assert!(b.is_global(), "and the name given to it was not");
1293 assert_eq!(file.section_by_name(".data").expect("a data section").size(), 4);
1295 }
1296
1297 #[test]
1298 fn a_function_can_be_given_a_second_name_the_same_way_a_variable_can() {
1299 let text = calling("puts");
1300 let aliases = [Alias {
1301 name: "g".to_owned(),
1302 target: "f".to_owned(),
1303 binding: Binding::Weak,
1304 visibility: Visibility::Default,
1305 }];
1306 let bytes = write(&text, &Data::default(), &aliases, &target(), Output::default())
1307 .expect("an object");
1308 let file = object::File::parse(&bytes[..]).expect("a readable object");
1309 let f = file.symbols().find(|s| s.name() == Ok("f")).expect("the function");
1310 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second name");
1311 assert_eq!(g.address(), f.address());
1312 assert_eq!(g.size(), f.size());
1313 assert_eq!(g.kind(), f.kind(), "a second name for a function is a function");
1314 assert!(g.is_weak(), "so that a program may define the name itself instead");
1315 }
1316
1317 #[test]
1320 fn a_second_name_for_something_this_file_does_not_define_is_refused() {
1321 let aliases = [Alias {
1322 name: "b".to_owned(),
1323 target: "a".to_owned(),
1324 binding: Binding::Global,
1325 visibility: Visibility::Default,
1326 }];
1327 let error =
1328 write(&Text::default(), &Data::default(), &aliases, &target(), Output::default())
1329 .expect_err("nothing to point at");
1330 assert!(matches!(error, Error::Refused { .. }), "{error:?}");
1331 }
1332
1333 #[test]
1334 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
1335 let text = calling("puts");
1336 for triple in [
1337 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
1338 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
1339 ] {
1340 let error =
1341 write(&text, &Data::default(), &[], &TargetInfo::new(triple), Output::default())
1342 .expect_err("no writer");
1343 assert!(matches!(error, Error::Format { .. }), "{error:?}");
1344 }
1345 }
1346}