1use crate::abi;
2use crate::compression::CompressionHeader;
3use crate::dynamic::{Dyn, DynamicTable};
4use crate::endian::EndianParse;
5use crate::file::{parse_ident, Class, FileHeader};
6use crate::gnu_symver::{
7 SymbolVersionTable, VerDefIterator, VerNeedIterator, VersionIndex, VersionIndexTable,
8};
9use crate::hash::{GnuHashTable, SysVHashTable};
10use crate::note::NoteIterator;
11use crate::parse::{ParseAt, ParseError, ReadBytesExt};
12use crate::relocation::{RelIterator, RelaIterator};
13use crate::section::{SectionHeader, SectionHeaderTable};
14use crate::segment::{ProgramHeader, SegmentTable};
15use crate::string_table::StringTable;
16use crate::symbol::{Symbol, SymbolTable};
17
18#[derive(Debug)]
65pub struct ElfBytes<'data, E: EndianParse> {
66 pub ehdr: FileHeader<E>,
67 data: &'data [u8],
68 shdrs: Option<SectionHeaderTable<'data, E>>,
69 phdrs: Option<SegmentTable<'data, E>>,
70}
71
72fn find_shdrs<'data, E: EndianParse>(
77 ehdr: &FileHeader<E>,
78 data: &'data [u8],
79) -> Result<Option<SectionHeaderTable<'data, E>>, ParseError> {
80 if ehdr.e_shoff == 0 {
82 return Ok(None);
83 }
84
85 let shoff: usize = ehdr.e_shoff.try_into()?;
89 let mut shnum = ehdr.e_shnum as usize;
90 if shnum == 0 {
91 let mut offset = shoff;
92 let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?;
93 shnum = shdr0.sh_size.try_into()?;
94 }
95
96 let entsize = SectionHeader::validate_entsize(ehdr.class, ehdr.e_shentsize as usize)?;
98
99 let size = entsize
100 .checked_mul(shnum)
101 .ok_or(ParseError::IntegerOverflow)?;
102 let end = shoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
103 let buf = data.get_bytes(shoff..end)?;
104 Ok(Some(SectionHeaderTable::new(
105 ehdr.endianness,
106 ehdr.class,
107 buf,
108 )))
109}
110
111fn find_phdrs<'data, E: EndianParse>(
114 ehdr: &FileHeader<E>,
115 data: &'data [u8],
116) -> Result<Option<SegmentTable<'data, E>>, ParseError> {
117 if ehdr.e_phoff == 0 {
119 return Ok(None);
120 }
121
122 let mut phnum = ehdr.e_phnum as usize;
126 if phnum == abi::PN_XNUM as usize {
127 let shoff: usize = ehdr.e_shoff.try_into()?;
128 let mut offset = shoff;
129 let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?;
130 phnum = shdr0.sh_info.try_into()?;
131 }
132
133 let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?;
135
136 let phoff: usize = ehdr.e_phoff.try_into()?;
137 let size = entsize
138 .checked_mul(phnum)
139 .ok_or(ParseError::IntegerOverflow)?;
140 let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
141 let buf = data.get_bytes(phoff..end)?;
142 Ok(Some(SegmentTable::new(ehdr.endianness, ehdr.class, buf)))
143}
144
145#[derive(Debug, Default)]
147pub struct CommonElfData<'data, E: EndianParse> {
148 pub symtab: Option<SymbolTable<'data, E>>,
150 pub symtab_strs: Option<StringTable<'data>>,
152
153 pub dynsyms: Option<SymbolTable<'data, E>>,
155 pub dynsyms_strs: Option<StringTable<'data>>,
157
158 pub dynamic: Option<DynamicTable<'data, E>>,
160
161 pub sysv_hash: Option<SysVHashTable<'data, E>>,
163
164 pub gnu_hash: Option<GnuHashTable<'data, E>>,
166}
167
168impl<'data, E: EndianParse> ElfBytes<'data, E> {
169 pub fn minimal_parse(data: &'data [u8]) -> Result<Self, ParseError> {
176 let ident_buf = data.get_bytes(0..abi::EI_NIDENT)?;
177 let ident = parse_ident(ident_buf)?;
178
179 let tail_start = abi::EI_NIDENT;
180 let tail_end = match ident.1 {
181 Class::ELF32 => tail_start + crate::file::ELF32_EHDR_TAILSIZE,
182 Class::ELF64 => tail_start + crate::file::ELF64_EHDR_TAILSIZE,
183 };
184 let tail_buf = data.get_bytes(tail_start..tail_end)?;
185
186 let ehdr = FileHeader::parse_tail(ident, tail_buf)?;
187
188 let shdrs = find_shdrs(&ehdr, data)?;
189 let phdrs = find_phdrs(&ehdr, data)?;
190 Ok(ElfBytes {
191 ehdr,
192 data,
193 shdrs,
194 phdrs,
195 })
196 }
197
198 pub fn segments(&self) -> Option<SegmentTable<'data, E>> {
203 self.phdrs
204 }
205
206 pub fn section_headers(&self) -> Option<SectionHeaderTable<'data, E>> {
211 self.shdrs
212 }
213
214 pub fn section_headers_with_strtab(
264 &self,
265 ) -> Result<
266 (
267 Option<SectionHeaderTable<'data, E>>,
268 Option<StringTable<'data>>,
269 ),
270 ParseError,
271 > {
272 let shdrs = match self.section_headers() {
274 Some(shdrs) => shdrs,
275 None => {
276 return Ok((None, None));
277 }
278 };
279
280 if self.ehdr.e_shstrndx == abi::SHN_UNDEF {
282 return Ok((Some(shdrs), None));
283 }
284
285 let mut shstrndx = self.ehdr.e_shstrndx as usize;
290 if self.ehdr.e_shstrndx == abi::SHN_XINDEX {
291 let shdr_0 = shdrs.get(0)?;
292 shstrndx = shdr_0.sh_link as usize;
293 }
294
295 let strtab = shdrs.get(shstrndx)?;
296 let (strtab_start, strtab_end) = strtab.get_data_range()?;
297 let strtab_buf = self.data.get_bytes(strtab_start..strtab_end)?;
298 Ok((Some(shdrs), Some(StringTable::new(strtab_buf))))
299 }
300
301 pub fn section_header_by_name(&self, name: &str) -> Result<Option<SectionHeader>, ParseError> {
335 let (shdrs, strtab) = match self.section_headers_with_strtab()? {
336 (Some(shdrs), Some(strtab)) => (shdrs, strtab),
337 _ => {
338 return Ok(None);
340 }
341 };
342
343 Ok(shdrs.iter().find(|shdr| {
344 let sh_name = match strtab.get(shdr.sh_name as usize) {
345 Ok(name) => name,
346 _ => {
347 return false;
348 }
349 };
350 name == sh_name
351 }))
352 }
353
354 pub fn find_common_data(&self) -> Result<CommonElfData<'data, E>, ParseError> {
362 let mut result: CommonElfData<'data, E> = CommonElfData::default();
363
364 if let Some(shdrs) = self.shdrs {
366 for shdr in shdrs.iter() {
367 match shdr.sh_type {
368 abi::SHT_SYMTAB => {
369 let strtab_shdr = shdrs.get(shdr.sh_link as usize)?;
370 let (symtab, strtab) =
371 self.section_data_as_symbol_table(&shdr, &strtab_shdr)?;
372
373 result.symtab = Some(symtab);
374 result.symtab_strs = Some(strtab);
375 }
376 abi::SHT_DYNSYM => {
377 let strtab_shdr = shdrs.get(shdr.sh_link as usize)?;
378 let (symtab, strtab) =
379 self.section_data_as_symbol_table(&shdr, &strtab_shdr)?;
380
381 result.dynsyms = Some(symtab);
382 result.dynsyms_strs = Some(strtab);
383 }
384 abi::SHT_DYNAMIC => {
385 result.dynamic = Some(self.section_data_as_dynamic(&shdr)?);
386 }
387 abi::SHT_HASH => {
388 let (start, end) = shdr.get_data_range()?;
389 let buf = self.data.get_bytes(start..end)?;
390 result.sysv_hash = Some(SysVHashTable::new(
391 self.ehdr.endianness,
392 self.ehdr.class,
393 buf,
394 )?);
395 }
396 abi::SHT_GNU_HASH => {
397 let (start, end) = shdr.get_data_range()?;
398 let buf = self.data.get_bytes(start..end)?;
399 result.gnu_hash = Some(GnuHashTable::new(
400 self.ehdr.endianness,
401 self.ehdr.class,
402 buf,
403 )?);
404 }
405 _ => {
406 continue;
407 }
408 }
409 }
410 }
411
412 if result.dynamic.is_none() {
414 if let Some(phdrs) = self.phdrs {
415 if let Some(dyn_phdr) = phdrs.iter().find(|phdr| phdr.p_type == abi::PT_DYNAMIC) {
416 let (start, end) = dyn_phdr.get_file_data_range()?;
417 let buf = self.data.get_bytes(start..end)?;
418 result.dynamic = Some(DynamicTable::new(
419 self.ehdr.endianness,
420 self.ehdr.class,
421 buf,
422 ));
423 }
424 }
425 }
426
427 Ok(result)
428 }
429
430 pub fn section_data(
440 &self,
441 shdr: &SectionHeader,
442 ) -> Result<(&'data [u8], Option<CompressionHeader>), ParseError> {
443 if shdr.sh_type == abi::SHT_NOBITS {
444 return Ok((&[], None));
445 }
446
447 let (start, end) = shdr.get_data_range()?;
448 let buf = self.data.get_bytes(start..end)?;
449
450 if shdr.sh_flags & abi::SHF_COMPRESSED as u64 == 0 {
451 Ok((buf, None))
452 } else {
453 let mut offset = 0;
454 let chdr = CompressionHeader::parse_at(
455 self.ehdr.endianness,
456 self.ehdr.class,
457 &mut offset,
458 buf,
459 )?;
460 let compressed_buf = buf.get(offset..).ok_or(ParseError::SliceReadError((
461 offset,
462 shdr.sh_size.try_into()?,
463 )))?;
464 Ok((compressed_buf, Some(chdr)))
465 }
466 }
467
468 pub fn section_data_as_strtab(
472 &self,
473 shdr: &SectionHeader,
474 ) -> Result<StringTable<'data>, ParseError> {
475 if shdr.sh_type != abi::SHT_STRTAB {
476 return Err(ParseError::UnexpectedSectionType((
477 shdr.sh_type,
478 abi::SHT_STRTAB,
479 )));
480 }
481
482 let (buf, _) = self.section_data(shdr)?;
483 Ok(StringTable::new(buf))
484 }
485
486 pub fn section_data_as_rels(
491 &self,
492 shdr: &SectionHeader,
493 ) -> Result<RelIterator<'data, E>, ParseError> {
494 if shdr.sh_type != abi::SHT_REL {
495 return Err(ParseError::UnexpectedSectionType((
496 shdr.sh_type,
497 abi::SHT_REL,
498 )));
499 }
500
501 let (buf, _) = self.section_data(shdr)?;
502 Ok(RelIterator::new(self.ehdr.endianness, self.ehdr.class, buf))
503 }
504
505 pub fn section_data_as_relas(
510 &self,
511 shdr: &SectionHeader,
512 ) -> Result<RelaIterator<'data, E>, ParseError> {
513 if shdr.sh_type != abi::SHT_RELA {
514 return Err(ParseError::UnexpectedSectionType((
515 shdr.sh_type,
516 abi::SHT_RELA,
517 )));
518 }
519
520 let (buf, _) = self.section_data(shdr)?;
521 Ok(RelaIterator::new(
522 self.ehdr.endianness,
523 self.ehdr.class,
524 buf,
525 ))
526 }
527
528 pub fn section_data_as_notes(
533 &self,
534 shdr: &SectionHeader,
535 ) -> Result<NoteIterator<'data, E>, ParseError> {
536 if shdr.sh_type != abi::SHT_NOTE {
537 return Err(ParseError::UnexpectedSectionType((
538 shdr.sh_type,
539 abi::SHT_NOTE,
540 )));
541 }
542
543 let (buf, _) = self.section_data(shdr)?;
544 Ok(NoteIterator::new(
545 self.ehdr.endianness,
546 self.ehdr.class,
547 shdr.sh_addralign as usize,
548 buf,
549 ))
550 }
551
552 fn section_data_as_dynamic(
555 &self,
556 shdr: &SectionHeader,
557 ) -> Result<DynamicTable<'data, E>, ParseError> {
558 if shdr.sh_type != abi::SHT_DYNAMIC {
559 return Err(ParseError::UnexpectedSectionType((
560 shdr.sh_type,
561 abi::SHT_DYNAMIC,
562 )));
563 }
564
565 Dyn::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?;
567 let (buf, _) = self.section_data(shdr)?;
568 Ok(DynamicTable::new(
569 self.ehdr.endianness,
570 self.ehdr.class,
571 buf,
572 ))
573 }
574
575 pub fn segment_data(&self, phdr: &ProgramHeader) -> Result<&'data [u8], ParseError> {
579 let (start, end) = phdr.get_file_data_range()?;
580 self.data.get_bytes(start..end)
581 }
582
583 pub fn segment_data_as_notes(
588 &self,
589 phdr: &ProgramHeader,
590 ) -> Result<NoteIterator<'data, E>, ParseError> {
591 if phdr.p_type != abi::PT_NOTE {
592 return Err(ParseError::UnexpectedSegmentType((
593 phdr.p_type,
594 abi::PT_NOTE,
595 )));
596 }
597
598 let buf = self.segment_data(phdr)?;
599 Ok(NoteIterator::new(
600 self.ehdr.endianness,
601 self.ehdr.class,
602 phdr.p_align as usize,
603 buf,
604 ))
605 }
606
607 pub fn dynamic(&self) -> Result<Option<DynamicTable<'data, E>>, ParseError> {
609 if let Some(shdrs) = self.section_headers() {
611 if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_DYNAMIC) {
612 return Ok(Some(self.section_data_as_dynamic(&shdr)?));
613 }
614 } else if let Some(phdrs) = self.segments() {
616 if let Some(phdr) = phdrs.iter().find(|phdr| phdr.p_type == abi::PT_DYNAMIC) {
617 let (start, end) = phdr.get_file_data_range()?;
618 let buf = self.data.get_bytes(start..end)?;
619 return Ok(Some(DynamicTable::new(
620 self.ehdr.endianness,
621 self.ehdr.class,
622 buf,
623 )));
624 }
625 }
626
627 Ok(None)
628 }
629
630 fn section_data_as_symbol_table(
633 &self,
634 shdr: &SectionHeader,
635 strtab_shdr: &SectionHeader,
636 ) -> Result<(SymbolTable<'data, E>, StringTable<'data>), ParseError> {
637 Symbol::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?;
639
640 let (symtab_start, symtab_end) = shdr.get_data_range()?;
643 let symtab_buf = self.data.get_bytes(symtab_start..symtab_end)?;
644
645 let (strtab_start, strtab_end) = strtab_shdr.get_data_range()?;
648 let strtab_buf = self.data.get_bytes(strtab_start..strtab_end)?;
649
650 let symtab = SymbolTable::new(self.ehdr.endianness, self.ehdr.class, symtab_buf);
651 let strtab = StringTable::new(strtab_buf);
652 Ok((symtab, strtab))
653 }
654
655 pub fn symbol_table(
657 &self,
658 ) -> Result<Option<(SymbolTable<'data, E>, StringTable<'data>)>, ParseError> {
659 let shdrs = match self.section_headers() {
660 Some(shdrs) => shdrs,
661 None => {
662 return Ok(None);
663 }
664 };
665
666 let symtab_shdr = match shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_SYMTAB) {
668 Some(shdr) => shdr,
669 None => {
670 return Ok(None);
671 }
672 };
673
674 let strtab_shdr = shdrs.get(symtab_shdr.sh_link as usize)?;
675 Ok(Some(self.section_data_as_symbol_table(
676 &symtab_shdr,
677 &strtab_shdr,
678 )?))
679 }
680
681 pub fn dynamic_symbol_table(
683 &self,
684 ) -> Result<Option<(SymbolTable<'data, E>, StringTable<'data>)>, ParseError> {
685 let shdrs = match self.section_headers() {
686 Some(shdrs) => shdrs,
687 None => {
688 return Ok(None);
689 }
690 };
691
692 let symtab_shdr = match shdrs.iter().find(|shdr| shdr.sh_type == abi::SHT_DYNSYM) {
694 Some(shdr) => shdr,
695 None => {
696 return Ok(None);
697 }
698 };
699
700 let strtab_shdr = shdrs.get(symtab_shdr.sh_link as usize)?;
701 Ok(Some(self.section_data_as_symbol_table(
702 &symtab_shdr,
703 &strtab_shdr,
704 )?))
705 }
706
707 pub fn symbol_version_table(&self) -> Result<Option<SymbolVersionTable<'data, E>>, ParseError> {
715 let shdrs = match self.section_headers() {
717 Some(shdrs) => shdrs,
718 None => {
719 return Ok(None);
720 }
721 };
722
723 let mut versym_opt: Option<SectionHeader> = None;
724 let mut needs_opt: Option<SectionHeader> = None;
725 let mut defs_opt: Option<SectionHeader> = None;
726 for shdr in shdrs.iter() {
728 if shdr.sh_type == abi::SHT_GNU_VERSYM {
729 versym_opt = Some(shdr);
730 } else if shdr.sh_type == abi::SHT_GNU_VERNEED {
731 needs_opt = Some(shdr);
732 } else if shdr.sh_type == abi::SHT_GNU_VERDEF {
733 defs_opt = Some(shdr);
734 }
735
736 if versym_opt.is_some() && needs_opt.is_some() && defs_opt.is_some() {
738 break;
739 }
740 }
741
742 let versym_shdr = match versym_opt {
743 Some(shdr) => shdr,
744 None => {
746 return Ok(None);
747 }
748 };
749
750 VersionIndex::validate_entsize(self.ehdr.class, versym_shdr.sh_entsize.try_into()?)?;
753 let (versym_start, versym_end) = versym_shdr.get_data_range()?;
754 let version_ids = VersionIndexTable::new(
755 self.ehdr.endianness,
756 self.ehdr.class,
757 self.data.get_bytes(versym_start..versym_end)?,
758 );
759
760 let verneeds = match needs_opt {
762 Some(shdr) => {
763 let (start, end) = shdr.get_data_range()?;
764 let needs_buf = self.data.get_bytes(start..end)?;
765
766 let strs_shdr = shdrs.get(shdr.sh_link as usize)?;
767 let (strs_start, strs_end) = strs_shdr.get_data_range()?;
768 let strs_buf = self.data.get_bytes(strs_start..strs_end)?;
769
770 Some((
771 VerNeedIterator::new(
772 self.ehdr.endianness,
773 self.ehdr.class,
774 shdr.sh_info as u64,
775 0,
776 needs_buf,
777 ),
778 StringTable::new(strs_buf),
779 ))
780 }
781 None => None,
784 };
785
786 let verdefs = match defs_opt {
788 Some(shdr) => {
789 let (start, end) = shdr.get_data_range()?;
790 let defs_buf = self.data.get_bytes(start..end)?;
791
792 let strs_shdr = shdrs.get(shdr.sh_link as usize)?;
793 let (strs_start, strs_end) = strs_shdr.get_data_range()?;
794 let strs_buf = self.data.get_bytes(strs_start..strs_end)?;
795
796 Some((
797 VerDefIterator::new(
798 self.ehdr.endianness,
799 self.ehdr.class,
800 shdr.sh_info as u64,
801 0,
802 defs_buf,
803 ),
804 StringTable::new(strs_buf),
805 ))
806 }
807 None => None,
810 };
811
812 Ok(Some(SymbolVersionTable::new(
814 version_ids,
815 verneeds,
816 verdefs,
817 )))
818 }
819}
820
821#[cfg(test)]
829mod interface_tests {
830 use super::*;
831 use crate::abi::{SHT_GNU_HASH, SHT_NOBITS, SHT_NOTE, SHT_NULL, SHT_REL, SHT_RELA, SHT_STRTAB};
832 use crate::dynamic::Dyn;
833 use crate::endian::AnyEndian;
834 use crate::hash::sysv_hash;
835 use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId};
836 use crate::relocation::Rela;
837 use crate::segment::ProgramHeader;
838
839 #[test]
840 fn simultaenous_segments_parsing() {
841 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
842 let file_data = std::fs::read(path).expect("Could not read file.");
843 let slice = file_data.as_slice();
844 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
845
846 let iter = file.segments().expect("File should have a segment table");
851
852 let segments: Vec<ProgramHeader> = file
854 .segments()
855 .expect("File should have a segment table")
856 .iter()
857 .collect();
858
859 let expected_phdr = ProgramHeader {
860 p_type: abi::PT_PHDR,
861 p_offset: 64,
862 p_vaddr: 4194368,
863 p_paddr: 4194368,
864 p_filesz: 448,
865 p_memsz: 448,
866 p_flags: 5,
867 p_align: 8,
868 };
869
870 assert_eq!(segments[0], expected_phdr);
872
873 assert_eq!(
875 iter.get(0).expect("should be able to parse phdr"),
876 expected_phdr
877 )
878 }
879
880 #[test]
881 fn segments() {
882 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
883 let file_data = std::fs::read(path).expect("Could not read file.");
884 let slice = file_data.as_slice();
885 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
886
887 let segments: Vec<ProgramHeader> = file
888 .segments()
889 .expect("File should have a segment table")
890 .iter()
891 .collect();
892 assert_eq!(
893 segments[0],
894 ProgramHeader {
895 p_type: abi::PT_PHDR,
896 p_offset: 64,
897 p_vaddr: 4194368,
898 p_paddr: 4194368,
899 p_filesz: 448,
900 p_memsz: 448,
901 p_flags: 5,
902 p_align: 8,
903 }
904 );
905 }
906
907 #[test]
908 fn segments_phnum_in_shdr0() {
909 let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
910 let file_data = std::fs::read(path).expect("Could not read file.");
911 let slice = file_data.as_slice();
912 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
913
914 let segments: Vec<ProgramHeader> = file
915 .segments()
916 .expect("File should have a segment table")
917 .iter()
918 .collect();
919 assert_eq!(
920 segments[0],
921 ProgramHeader {
922 p_type: abi::PT_PHDR,
923 p_offset: 92,
924 p_vaddr: 0,
925 p_paddr: 0,
926 p_filesz: 32,
927 p_memsz: 32,
928 p_flags: 0x20003,
929 p_align: 0x40000,
930 }
931 );
932 }
933
934 #[test]
935 fn section_headers() {
936 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
937 let file_data = std::fs::read(path).expect("Could not read file.");
938 let slice = file_data.as_slice();
939 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
940
941 let shdrs = file
942 .section_headers()
943 .expect("File should have a section table");
944
945 let shdrs_vec: Vec<SectionHeader> = shdrs.iter().collect();
946
947 assert_eq!(shdrs_vec[4].sh_type, SHT_GNU_HASH);
948 }
949
950 #[test]
951 fn section_headers_with_strtab() {
952 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
953 let file_data = std::fs::read(path).expect("Could not read file.");
954 let slice = file_data.as_slice();
955 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
956
957 let (shdrs, strtab) = file
958 .section_headers_with_strtab()
959 .expect("shdrs should be parsable");
960 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
961
962 let with_names: Vec<(&str, SectionHeader)> = shdrs
963 .iter()
964 .map(|shdr| {
965 (
966 strtab
967 .get(shdr.sh_name as usize)
968 .expect("Failed to get section name"),
969 shdr,
970 )
971 })
972 .collect();
973
974 let (name, shdr) = with_names[4];
975 assert_eq!(name, ".gnu.hash");
976 assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH);
977 }
978
979 #[test]
980 fn shnum_and_shstrndx_in_shdr0() {
981 let path = std::path::PathBuf::from("sample-objects/shnum.x86_64");
982 let file_data = std::fs::read(path).expect("Could not read file.");
983 let slice = file_data.as_slice();
984 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).unwrap();
985
986 let (shdrs, strtab) = file
987 .section_headers_with_strtab()
988 .expect("shdrs should be parsable");
989 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
990
991 let shdrs_len = shdrs.len();
992 assert_eq!(shdrs_len, 0xFF15);
993
994 let shdr = shdrs.get(shdrs_len - 1).unwrap();
995 let name = strtab
996 .get(shdr.sh_name as usize)
997 .expect("Failed to get section name");
998
999 assert_eq!(name, ".shstrtab");
1000 assert_eq!(shdr.sh_type, abi::SHT_STRTAB);
1001 }
1002
1003 #[test]
1004 fn section_header_by_name() {
1005 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1006 let file_data = std::fs::read(path).expect("Could not read file.");
1007 let slice = file_data.as_slice();
1008 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1009
1010 let shdr = file
1011 .section_header_by_name(".gnu.hash")
1012 .expect("section table should be parseable")
1013 .expect("file should have .gnu.hash section");
1014
1015 assert_eq!(shdr.sh_type, SHT_GNU_HASH);
1016
1017 let shdr = file
1018 .section_header_by_name(".not.found")
1019 .expect("section table should be parseable");
1020
1021 assert_eq!(shdr, None);
1022 }
1023
1024 #[test]
1025 fn find_common_data() {
1026 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1027 let file_data = std::fs::read(path).expect("Could not read file.");
1028 let slice = file_data.as_slice();
1029 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1030
1031 let elf_scns = file.find_common_data().expect("file should parse");
1032
1033 assert!(elf_scns.symtab.is_some());
1035 assert!(elf_scns.symtab_strs.is_some());
1036 assert!(elf_scns.dynsyms.is_some());
1037 assert!(elf_scns.dynsyms_strs.is_some());
1038 assert!(elf_scns.dynamic.is_some());
1039 assert!(elf_scns.sysv_hash.is_some());
1040 assert!(elf_scns.gnu_hash.is_some());
1041 }
1042
1043 #[test]
1044 fn section_data() {
1045 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1046 let file_data = std::fs::read(path).expect("Could not read file.");
1047 let slice = file_data.as_slice();
1048 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1049
1050 let shdr = file
1051 .section_headers()
1052 .expect("File should have section table")
1053 .get(26)
1054 .expect("shdr should be parsable");
1055
1056 assert_eq!(shdr.sh_type, SHT_NOBITS);
1057
1058 let (data, chdr) = file
1059 .section_data(&shdr)
1060 .expect("Failed to get section data");
1061
1062 assert_eq!(chdr, None);
1063 assert_eq!(data, &[]);
1064 }
1065
1066 #[test]
1068 fn section_data_as_wrong_type() {
1069 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1070 let file_data = std::fs::read(path).expect("Could not read file.");
1071 let slice = file_data.as_slice();
1072 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1073
1074 let shdr = file
1076 .section_headers()
1077 .expect("File should have section table")
1078 .get(0)
1079 .expect("shdr should be parsable");
1080
1081 let err = file
1082 .section_data_as_strtab(&shdr)
1083 .expect_err("shdr0 should be the wrong type");
1084 assert!(
1085 matches!(
1086 err,
1087 ParseError::UnexpectedSectionType((SHT_NULL, SHT_STRTAB))
1088 ),
1089 "Unexpected Error type found: {err}"
1090 );
1091
1092 let err = file
1093 .section_data_as_rels(&shdr)
1094 .expect_err("shdr0 should be the wrong type");
1095 assert!(
1096 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_REL))),
1097 "Unexpected Error type found: {err}"
1098 );
1099
1100 let err = file
1101 .section_data_as_relas(&shdr)
1102 .expect_err("shdr0 should be the wrong type");
1103 assert!(
1104 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_RELA))),
1105 "Unexpected Error type found: {err}"
1106 );
1107
1108 let err = file
1109 .section_data_as_notes(&shdr)
1110 .expect_err("shdr0 should be the wrong type");
1111 assert!(
1112 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_NOTE))),
1113 "Unexpected Error type found: {err}"
1114 );
1115 }
1116
1117 #[test]
1118 fn section_data_as_strtab() {
1119 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1120 let file_data = std::fs::read(path).expect("Could not read file.");
1121 let slice = file_data.as_slice();
1122 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1123
1124 let shdr = file
1125 .section_headers()
1126 .expect("File should have section table")
1127 .get(file.ehdr.e_shstrndx as usize)
1128 .expect("shdr should be parsable");
1129
1130 let strtab = file
1131 .section_data_as_strtab(&shdr)
1132 .expect("Failed to read strtab");
1133
1134 assert_eq!(
1135 strtab.get(1).expect("Failed to get strtab entry"),
1136 ".symtab"
1137 );
1138 }
1139
1140 #[test]
1141 fn section_data_as_relas() {
1142 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1143 let file_data = std::fs::read(path).expect("Could not read file.");
1144 let slice = file_data.as_slice();
1145 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1146
1147 let shdr = file
1148 .section_headers()
1149 .expect("File should have section table")
1150 .get(10)
1151 .expect("Failed to get rela shdr");
1152
1153 let mut relas = file
1154 .section_data_as_relas(&shdr)
1155 .expect("Failed to read relas section");
1156 assert_eq!(
1157 relas.next().expect("Failed to get rela entry"),
1158 Rela {
1159 r_offset: 6293704,
1160 r_sym: 1,
1161 r_type: 7,
1162 r_addend: 0,
1163 }
1164 );
1165 assert_eq!(
1166 relas.next().expect("Failed to get rela entry"),
1167 Rela {
1168 r_offset: 6293712,
1169 r_sym: 2,
1170 r_type: 7,
1171 r_addend: 0,
1172 }
1173 );
1174 assert!(relas.next().is_none());
1175 }
1176
1177 #[test]
1178 fn section_data_as_notes() {
1179 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1180 let file_data = std::fs::read(path).expect("Could not read file.");
1181 let slice = file_data.as_slice();
1182 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1183
1184 let shdr = file
1185 .section_headers()
1186 .expect("File should have section table")
1187 .get(2)
1188 .expect("Failed to get note shdr");
1189
1190 let mut notes = file
1191 .section_data_as_notes(&shdr)
1192 .expect("Failed to read note section");
1193 assert_eq!(
1194 notes.next().expect("Failed to get first note"),
1195 Note::GnuAbiTag(NoteGnuAbiTag {
1196 os: 0,
1197 major: 2,
1198 minor: 6,
1199 subminor: 32
1200 })
1201 );
1202 assert!(notes.next().is_none());
1203 }
1204
1205 #[test]
1206 fn segment_data_as_notes() {
1207 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1208 let file_data = std::fs::read(path).expect("Could not read file.");
1209 let slice = file_data.as_slice();
1210 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1211
1212 let phdr = file
1213 .segments()
1214 .expect("File should have segmetn table")
1215 .get(5)
1216 .expect("Failed to get notes phdr");
1217
1218 let mut notes = file
1219 .segment_data_as_notes(&phdr)
1220 .expect("Failed to read notes segment");
1221 assert_eq!(
1222 notes.next().expect("Failed to get first note"),
1223 Note::GnuAbiTag(NoteGnuAbiTag {
1224 os: 0,
1225 major: 2,
1226 minor: 6,
1227 subminor: 32
1228 })
1229 );
1230 assert_eq!(
1231 notes.next().expect("Failed to get second note"),
1232 Note::GnuBuildId(NoteGnuBuildId(&[
1233 119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211,
1234 118, 163
1235 ]))
1236 );
1237 assert!(notes.next().is_none());
1238 }
1239
1240 #[test]
1241 fn dynamic() {
1242 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1243 let file_data = std::fs::read(path).expect("Could not read file.");
1244 let slice = file_data.as_slice();
1245 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1246
1247 let mut dynamic = file
1248 .dynamic()
1249 .expect("Failed to parse .dynamic")
1250 .expect("Failed to find .dynamic")
1251 .iter();
1252 assert_eq!(
1253 dynamic.next().expect("Failed to get dyn entry"),
1254 Dyn {
1255 d_tag: abi::DT_NEEDED,
1256 d_un: 1
1257 }
1258 );
1259 assert_eq!(
1260 dynamic.next().expect("Failed to get dyn entry"),
1261 Dyn {
1262 d_tag: abi::DT_INIT,
1263 d_un: 4195216
1264 }
1265 );
1266 }
1267
1268 #[test]
1269 fn symbol_table() {
1270 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1271 let file_data = std::fs::read(path).expect("Could not read file.");
1272 let slice = file_data.as_slice();
1273 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1274
1275 let (symtab, strtab) = file
1276 .symbol_table()
1277 .expect("Failed to read symbol table")
1278 .expect("Failed to find symbol table");
1279 let symbol = symtab.get(30).expect("Failed to get symbol");
1280 assert_eq!(
1281 symbol,
1282 Symbol {
1283 st_name: 19,
1284 st_value: 6293200,
1285 st_size: 0,
1286 st_shndx: 21,
1287 st_info: 1,
1288 st_other: 0,
1289 }
1290 );
1291 assert_eq!(
1292 strtab
1293 .get(symbol.st_name as usize)
1294 .expect("Failed to get name from strtab"),
1295 "__JCR_LIST__"
1296 );
1297 }
1298
1299 #[test]
1300 fn dynamic_symbol_table() {
1301 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1302 let file_data = std::fs::read(path).expect("Could not read file.");
1303 let slice = file_data.as_slice();
1304 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1305
1306 let (symtab, strtab) = file
1307 .dynamic_symbol_table()
1308 .expect("Failed to read symbol table")
1309 .expect("Failed to find symbol table");
1310 let symbol = symtab.get(1).expect("Failed to get symbol");
1311 assert_eq!(
1312 symbol,
1313 Symbol {
1314 st_name: 11,
1315 st_value: 0,
1316 st_size: 0,
1317 st_shndx: 0,
1318 st_info: 18,
1319 st_other: 0,
1320 }
1321 );
1322 assert_eq!(
1323 strtab
1324 .get(symbol.st_name as usize)
1325 .expect("Failed to get name from strtab"),
1326 "memset"
1327 );
1328 }
1329
1330 #[test]
1331 fn symbol_version_table() {
1332 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1333 let file_data = std::fs::read(path).expect("Could not read file.");
1334 let slice = file_data.as_slice();
1335 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1336
1337 let vst = file
1338 .symbol_version_table()
1339 .expect("Failed to parse GNU symbol versions")
1340 .expect("Failed to find GNU symbol versions");
1341
1342 let req = vst
1343 .get_requirement(2)
1344 .expect("Failed to parse NEED")
1345 .expect("Failed to find NEED");
1346 assert_eq!(req.file, "libc.so.6");
1347 assert_eq!(req.name, "GLIBC_2.2.5");
1348 assert_eq!(req.hash, 0x9691A75);
1349
1350 let req = vst.get_requirement(3).expect("Failed to parse NEED");
1351 assert!(req.is_none());
1352
1353 let req = vst.get_requirement(4).expect("Failed to parse NEED");
1354 assert!(req.is_none());
1355
1356 let req = vst
1357 .get_requirement(5)
1358 .expect("Failed to parse NEED")
1359 .expect("Failed to find NEED");
1360 assert_eq!(req.file, "libc.so.6");
1361 assert_eq!(req.name, "GLIBC_2.2.5");
1362 assert_eq!(req.hash, 0x9691A75);
1363
1364 let def = vst
1365 .get_definition(3)
1366 .expect("Failed to parse DEF")
1367 .expect("Failed to find DEF");
1368 assert_eq!(def.hash, 0xC33237F);
1369 assert_eq!(def.flags, 1);
1370 assert!(!def.hidden);
1371 let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1372 assert_eq!(def_names, &["hello.so"]);
1373
1374 let def = vst
1375 .get_definition(7)
1376 .expect("Failed to parse DEF")
1377 .expect("Failed to find DEF");
1378 assert_eq!(def.hash, 0x1570B62);
1379 assert_eq!(def.flags, 0);
1380 assert!(def.hidden);
1381 let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1382 assert_eq!(def_names, &["HELLO_1.42"]);
1383 }
1384
1385 #[test]
1386 fn sysv_hash_table() {
1387 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1388 let file_data = std::fs::read(path).expect("Could not read file.");
1389 let slice = file_data.as_slice();
1390 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1391
1392 let common = file.find_common_data().expect("should parse");
1394 let hash_table = common.sysv_hash.expect("should have .hash section");
1395
1396 let (symtab, strtab) = file
1398 .dynamic_symbol_table()
1399 .expect("Failed to read symbol table")
1400 .expect("Failed to find symbol table");
1401
1402 assert_eq!(sysv_hash(b"use_memset_v2"), 0x8080542);
1404 assert_eq!(sysv_hash(b"__gmon_start__"), 0xF4D007F);
1405 assert_eq!(sysv_hash(b"memset"), 0x73C49C4);
1406 assert_eq!(sysv_hash(b"use_memset_v2") % 3, 0);
1407 assert_eq!(sysv_hash(b"__gmon_start__") % 3, 0);
1408 assert_eq!(sysv_hash(b"memset") % 3, 0);
1409
1410 let (sym_idx, sym) = hash_table
1412 .find(b"memset", &symtab, &strtab)
1413 .expect("Failed to parse hash")
1414 .expect("Failed to find hash");
1415
1416 assert_eq!(sym_idx, 2);
1418 assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset");
1419 assert_eq!(
1420 sym,
1421 symtab.get(sym_idx).expect("Failed to get expected sym")
1422 );
1423 }
1424
1425 #[test]
1426 fn gnu_hash_table() {
1427 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1428 let file_data = std::fs::read(path).expect("Could not read file.");
1429 let slice = file_data.as_slice();
1430 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).unwrap();
1431
1432 let common = file.find_common_data().unwrap();
1434 let hash_table = common.gnu_hash.expect("should have .gnu.hash section");
1435
1436 let (symtab, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap());
1438
1439 let (sym_idx, sym) = hash_table
1441 .find(b"use_memset", &symtab, &strtab)
1442 .expect("Failed to parse hash")
1443 .expect("Failed to find hash");
1444
1445 assert_eq!(sym_idx, 9);
1447 assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "use_memset");
1448 assert_eq!(
1449 sym,
1450 symtab.get(sym_idx).expect("Failed to get expected sym")
1451 );
1452 }
1453}
1454
1455#[cfg(test)]
1456mod arch_tests {
1457 use super::*;
1458 use crate::endian::AnyEndian;
1459
1460 macro_rules! arch_test {
1462 ( $arch:expr, $e_machine:expr, $endian:expr) => {{
1463 let path_str = format!("sample-objects/symver.{}.so", $arch);
1464 let path = std::path::PathBuf::from(path_str);
1465 let file_data = std::fs::read(path).expect("file should exist");
1466 let slice = file_data.as_slice();
1467 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("should parse");
1468
1469 assert_eq!(file.ehdr.e_machine, $e_machine);
1470 assert_eq!(file.ehdr.endianness, $endian);
1471
1472 let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse");
1473 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
1474 let _: Vec<_> = shdrs
1475 .iter()
1476 .map(|shdr| {
1477 (
1478 strtab.get(shdr.sh_name as usize).expect("should parse"),
1479 shdr,
1480 )
1481 })
1482 .collect();
1483
1484 let common = file.find_common_data().expect("should parse");
1485
1486 {
1488 let symtab = common.symtab.unwrap();
1489 let strtab = common.symtab_strs.unwrap();
1490 let _: Vec<_> = symtab
1491 .iter()
1492 .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1493 .collect();
1494 }
1495
1496 {
1498 let symtab = common.dynsyms.unwrap();
1499 let strtab = common.dynsyms_strs.unwrap();
1500 let symbols_with_names: Vec<_> = symtab
1501 .iter()
1502 .map(|sym| (strtab.get_raw(sym.st_name as usize).expect("should parse"), sym))
1503 .collect();
1504
1505 let hash_table = common.gnu_hash.unwrap();
1506
1507 let start_idx = hash_table.hdr.table_start_idx as usize;
1509 for sym_idx in 0..symtab.len() {
1510 let (symbol_name, symbol) = symbols_with_names.get(sym_idx).unwrap();
1511
1512 let result = hash_table
1513 .find(symbol_name, &symtab, &strtab)
1514 .expect("Failed to parse hash");
1515
1516 if sym_idx < start_idx {
1517 assert_eq!(result, None);
1518 } else {
1519 let (hash_sym_idx, hash_symbol) = result.unwrap();
1520
1521 assert_eq!(sym_idx, hash_sym_idx);
1523 assert_eq!(
1524 strtab.get_raw(hash_symbol.st_name as usize).unwrap(),
1525 *symbol_name
1526 );
1527 assert_eq!(*symbol, hash_symbol);
1528 }
1529 }
1530 }
1531
1532 let phdrs = file.segments().unwrap();
1533 let note_phdrs: Vec<_> = phdrs
1534 .iter()
1535 .filter(|phdr| phdr.p_type == abi::PT_NOTE)
1536 .collect();
1537 for phdr in note_phdrs {
1538 let _: Vec<_> = file
1539 .segment_data_as_notes(&phdr)
1540 .expect("should parse")
1541 .collect();
1542 }
1543 }};
1544 }
1545
1546 #[test]
1547 fn x86_64() {
1548 arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little);
1549 }
1550
1551 #[test]
1552 fn m68k() {
1553 arch_test!("m68k", abi::EM_68K, AnyEndian::Big);
1554 }
1555
1556 #[test]
1557 fn aarch64() {
1558 arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little);
1559 }
1560
1561 #[test]
1562 fn armhf() {
1563 arch_test!("armhf", abi::EM_ARM, AnyEndian::Little);
1564 }
1565
1566 #[test]
1567 fn powerpc64() {
1568 arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big);
1569 }
1570
1571 #[test]
1572 fn powerpc64le() {
1573 arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little);
1574 }
1575
1576 #[test]
1577 fn riscv64() {
1578 arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little);
1579 }
1580}