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::endian::AnyEndian;
833 use crate::hash::sysv_hash;
834 use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId};
835 use crate::relocation::Rela;
836
837 #[test]
838 fn simultaenous_segments_parsing() {
839 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
840 let file_data = std::fs::read(path).expect("Could not read file.");
841 let slice = file_data.as_slice();
842 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
843
844 let iter = file.segments().expect("File should have a segment table");
849
850 let segments: Vec<ProgramHeader> = file
852 .segments()
853 .expect("File should have a segment table")
854 .iter()
855 .collect();
856
857 let expected_phdr = ProgramHeader {
858 p_type: abi::PT_PHDR,
859 p_offset: 64,
860 p_vaddr: 4194368,
861 p_paddr: 4194368,
862 p_filesz: 448,
863 p_memsz: 448,
864 p_flags: 5,
865 p_align: 8,
866 };
867
868 assert_eq!(segments[0], expected_phdr);
870
871 assert_eq!(
873 iter.get(0).expect("should be able to parse phdr"),
874 expected_phdr
875 )
876 }
877
878 #[test]
879 fn segments() {
880 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
881 let file_data = std::fs::read(path).expect("Could not read file.");
882 let slice = file_data.as_slice();
883 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
884
885 let segments: Vec<ProgramHeader> = file
886 .segments()
887 .expect("File should have a segment table")
888 .iter()
889 .collect();
890 assert_eq!(
891 segments[0],
892 ProgramHeader {
893 p_type: abi::PT_PHDR,
894 p_offset: 64,
895 p_vaddr: 4194368,
896 p_paddr: 4194368,
897 p_filesz: 448,
898 p_memsz: 448,
899 p_flags: 5,
900 p_align: 8,
901 }
902 );
903 }
904
905 #[test]
906 fn segments_phnum_in_shdr0() {
907 let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
908 let file_data = std::fs::read(path).expect("Could not read file.");
909 let slice = file_data.as_slice();
910 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
911
912 let segments: Vec<ProgramHeader> = file
913 .segments()
914 .expect("File should have a segment table")
915 .iter()
916 .collect();
917 assert_eq!(
918 segments[0],
919 ProgramHeader {
920 p_type: abi::PT_PHDR,
921 p_offset: 92,
922 p_vaddr: 0,
923 p_paddr: 0,
924 p_filesz: 32,
925 p_memsz: 32,
926 p_flags: 0x20003,
927 p_align: 0x40000,
928 }
929 );
930 }
931
932 #[test]
933 fn section_headers() {
934 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
935 let file_data = std::fs::read(path).expect("Could not read file.");
936 let slice = file_data.as_slice();
937 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
938
939 let shdrs = file
940 .section_headers()
941 .expect("File should have a section table");
942
943 let shdrs_vec: Vec<SectionHeader> = shdrs.iter().collect();
944
945 assert_eq!(shdrs_vec[4].sh_type, SHT_GNU_HASH);
946 }
947
948 #[test]
949 fn section_headers_with_strtab() {
950 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
951 let file_data = std::fs::read(path).expect("Could not read file.");
952 let slice = file_data.as_slice();
953 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
954
955 let (shdrs, strtab) = file
956 .section_headers_with_strtab()
957 .expect("shdrs should be parsable");
958 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
959
960 let with_names: Vec<(&str, SectionHeader)> = shdrs
961 .iter()
962 .map(|shdr| {
963 (
964 strtab
965 .get(shdr.sh_name as usize)
966 .expect("Failed to get section name"),
967 shdr,
968 )
969 })
970 .collect();
971
972 let (name, shdr) = with_names[4];
973 assert_eq!(name, ".gnu.hash");
974 assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH);
975 }
976
977 #[test]
978 fn shnum_and_shstrndx_in_shdr0() {
979 let path = std::path::PathBuf::from("sample-objects/shnum.x86_64");
980 let file_data = std::fs::read(path).expect("Could not read file.");
981 let slice = file_data.as_slice();
982 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).unwrap();
983
984 let (shdrs, strtab) = file
985 .section_headers_with_strtab()
986 .expect("shdrs should be parsable");
987 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
988
989 let shdrs_len = shdrs.len();
990 assert_eq!(shdrs_len, 0xFF15);
991
992 let shdr = shdrs.get(shdrs_len - 1).unwrap();
993 let name = strtab
994 .get(shdr.sh_name as usize)
995 .expect("Failed to get section name");
996
997 assert_eq!(name, ".shstrtab");
998 assert_eq!(shdr.sh_type, abi::SHT_STRTAB);
999 }
1000
1001 #[test]
1002 fn section_header_by_name() {
1003 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1004 let file_data = std::fs::read(path).expect("Could not read file.");
1005 let slice = file_data.as_slice();
1006 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1007
1008 let shdr = file
1009 .section_header_by_name(".gnu.hash")
1010 .expect("section table should be parseable")
1011 .expect("file should have .gnu.hash section");
1012
1013 assert_eq!(shdr.sh_type, SHT_GNU_HASH);
1014
1015 let shdr = file
1016 .section_header_by_name(".not.found")
1017 .expect("section table should be parseable");
1018
1019 assert_eq!(shdr, None);
1020 }
1021
1022 #[test]
1023 fn find_common_data() {
1024 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1025 let file_data = std::fs::read(path).expect("Could not read file.");
1026 let slice = file_data.as_slice();
1027 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1028
1029 let elf_scns = file.find_common_data().expect("file should parse");
1030
1031 assert!(elf_scns.symtab.is_some());
1033 assert!(elf_scns.symtab_strs.is_some());
1034 assert!(elf_scns.dynsyms.is_some());
1035 assert!(elf_scns.dynsyms_strs.is_some());
1036 assert!(elf_scns.dynamic.is_some());
1037 assert!(elf_scns.sysv_hash.is_some());
1038 assert!(elf_scns.gnu_hash.is_some());
1039 }
1040
1041 #[test]
1042 fn section_data() {
1043 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1044 let file_data = std::fs::read(path).expect("Could not read file.");
1045 let slice = file_data.as_slice();
1046 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1047
1048 let shdr = file
1049 .section_headers()
1050 .expect("File should have section table")
1051 .get(26)
1052 .expect("shdr should be parsable");
1053
1054 assert_eq!(shdr.sh_type, SHT_NOBITS);
1055
1056 let (data, chdr) = file
1057 .section_data(&shdr)
1058 .expect("Failed to get section data");
1059
1060 assert_eq!(chdr, None);
1061 assert_eq!(data, &[]);
1062 }
1063
1064 #[test]
1066 fn section_data_as_wrong_type() {
1067 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1068 let file_data = std::fs::read(path).expect("Could not read file.");
1069 let slice = file_data.as_slice();
1070 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1071
1072 let shdr = file
1074 .section_headers()
1075 .expect("File should have section table")
1076 .get(0)
1077 .expect("shdr should be parsable");
1078
1079 let err = file
1080 .section_data_as_strtab(&shdr)
1081 .expect_err("shdr0 should be the wrong type");
1082 assert!(
1083 matches!(
1084 err,
1085 ParseError::UnexpectedSectionType((SHT_NULL, SHT_STRTAB))
1086 ),
1087 "Unexpected Error type found: {err}"
1088 );
1089
1090 let err = file
1091 .section_data_as_rels(&shdr)
1092 .expect_err("shdr0 should be the wrong type");
1093 assert!(
1094 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_REL))),
1095 "Unexpected Error type found: {err}"
1096 );
1097
1098 let err = file
1099 .section_data_as_relas(&shdr)
1100 .expect_err("shdr0 should be the wrong type");
1101 assert!(
1102 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_RELA))),
1103 "Unexpected Error type found: {err}"
1104 );
1105
1106 let err = file
1107 .section_data_as_notes(&shdr)
1108 .expect_err("shdr0 should be the wrong type");
1109 assert!(
1110 matches!(err, ParseError::UnexpectedSectionType((SHT_NULL, SHT_NOTE))),
1111 "Unexpected Error type found: {err}"
1112 );
1113 }
1114
1115 #[test]
1116 fn section_data_as_strtab() {
1117 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1118 let file_data = std::fs::read(path).expect("Could not read file.");
1119 let slice = file_data.as_slice();
1120 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1121
1122 let shdr = file
1123 .section_headers()
1124 .expect("File should have section table")
1125 .get(file.ehdr.e_shstrndx as usize)
1126 .expect("shdr should be parsable");
1127
1128 let strtab = file
1129 .section_data_as_strtab(&shdr)
1130 .expect("Failed to read strtab");
1131
1132 assert_eq!(
1133 strtab.get(1).expect("Failed to get strtab entry"),
1134 ".symtab"
1135 );
1136 }
1137
1138 #[test]
1139 fn section_data_as_relas() {
1140 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1141 let file_data = std::fs::read(path).expect("Could not read file.");
1142 let slice = file_data.as_slice();
1143 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1144
1145 let shdr = file
1146 .section_headers()
1147 .expect("File should have section table")
1148 .get(10)
1149 .expect("Failed to get rela shdr");
1150
1151 let mut relas = file
1152 .section_data_as_relas(&shdr)
1153 .expect("Failed to read relas section");
1154 assert_eq!(
1155 relas.next().expect("Failed to get rela entry"),
1156 Rela {
1157 r_offset: 6293704,
1158 r_sym: 1,
1159 r_type: 7,
1160 r_addend: 0,
1161 }
1162 );
1163 assert_eq!(
1164 relas.next().expect("Failed to get rela entry"),
1165 Rela {
1166 r_offset: 6293712,
1167 r_sym: 2,
1168 r_type: 7,
1169 r_addend: 0,
1170 }
1171 );
1172 assert!(relas.next().is_none());
1173 }
1174
1175 #[test]
1176 fn section_data_as_notes() {
1177 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1178 let file_data = std::fs::read(path).expect("Could not read file.");
1179 let slice = file_data.as_slice();
1180 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1181
1182 let shdr = file
1183 .section_headers()
1184 .expect("File should have section table")
1185 .get(2)
1186 .expect("Failed to get note shdr");
1187
1188 let mut notes = file
1189 .section_data_as_notes(&shdr)
1190 .expect("Failed to read note section");
1191 assert_eq!(
1192 notes.next().expect("Failed to get first note"),
1193 Note::GnuAbiTag(NoteGnuAbiTag {
1194 os: 0,
1195 major: 2,
1196 minor: 6,
1197 subminor: 32
1198 })
1199 );
1200 assert!(notes.next().is_none());
1201 }
1202
1203 #[test]
1204 fn segment_data_as_notes() {
1205 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1206 let file_data = std::fs::read(path).expect("Could not read file.");
1207 let slice = file_data.as_slice();
1208 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1209
1210 let phdr = file
1211 .segments()
1212 .expect("File should have segmetn table")
1213 .get(5)
1214 .expect("Failed to get notes phdr");
1215
1216 let mut notes = file
1217 .segment_data_as_notes(&phdr)
1218 .expect("Failed to read notes segment");
1219 assert_eq!(
1220 notes.next().expect("Failed to get first note"),
1221 Note::GnuAbiTag(NoteGnuAbiTag {
1222 os: 0,
1223 major: 2,
1224 minor: 6,
1225 subminor: 32
1226 })
1227 );
1228 assert_eq!(
1229 notes.next().expect("Failed to get second note"),
1230 Note::GnuBuildId(NoteGnuBuildId(&[
1231 119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211,
1232 118, 163
1233 ]))
1234 );
1235 assert!(notes.next().is_none());
1236 }
1237
1238 #[test]
1239 fn dynamic() {
1240 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1241 let file_data = std::fs::read(path).expect("Could not read file.");
1242 let slice = file_data.as_slice();
1243 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1244
1245 let mut dynamic = file
1246 .dynamic()
1247 .expect("Failed to parse .dynamic")
1248 .expect("Failed to find .dynamic")
1249 .iter();
1250 assert_eq!(
1251 dynamic.next().expect("Failed to get dyn entry"),
1252 Dyn {
1253 d_tag: abi::DT_NEEDED,
1254 d_un: 1
1255 }
1256 );
1257 assert_eq!(
1258 dynamic.next().expect("Failed to get dyn entry"),
1259 Dyn {
1260 d_tag: abi::DT_INIT,
1261 d_un: 4195216
1262 }
1263 );
1264 }
1265
1266 #[test]
1267 fn symbol_table() {
1268 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1269 let file_data = std::fs::read(path).expect("Could not read file.");
1270 let slice = file_data.as_slice();
1271 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1272
1273 let (symtab, strtab) = file
1274 .symbol_table()
1275 .expect("Failed to read symbol table")
1276 .expect("Failed to find symbol table");
1277 let symbol = symtab.get(30).expect("Failed to get symbol");
1278 assert_eq!(
1279 symbol,
1280 Symbol {
1281 st_name: 19,
1282 st_value: 6293200,
1283 st_size: 0,
1284 st_shndx: 21,
1285 st_info: 1,
1286 st_other: 0,
1287 }
1288 );
1289 assert_eq!(
1290 strtab
1291 .get(symbol.st_name as usize)
1292 .expect("Failed to get name from strtab"),
1293 "__JCR_LIST__"
1294 );
1295 }
1296
1297 #[test]
1298 fn dynamic_symbol_table() {
1299 let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1300 let file_data = std::fs::read(path).expect("Could not read file.");
1301 let slice = file_data.as_slice();
1302 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1303
1304 let (symtab, strtab) = file
1305 .dynamic_symbol_table()
1306 .expect("Failed to read symbol table")
1307 .expect("Failed to find symbol table");
1308 let symbol = symtab.get(1).expect("Failed to get symbol");
1309 assert_eq!(
1310 symbol,
1311 Symbol {
1312 st_name: 11,
1313 st_value: 0,
1314 st_size: 0,
1315 st_shndx: 0,
1316 st_info: 18,
1317 st_other: 0,
1318 }
1319 );
1320 assert_eq!(
1321 strtab
1322 .get(symbol.st_name as usize)
1323 .expect("Failed to get name from strtab"),
1324 "memset"
1325 );
1326 }
1327
1328 #[test]
1329 fn symbol_version_table() {
1330 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1331 let file_data = std::fs::read(path).expect("Could not read file.");
1332 let slice = file_data.as_slice();
1333 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1334
1335 let vst = file
1336 .symbol_version_table()
1337 .expect("Failed to parse GNU symbol versions")
1338 .expect("Failed to find GNU symbol versions");
1339
1340 let req = vst
1341 .get_requirement(2)
1342 .expect("Failed to parse NEED")
1343 .expect("Failed to find NEED");
1344 assert_eq!(req.file, "libc.so.6");
1345 assert_eq!(req.name, "GLIBC_2.2.5");
1346 assert_eq!(req.hash, 0x9691A75);
1347
1348 let req = vst.get_requirement(3).expect("Failed to parse NEED");
1349 assert!(req.is_none());
1350
1351 let req = vst.get_requirement(4).expect("Failed to parse NEED");
1352 assert!(req.is_none());
1353
1354 let req = vst
1355 .get_requirement(5)
1356 .expect("Failed to parse NEED")
1357 .expect("Failed to find NEED");
1358 assert_eq!(req.file, "libc.so.6");
1359 assert_eq!(req.name, "GLIBC_2.2.5");
1360 assert_eq!(req.hash, 0x9691A75);
1361
1362 let def = vst
1363 .get_definition(3)
1364 .expect("Failed to parse DEF")
1365 .expect("Failed to find DEF");
1366 assert_eq!(def.hash, 0xC33237F);
1367 assert_eq!(def.flags, 1);
1368 assert!(!def.hidden);
1369 let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1370 assert_eq!(def_names, &["hello.so"]);
1371
1372 let def = vst
1373 .get_definition(7)
1374 .expect("Failed to parse DEF")
1375 .expect("Failed to find DEF");
1376 assert_eq!(def.hash, 0x1570B62);
1377 assert_eq!(def.flags, 0);
1378 assert!(def.hidden);
1379 let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1380 assert_eq!(def_names, &["HELLO_1.42"]);
1381 }
1382
1383 #[test]
1384 fn sysv_hash_table() {
1385 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1386 let file_data = std::fs::read(path).expect("Could not read file.");
1387 let slice = file_data.as_slice();
1388 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
1389
1390 let common = file.find_common_data().expect("should parse");
1392 let hash_table = common.sysv_hash.expect("should have .hash section");
1393
1394 let (symtab, strtab) = file
1396 .dynamic_symbol_table()
1397 .expect("Failed to read symbol table")
1398 .expect("Failed to find symbol table");
1399
1400 assert_eq!(sysv_hash(b"use_memset_v2"), 0x8080542);
1402 assert_eq!(sysv_hash(b"__gmon_start__"), 0xF4D007F);
1403 assert_eq!(sysv_hash(b"memset"), 0x73C49C4);
1404 assert_eq!(sysv_hash(b"use_memset_v2") % 3, 0);
1405 assert_eq!(sysv_hash(b"__gmon_start__") % 3, 0);
1406 assert_eq!(sysv_hash(b"memset") % 3, 0);
1407
1408 let (sym_idx, sym) = hash_table
1410 .find(b"memset", &symtab, &strtab)
1411 .expect("Failed to parse hash")
1412 .expect("Failed to find hash");
1413
1414 assert_eq!(sym_idx, 2);
1416 assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset");
1417 assert_eq!(
1418 sym,
1419 symtab.get(sym_idx).expect("Failed to get expected sym")
1420 );
1421 }
1422
1423 #[test]
1424 fn gnu_hash_table() {
1425 let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1426 let file_data = std::fs::read(path).expect("Could not read file.");
1427 let slice = file_data.as_slice();
1428 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).unwrap();
1429
1430 let common = file.find_common_data().unwrap();
1432 let hash_table = common.gnu_hash.expect("should have .gnu.hash section");
1433
1434 let (symtab, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap());
1436
1437 let (sym_idx, sym) = hash_table
1439 .find(b"use_memset", &symtab, &strtab)
1440 .expect("Failed to parse hash")
1441 .expect("Failed to find hash");
1442
1443 assert_eq!(sym_idx, 9);
1445 assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "use_memset");
1446 assert_eq!(
1447 sym,
1448 symtab.get(sym_idx).expect("Failed to get expected sym")
1449 );
1450 }
1451}
1452
1453#[cfg(test)]
1454mod arch_tests {
1455 use super::*;
1456 use crate::endian::AnyEndian;
1457
1458 macro_rules! arch_test {
1460 ( $arch:expr, $e_machine:expr, $endian:expr) => {{
1461 let path_str = format!("sample-objects/symver.{}.so", $arch);
1462 let path = std::path::PathBuf::from(path_str);
1463 let file_data = std::fs::read(path).expect("file should exist");
1464 let slice = file_data.as_slice();
1465 let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("should parse");
1466
1467 assert_eq!(file.ehdr.e_machine, $e_machine);
1468 assert_eq!(file.ehdr.endianness, $endian);
1469
1470 let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse");
1471 let (shdrs, strtab) = (shdrs.unwrap(), strtab.unwrap());
1472 let _: Vec<_> = shdrs
1473 .iter()
1474 .map(|shdr| {
1475 (
1476 strtab.get(shdr.sh_name as usize).expect("should parse"),
1477 shdr,
1478 )
1479 })
1480 .collect();
1481
1482 let common = file.find_common_data().expect("should parse");
1483
1484 {
1486 let symtab = common.symtab.unwrap();
1487 let strtab = common.symtab_strs.unwrap();
1488 let _: Vec<_> = symtab
1489 .iter()
1490 .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1491 .collect();
1492 }
1493
1494 {
1496 let symtab = common.dynsyms.unwrap();
1497 let strtab = common.dynsyms_strs.unwrap();
1498 let symbols_with_names: Vec<_> = symtab
1499 .iter()
1500 .map(|sym| (strtab.get_raw(sym.st_name as usize).expect("should parse"), sym))
1501 .collect();
1502
1503 let hash_table = common.gnu_hash.unwrap();
1504
1505 let start_idx = hash_table.hdr.table_start_idx as usize;
1507 for sym_idx in 0..symtab.len() {
1508 let (symbol_name, symbol) = symbols_with_names.get(sym_idx).unwrap();
1509
1510 let result = hash_table
1511 .find(symbol_name, &symtab, &strtab)
1512 .expect("Failed to parse hash");
1513
1514 if sym_idx < start_idx {
1515 assert_eq!(result, None);
1516 } else {
1517 let (hash_sym_idx, hash_symbol) = result.unwrap();
1518
1519 assert_eq!(sym_idx, hash_sym_idx);
1521 assert_eq!(
1522 strtab.get_raw(hash_symbol.st_name as usize).unwrap(),
1523 *symbol_name
1524 );
1525 assert_eq!(*symbol, hash_symbol);
1526 }
1527 }
1528 }
1529
1530 let phdrs = file.segments().unwrap();
1531 let note_phdrs: Vec<_> = phdrs
1532 .iter()
1533 .filter(|phdr| phdr.p_type == abi::PT_NOTE)
1534 .collect();
1535 for phdr in note_phdrs {
1536 let _: Vec<_> = file
1537 .segment_data_as_notes(&phdr)
1538 .expect("should parse")
1539 .collect();
1540 }
1541 }};
1542 }
1543
1544 #[test]
1545 fn x86_64() {
1546 arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little);
1547 }
1548
1549 #[test]
1550 fn m68k() {
1551 arch_test!("m68k", abi::EM_68K, AnyEndian::Big);
1552 }
1553
1554 #[test]
1555 fn aarch64() {
1556 arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little);
1557 }
1558
1559 #[test]
1560 fn armhf() {
1561 arch_test!("armhf", abi::EM_ARM, AnyEndian::Little);
1562 }
1563
1564 #[test]
1565 fn powerpc64() {
1566 arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big);
1567 }
1568
1569 #[test]
1570 fn powerpc64le() {
1571 arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little);
1572 }
1573
1574 #[test]
1575 fn riscv64() {
1576 arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little);
1577 }
1578}