From d94807ef3a935a8a43c13add986f31e71b6f99bd Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Tue, 3 Aug 2021 19:18:34 +1000 Subject: [PATCH 1/3] read/elf: use SectionIndex in lower level API --- examples/readobj.rs | 12 +++++++----- src/read/elf/file.rs | 4 ++-- src/read/elf/relocation.rs | 16 ++++++++-------- src/read/elf/section.rs | 17 +++++++++-------- src/read/elf/symbol.rs | 22 ++++++++++++++++------ tests/round_trip/elf.rs | 4 ++-- 6 files changed, 44 insertions(+), 31 deletions(-) diff --git a/examples/readobj.rs b/examples/readobj.rs index bbcab143..63b31767 100644 --- a/examples/readobj.rs +++ b/examples/readobj.rs @@ -241,6 +241,7 @@ mod elf { use super::*; use object::elf::*; use object::read::elf::*; + use object::read::{SectionIndex, StringTable}; pub(super) fn print_elf32(p: &mut Printer, data: &[u8]) { if let Ok(elf) = FileHeader32::::parse(data) { @@ -434,10 +435,10 @@ mod elf { strsz = d.d_val(endian).into(); } } - let mut dynstr = object::StringTable::default(); + let mut dynstr = StringTable::default(); for s in segments { if let Ok(Some(data)) = s.data_range(endian, data, strtab, strsz) { - dynstr = object::StringTable::new(data, 0, data.len() as u64); + dynstr = StringTable::new(data, 0, data.len() as u64); break; } } @@ -492,8 +493,9 @@ mod elf { sections: &SectionTable, ) { for (index, section) in sections.iter().enumerate() { + let index = SectionIndex(index); p.group("SectionHeader", |p| { - p.field("Index", index); + p.field("Index", index.0); p.field_string( "Name", section.sh_name(endian), @@ -563,7 +565,7 @@ mod elf { data: &[u8], elf: &Elf, sections: &SectionTable, - section_index: usize, + section_index: SectionIndex, section: &Elf::SectionHeader, ) { if let Ok(Some(symbols)) = section.symbols(endian, data, sections, section_index) { @@ -755,7 +757,7 @@ mod elf { for member in members { let index = member.get(endian); p.print_indent(); - if let Ok(section) = sections.section(index as usize) { + if let Ok(section) = sections.section(SectionIndex(index as usize)) { if let Ok(name) = sections.section_name(endian, section) { p.print_string(name); writeln!(p.w, " ({})", index).unwrap(); diff --git a/src/read/elf/file.rs b/src/read/elf/file.rs index 91273ca4..68094ba1 100644 --- a/src/read/elf/file.rs +++ b/src/read/elf/file.rs @@ -205,7 +205,7 @@ where &'file self, index: SectionIndex, ) -> read::Result> { - let section = self.sections.section(index.0)?; + let section = self.sections.section(index)?; Ok(ElfSection { file: self, index, @@ -274,7 +274,7 @@ where &'file self, ) -> Option> { Some(ElfDynamicRelocationIterator { - section_index: 1, + section_index: SectionIndex(1), file: self, relocations: None, }) diff --git a/src/read/elf/relocation.rs b/src/read/elf/relocation.rs index d3e24b2e..5930613a 100644 --- a/src/read/elf/relocation.rs +++ b/src/read/elf/relocation.rs @@ -8,7 +8,7 @@ use crate::endian::{self, Endianness}; use crate::pod::Pod; use crate::read::{ self, Error, ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget, - SymbolIndex, + SectionIndex, SymbolIndex, }; use super::{ElfFile, FileHeader, SectionHeader, SectionTable}; @@ -26,7 +26,7 @@ impl RelocationSections { pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>( endian: Elf::Endian, sections: &SectionTable<'data, Elf, R>, - symbol_section: usize, + symbol_section: SectionIndex, ) -> read::Result { let mut relocations = vec![0; sections.len()]; for (index, section) in sections.iter().enumerate().rev() { @@ -34,7 +34,7 @@ impl RelocationSections { if sh_type == elf::SHT_REL || sh_type == elf::SHT_RELA { // The symbol indices used in relocations must be for the symbol table // we are expecting to use. - let sh_link = section.sh_link(endian) as usize; + let sh_link = SectionIndex(section.sh_link(endian) as usize); if sh_link != symbol_section { continue; } @@ -105,7 +105,7 @@ where R: ReadRef<'data>, { /// The current relocation section index. - pub(super) section_index: usize, + pub(super) section_index: SectionIndex, pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) relocations: Option>, } @@ -130,9 +130,9 @@ where } let section = self.file.sections.section(self.section_index).ok()?; - self.section_index += 1; + self.section_index.0 += 1; - let sh_link = section.sh_link(endian) as usize; + let sh_link = SectionIndex(section.sh_link(endian) as usize); if sh_link != self.file.dynamic_symbols.section() { continue; } @@ -178,7 +178,7 @@ where R: ReadRef<'data>, { /// The current pointer in the chain of relocation sections. - pub(super) section_index: usize, + pub(super) section_index: SectionIndex, pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) relocations: Option>, } @@ -201,7 +201,7 @@ where } self.relocations = None; } - self.section_index = self.file.relocations.get(self.section_index)?; + self.section_index = SectionIndex(self.file.relocations.get(self.section_index.0)?); // The construction of RelocationSections ensures section_index is valid. let section = self.file.sections.section(self.section_index).unwrap(); match section.sh_type(endian) { diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index 5d899922..690b53d7 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -52,9 +52,9 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { } /// Return the section header at the given index. - pub fn section(&self, index: usize) -> read::Result<&'data Elf::SectionHeader> { + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Elf::SectionHeader> { self.sections - .get(index) + .get(index.0) .read_error("Invalid ELF section index") } @@ -102,7 +102,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { None => return Ok(SymbolTable::default()), }; - SymbolTable::parse(endian, data, self, index, section) + SymbolTable::parse(endian, data, self, SectionIndex(index), section) } /// Return the symbol table at the given section index. @@ -113,7 +113,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { &self, endian: Elf::Endian, data: R, - index: usize, + index: SectionIndex, ) -> read::Result> { let section = self.section(index)?; match section.sh_type(endian) { @@ -128,7 +128,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { pub fn relocation_sections( &self, endian: Elf::Endian, - symbol_section: usize, + symbol_section: SectionIndex, ) -> read::Result { RelocationSections::parse(endian, self, symbol_section) } @@ -515,7 +515,7 @@ where fn relocations(&self) -> ElfSectionRelocationIterator<'data, 'file, Elf, R> { ElfSectionRelocationIterator { - section_index: self.index.0, + section_index: self.index, file: self.file, relocations: None, } @@ -614,7 +614,7 @@ pub trait SectionHeader: Debug + Pod { endian: Self::Endian, data: R, sections: &SectionTable<'data, Self::Elf, R>, - section_index: usize, + section_index: SectionIndex, ) -> read::Result>> { let sh_type = self.sh_type(endian); if sh_type != elf::SHT_SYMTAB && sh_type != elf::SHT_DYNSYM { @@ -671,7 +671,8 @@ pub trait SectionHeader: Debug + Pod { if sh_type != elf::SHT_REL && sh_type != elf::SHT_RELA { return Err(Error("Invalid ELF relocation section type")); } - sections.symbol_table_by_index(endian, data, self.sh_link(endian) as usize) + let link = SectionIndex(self.sh_link(endian) as usize); + sections.symbol_table_by_index(endian, data, link) } /// Return a note iterator for the section data. diff --git a/src/read/elf/symbol.rs b/src/read/elf/symbol.rs index ff2ed199..bbea6125 100644 --- a/src/read/elf/symbol.rs +++ b/src/read/elf/symbol.rs @@ -23,7 +23,8 @@ pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]> where R: ReadRef<'data>, { - section: usize, + section: SectionIndex, + string_section: SectionIndex, symbols: &'data [Elf::Sym], strings: StringTable<'data, R>, shndx: &'data [u32], @@ -32,7 +33,8 @@ where impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { fn default() -> Self { SymbolTable { - section: 0, + section: SectionIndex(0), + string_section: SectionIndex(0), symbols: &[], strings: Default::default(), shndx: &[], @@ -46,7 +48,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { endian: Elf::Endian, data: R, sections: &SectionTable<'data, Elf, R>, - section_index: usize, + section_index: SectionIndex, section: &Elf::SectionHeader, ) -> read::Result> { debug_assert!( @@ -58,7 +60,8 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { .data_as_array(endian, data) .read_error("Invalid ELF symbol table data")?; - let strtab = sections.section(section.sh_link(endian) as usize)?; + let link = SectionIndex(section.sh_link(endian) as usize); + let strtab = sections.section(link)?; let strings = if let Some((str_offset, str_size)) = strtab.file_range(endian) { let str_end = str_offset .checked_add(str_size) @@ -72,7 +75,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { .iter() .find(|s| { s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX - && s.sh_link(endian) as usize == section_index + && s.sh_link(endian) as usize == section_index.0 }) .map(|section| { section @@ -84,6 +87,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { Ok(SymbolTable { section: section_index, + string_section: link, symbols, strings, shndx, @@ -92,10 +96,16 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { /// Return the section index of this symbol table. #[inline] - pub fn section(&self) -> usize { + pub fn section(&self) -> SectionIndex { self.section } + /// Return the section index of the linked string table. + #[inline] + pub fn string_section(&self) -> SectionIndex { + self.string_section + } + /// Return the string table used for the symbol names. #[inline] pub fn strings(&self) -> StringTable<'data, R> { diff --git a/tests/round_trip/elf.rs b/tests/round_trip/elf.rs index 501748cf..048db579 100644 --- a/tests/round_trip/elf.rs +++ b/tests/round_trip/elf.rs @@ -188,7 +188,7 @@ fn note() { let endian: LittleEndian = header.endian().unwrap(); let sections = header.sections(endian, bytes).unwrap(); - let section = sections.section(1).unwrap(); + let section = sections.section(SectionIndex(1)).unwrap(); assert_eq!(sections.section_name(endian, section).unwrap(), b".note4"); assert_eq!(section.sh_addralign(endian), 4); let mut notes = section.notes(endian, bytes).unwrap().unwrap(); @@ -202,7 +202,7 @@ fn note() { assert_eq!(note.n_type(endian), 2); assert!(notes.next().unwrap().is_none()); - let section = sections.section(2).unwrap(); + let section = sections.section(SectionIndex(2)).unwrap(); assert_eq!(sections.section_name(endian, section).unwrap(), b".note8"); assert_eq!(section.sh_addralign(endian), 8); let mut notes = section.notes(endian, bytes).unwrap().unwrap(); From 7c4360aed2eb7a1a220bbef551de2797e9f6fcb6 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Tue, 3 Aug 2021 19:30:56 +1000 Subject: [PATCH 2/3] read/elf: return linked sections in SectionTable methods This lets us print version names in readobj. --- examples/readobj.rs | 44 ++++++++---- src/read/elf/section.rs | 147 +++++++++++++++++++++++++++------------- src/read/elf/symbol.rs | 10 +-- src/read/elf/version.rs | 43 +++++++++++- 4 files changed, 171 insertions(+), 73 deletions(-) diff --git a/examples/readobj.rs b/examples/readobj.rs index 63b31767..45e32b03 100644 --- a/examples/readobj.rs +++ b/examples/readobj.rs @@ -645,8 +645,8 @@ mod elf { sections: &SectionTable, section: &Elf::SectionHeader, ) { - if let Ok(Some(relocations)) = section.rel(endian, data) { - let symbols = section.relocation_symbols(endian, data, sections).ok(); + if let Ok(Some((relocations, link))) = section.rel(endian, data) { + let symbols = sections.symbol_table_by_index(endian, data, link).ok(); let proc = rel_flag_type(endian, elf); for relocation in relocations { p.group("Relocation", |p| { @@ -667,8 +667,8 @@ mod elf { sections: &SectionTable, section: &Elf::SectionHeader, ) { - if let Ok(Some(relocations)) = section.rela(endian, data) { - let symbols = section.relocation_symbols(endian, data, sections).ok(); + if let Ok(Some((relocations, link))) = section.rela(endian, data) { + let symbols = sections.symbol_table_by_index(endian, data, link).ok(); let proc = rel_flag_type(endian, elf); for relocation in relocations { p.group("Relocation", |p| { @@ -866,13 +866,15 @@ mod elf { endian: Elf::Endian, data: &[u8], _elf: &Elf, - _sections: &SectionTable, + sections: &SectionTable, section: &Elf::SectionHeader, ) { - if let Ok(Some(mut verdefs)) = section.gnu_verdef(endian, data) { + if let Ok(Some((mut verdefs, link))) = section.gnu_verdef(endian, data) { + let strings = sections + .strings(endian, data, link) + .unwrap_or(StringTable::default()); while let Ok(Some((verdef, mut verdauxs))) = verdefs.next() { p.group("VersionDefinition", |p| { - // TODO: names p.field("Version", verdef.vd_version.get(endian)); p.field_hex("Flags", verdef.vd_flags.get(endian)); p.flags(verdef.vd_flags.get(endian), 0, FLAGS_VER_FLG); @@ -884,7 +886,11 @@ mod elf { p.field("NextOffset", verdef.vd_next.get(endian)); while let Ok(Some(verdaux)) = verdauxs.next() { p.group("Aux", |p| { - p.field_hex("Name", verdaux.vda_name.get(endian)); + p.field_string( + "Name", + verdaux.vda_name.get(endian), + verdaux.name(endian, strings).ok(), + ); p.field("NextOffset", verdaux.vda_next.get(endian)); }); } @@ -898,16 +904,22 @@ mod elf { endian: Elf::Endian, data: &[u8], _elf: &Elf, - _sections: &SectionTable, + sections: &SectionTable, section: &Elf::SectionHeader, ) { - if let Ok(Some(mut verneeds)) = section.gnu_verneed(endian, data) { + if let Ok(Some((mut verneeds, link))) = section.gnu_verneed(endian, data) { + let strings = sections + .strings(endian, data, link) + .unwrap_or(StringTable::default()); while let Ok(Some((verneed, mut vernauxs))) = verneeds.next() { p.group("VersionNeed", |p| { - // TODO: names p.field("Version", verneed.vn_version.get(endian)); p.field("AuxCount", verneed.vn_cnt.get(endian)); - p.field_hex("Filename", verneed.vn_file.get(endian)); + p.field_string( + "Filename", + verneed.vn_file.get(endian), + verneed.file(endian, strings).ok(), + ); p.field("AuxOffset", verneed.vn_aux.get(endian)); p.field("NextOffset", verneed.vn_next.get(endian)); while let Ok(Some(vernaux)) = vernauxs.next() { @@ -917,7 +929,11 @@ mod elf { p.flags(vernaux.vna_flags.get(endian), 0, FLAGS_VER_FLG); p.field("Index", vernaux.vna_other.get(endian) & !VER_NDX_HIDDEN); p.flags(vernaux.vna_other.get(endian), 0, FLAGS_VER_NDX); - p.field_hex("Name", vernaux.vna_name.get(endian)); + p.field_string( + "Name", + vernaux.vna_name.get(endian), + vernaux.name(endian, strings).ok(), + ); p.field("NextOffset", vernaux.vna_next.get(endian)); }); } @@ -934,7 +950,7 @@ mod elf { _sections: &SectionTable, section: &Elf::SectionHeader, ) { - if let Ok(Some(syms)) = section.gnu_versym(endian, data) { + if let Ok(Some((syms, _link))) = section.gnu_versym(endian, data) { for sym in syms { p.group("VersionSymbol", |p| { p.field("Version", sym.0.get(endian) & !VER_NDX_HIDDEN); diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index 690b53d7..5d93ed47 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -81,6 +81,21 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { section.name(endian, self.strings) } + /// Return the string table at the given section index. + /// + /// Returns an error if the section is not a string table. + #[inline] + pub fn strings( + &self, + endian: Elf::Endian, + data: R, + index: SectionIndex, + ) -> read::Result> { + self.section(index)? + .strings(endian, data)? + .read_error("Invalid ELF string section type") + } + /// Return the symbol table of the given section type. /// /// Returns an empty symbol table if the symbol table does not exist. @@ -118,7 +133,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { let section = self.section(index)?; match section.sh_type(endian) { elf::SHT_DYNSYM | elf::SHT_SYMTAB => {} - _ => return Err(Error("Invalid ELF symbol table section type.")), + _ => return Err(Error("Invalid ELF symbol table section type")), } SymbolTable::parse(endian, data, self, index, section) } @@ -152,13 +167,15 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Return the contents of a SysV hash section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if there is no SysV hash section. /// Returns `Err` for invalid values. pub fn hash( &self, endian: Elf::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(hash) = section.hash(endian, data)? { return Ok(Some(hash)); @@ -186,13 +203,15 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Return the contents of a GNU hash section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if there is no GNU hash section. /// Returns `Err` for invalid values. pub fn gnu_hash( &self, endian: Elf::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(hash) = section.gnu_hash(endian, data)? { return Ok(Some(hash)); @@ -203,13 +222,15 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Return the contents of a `SHT_GNU_VERSYM` section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. /// Returns `Err` for invalid values. pub fn gnu_versym( &self, endian: Elf::Endian, data: R, - ) -> read::Result]>> { + ) -> read::Result], SectionIndex)>> { for section in self.sections { if let Some(syms) = section.gnu_versym(endian, data)? { return Ok(Some(syms)); @@ -220,13 +241,15 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Return the contents of a `SHT_GNU_VERDEF` section. /// + /// Also returns the linked string table index. + /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERDEF` section. /// Returns `Err` for invalid values. pub fn gnu_verdef( &self, endian: Elf::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(defs) = section.gnu_verdef(endian, data)? { return Ok(Some(defs)); @@ -237,13 +260,15 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Return the contents of a `SHT_GNU_VERNEED` section. /// + /// Also returns the linked string table index. + /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERNEED` section. /// Returns `Err` for invalid values. pub fn gnu_verneed( &self, endian: Elf::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(needs) = section.gnu_verneed(endian, data)? { return Ok(Some(needs)); @@ -600,9 +625,29 @@ pub trait SectionHeader: Debug + Pod { .read_error("Invalid ELF section size or offset") } + /// Return the strings in the section. + /// + /// Returns `Ok(None)` if the section does not contain strings. + /// Returns `Err` for invalid values. + fn strings<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result>> { + if self.sh_type(endian) != elf::SHT_STRTAB { + return Ok(None); + } + let str_offset = self.sh_offset(endian).into(); + let str_size = self.sh_size(endian).into(); + let str_end = str_offset + .checked_add(str_size) + .read_error("Invalid ELF string section offset or size")?; + Ok(Some(StringTable::new(data, str_offset, str_end))) + } + /// Return the symbols in the section. /// - /// Also finds the corresponding string table in `sections`. + /// Also finds the linked string table in `sections`. /// /// `section_index` must be the 0-based index of this section, and is used /// to find the corresponding extended section index table in `sections`. @@ -625,54 +670,44 @@ pub trait SectionHeader: Debug + Pod { /// Return the `Elf::Rel` entries in the section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if the section does not contain relocations. /// Returns `Err` for invalid values. fn rel<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result::Rel]>> { + ) -> read::Result::Rel], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_REL { return Ok(None); } - self.data_as_array(endian, data) - .map(Some) - .read_error("Invalid ELF relocation section offset or size") + let rel = self + .data_as_array(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((rel, link))) } /// Return the `Elf::Rela` entries in the section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if the section does not contain relocations. /// Returns `Err` for invalid values. fn rela<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result::Rela]>> { + ) -> read::Result::Rela], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_RELA { return Ok(None); } - self.data_as_array(endian, data) - .map(Some) - .read_error("Invalid ELF relocation section offset or size") - } - - /// Return the symbol table for a relocation section. - /// - /// Returns `Err` for invalid values, including if the section does not contain - /// relocations. - fn relocation_symbols<'data, R: ReadRef<'data>>( - &self, - endian: Self::Endian, - data: R, - sections: &SectionTable<'data, Self::Elf, R>, - ) -> read::Result> { - let sh_type = self.sh_type(endian); - if sh_type != elf::SHT_REL && sh_type != elf::SHT_RELA { - return Err(Error("Invalid ELF relocation section type")); - } + let rela = self + .data_as_array(endian, data) + .read_error("Invalid ELF relocation section offset or size")?; let link = SectionIndex(self.sh_link(endian) as usize); - sections.symbol_table_by_index(endian, data, link) + Ok(Some((rela, link))) } /// Return a note iterator for the section data. @@ -747,13 +782,15 @@ pub trait SectionHeader: Debug + Pod { /// Return the contents of a SysV hash section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if the section does not contain a SysV hash. /// Returns `Err` for invalid values. fn hash<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_HASH { return Ok(None); } @@ -761,7 +798,8 @@ pub trait SectionHeader: Debug + Pod { .data(endian, data) .read_error("Invalid ELF hash section offset or size")?; let hash = HashTable::parse(endian, data)?; - Ok(Some(hash)) + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((hash, link))) } /// Return the header of a GNU hash section. @@ -787,13 +825,15 @@ pub trait SectionHeader: Debug + Pod { /// Return the contents of a GNU hash section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if the section does not contain a GNU hash. /// Returns `Err` for invalid values. fn gnu_hash<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_HASH { return Ok(None); } @@ -801,60 +841,71 @@ pub trait SectionHeader: Debug + Pod { .data(endian, data) .read_error("Invalid ELF GNU hash section offset or size")?; let hash = GnuHashTable::parse(endian, data)?; - Ok(Some(hash)) + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((hash, link))) } /// Return the contents of a `SHT_GNU_VERSYM` section. /// + /// Also returns the linked symbol table index. + /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERSYM`. /// Returns `Err` for invalid values. fn gnu_versym<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result]>> { + ) -> read::Result], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERSYM { return Ok(None); } - self.data_as_array(endian, data) - .read_error("Invalid ELF GNU versym section offset or size") - .map(Some) + let versym = self + .data_as_array(endian, data) + .read_error("Invalid ELF GNU versym section offset or size")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((versym, link))) } /// Return an iterator for the entries of a `SHT_GNU_VERDEF` section. /// + /// Also returns the linked string table index. + /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERDEF`. /// Returns `Err` for invalid values. fn gnu_verdef<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERDEF { return Ok(None); } - let data = self - .data_as_array(endian, data) + let verdef = self + .data(endian, data) .read_error("Invalid ELF GNU verdef section offset or size")?; - Ok(Some(VerdefIterator::new(endian, data))) + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((VerdefIterator::new(endian, verdef), link))) } /// Return an iterator for the entries of a `SHT_GNU_VERNEED` section. /// + /// Also returns the linked string table index. + /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERNEED`. /// Returns `Err` for invalid values. fn gnu_verneed<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, - ) -> read::Result>> { + ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERNEED { return Ok(None); } - let data = self - .data_as_array(endian, data) + let verneed = self + .data(endian, data) .read_error("Invalid ELF GNU verneed section offset or size")?; - Ok(Some(VerneedIterator::new(endian, data))) + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((VerneedIterator::new(endian, verneed), link))) } } diff --git a/src/read/elf/symbol.rs b/src/read/elf/symbol.rs index bbea6125..d1e1d60f 100644 --- a/src/read/elf/symbol.rs +++ b/src/read/elf/symbol.rs @@ -61,15 +61,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { .read_error("Invalid ELF symbol table data")?; let link = SectionIndex(section.sh_link(endian) as usize); - let strtab = sections.section(link)?; - let strings = if let Some((str_offset, str_size)) = strtab.file_range(endian) { - let str_end = str_offset - .checked_add(str_size) - .read_error("Invalid str_size")?; - StringTable::new(data, str_offset, str_end) - } else { - StringTable::default() - }; + let strings = sections.strings(endian, data, link)?; let shndx = sections .iter() diff --git a/src/read/elf/version.rs b/src/read/elf/version.rs index 099e27af..4ead4224 100644 --- a/src/read/elf/version.rs +++ b/src/read/elf/version.rs @@ -1,5 +1,5 @@ -use crate::elf; -use crate::read::{Bytes, ReadError, Result}; +use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable}; +use crate::{elf, endian}; use super::FileHeader; @@ -173,3 +173,42 @@ impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> { Ok(Some(vernaux)) } } + +impl elf::Verdaux { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vda_name.get(endian)) + .read_error("Invalid ELF vda_name") + } +} + +impl elf::Verneed { + /// Parse the file from the string table. + pub fn file<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vn_file.get(endian)) + .read_error("Invalid ELF vn_file") + } +} + +impl elf::Vernaux { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vna_name.get(endian)) + .read_error("Invalid ELF vna_name") + } +} From ff64ae228cffa898f93363fa27e394d1224f764e Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Fri, 6 Aug 2021 14:41:57 +1000 Subject: [PATCH 3/3] read/elf: add VersionTable This allows looking up symbol versions. Used in: - hash table find functions - readobj example output --- examples/readobj.rs | 94 +++++++++++++------ src/elf.rs | 10 +- src/read/elf/hash.rs | 29 ++++-- src/read/elf/section.rs | 18 +++- src/read/elf/version.rs | 199 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 308 insertions(+), 42 deletions(-) diff --git a/examples/readobj.rs b/examples/readobj.rs index 45e32b03..72b2b86c 100644 --- a/examples/readobj.rs +++ b/examples/readobj.rs @@ -569,6 +569,11 @@ mod elf { section: &Elf::SectionHeader, ) { if let Ok(Some(symbols)) = section.symbols(endian, data, sections, section_index) { + let versions = if section.sh_type(endian) == SHT_DYNSYM { + sections.versions(endian, data).ok() + } else { + None + }; let os_stt = match elf.e_ident().os_abi { ELFOSABI_GNU => FLAGS_STT_GNU, ELFOSABI_HPUX => FLAGS_STT_HP, @@ -601,6 +606,13 @@ mod elf { symbol.st_name(endian), symbol.name(endian, symbols.strings()).ok(), ); + if let Some(versions) = versions.as_ref() { + let version_index = versions.version_index(endian, index); + if let Some(version) = versions.version(version_index) { + p.field_string("Version", version_index.0, Some(version.name())); + p.flags(version_index.0, 0, FLAGS_VERSYM); + } + } p.field_hex("Value", symbol.st_value(endian).into()); p.field_hex("Size", symbol.st_size(endian).into()); p.field_enums("Type", symbol.st_type(), &[FLAGS_STT, os_stt, proc_stt]); @@ -813,17 +825,23 @@ mod elf { }); } /* TODO: add this in a test somewhere - if let Ok(Some(hash_table)) = section.hash(endian, data) { - if let Ok(symbols) = _sections.symbols(endian, data, SHT_DYNSYM) { - for symbol in symbols.symbols() { - let name = symbols.symbol_name(endian, symbol).unwrap(); - if !symbol.is_definition(endian) { - continue; + if let Ok(Some((hash_table, link))) = section.hash(endian, data) { + if let Ok(symbols) = _sections.symbol_table_by_index(endian, data, link) { + if let Ok(versions) = _sections.versions(endian, data) { + for (index, symbol) in symbols.symbols().iter().enumerate() { + let name = symbols.symbol_name(endian, symbol).unwrap(); + if !symbol.is_definition(endian) { + continue; + } + let hash = hash(name); + let version = versions.version(versions.version_index(endian, index)); + let (hash_index, hash_symbol) = hash_table + .find(endian, name, hash, version, &symbols, &versions) + .unwrap(); + let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap(); + assert_eq!(name, hash_name); + assert_eq!(index, hash_index); } - let hash = hash(name); - let hash_symbol = hash_table.find(endian, name, hash, &symbols).unwrap(); - let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap(); - assert_eq!(name, hash_name); } } } @@ -847,14 +865,25 @@ mod elf { }); } /* TODO: add this in a test somewhere - if let Ok(Some(hash_table)) = section.gnu_hash(endian, data) { - if let Ok(symbols) = _sections.symbols(endian, data, SHT_DYNSYM) { - for symbol in &symbols.symbols()[hash_table.symbol_base() as usize..] { - let name = symbols.symbol_name(endian, symbol).unwrap(); - let hash = gnu_hash(name); - let hash_symbol = hash_table.find(endian, name, hash, &symbols).unwrap(); - let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap(); - assert_eq!(name, hash_name); + if let Ok(Some((hash_table, link))) = section.gnu_hash(endian, data) { + if let Ok(symbols) = _sections.symbol_table_by_index(endian, data, link) { + if let Ok(versions) = _sections.versions(endian, data) { + for (index, symbol) in symbols + .symbols() + .iter() + .enumerate() + .skip(hash_table.symbol_base() as usize) + { + let name = symbols.symbol_name(endian, symbol).unwrap(); + let hash = gnu_hash(name); + let version = versions.version(versions.version_index(endian, index)); + let (hash_index, hash_symbol) = hash_table + .find(endian, name, hash, version, &symbols, &versions) + .unwrap(); + let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap(); + assert_eq!(name, hash_name); + assert_eq!(index, hash_index); + } } } } @@ -878,8 +907,7 @@ mod elf { p.field("Version", verdef.vd_version.get(endian)); p.field_hex("Flags", verdef.vd_flags.get(endian)); p.flags(verdef.vd_flags.get(endian), 0, FLAGS_VER_FLG); - p.field("Index", verdef.vd_ndx.get(endian) & !VER_NDX_HIDDEN); - p.flags(verdef.vd_ndx.get(endian), 0, FLAGS_VER_NDX); + p.field("Index", verdef.vd_ndx.get(endian)); p.field("AuxCount", verdef.vd_cnt.get(endian)); p.field_hex("Hash", verdef.vd_hash.get(endian)); p.field("AuxOffset", verdef.vd_aux.get(endian)); @@ -927,8 +955,7 @@ mod elf { p.field_hex("Hash", vernaux.vna_hash.get(endian)); p.field_hex("Flags", vernaux.vna_flags.get(endian)); p.flags(vernaux.vna_flags.get(endian), 0, FLAGS_VER_FLG); - p.field("Index", vernaux.vna_other.get(endian) & !VER_NDX_HIDDEN); - p.flags(vernaux.vna_other.get(endian), 0, FLAGS_VER_NDX); + p.field("Index", vernaux.vna_other.get(endian)); p.field_string( "Name", vernaux.vna_name.get(endian), @@ -947,15 +974,25 @@ mod elf { endian: Elf::Endian, data: &[u8], _elf: &Elf, - _sections: &SectionTable, + sections: &SectionTable, section: &Elf::SectionHeader, ) { if let Ok(Some((syms, _link))) = section.gnu_versym(endian, data) { - for sym in syms { + let versions = sections.versions(endian, data).ok(); + for (index, sym) in syms.iter().enumerate() { + let sym = VersionIndex(sym.0.get(endian)); p.group("VersionSymbol", |p| { - p.field("Version", sym.0.get(endian) & !VER_NDX_HIDDEN); - p.flags(sym.0.get(endian), 0, FLAGS_VER_NDX); - // TODO: version name + p.field("Index", index); + if sym.0 <= VER_NDX_GLOBAL { + p.field_enum("Version", sym.0, FLAGS_VER_NDX); + } else { + let name = versions + .as_ref() + .and_then(|versions| versions.version(sym)) + .map(|version| version.name()); + p.field_string("Version", sym.0, name); + }; + p.flags(sym.0, 0, FLAGS_VERSYM); }); } } @@ -3360,7 +3397,8 @@ mod elf { DF_1_PIE, ); static FLAGS_VER_FLG: &[Flag] = &flags!(VER_FLG_BASE, VER_FLG_WEAK); - static FLAGS_VER_NDX: &[Flag] = &flags!(VER_NDX_HIDDEN); + static FLAGS_VER_NDX: &[Flag] = &flags!(VER_NDX_LOCAL, VER_NDX_GLOBAL); + static FLAGS_VERSYM: &[Flag] = &flags!(VERSYM_HIDDEN); } mod macho { diff --git a/src/elf.rs b/src/elf.rs index 28ac16bb..7e0e328c 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -1694,6 +1694,11 @@ pub const DF_1_PIE: u32 = 0x0800_0000; #[repr(C)] pub struct Versym(pub U16); +/// Symbol is hidden. +pub const VERSYM_HIDDEN: u16 = 0x8000; +/// Symbol version index. +pub const VERSYM_VERSION: u16 = 0x7fff; + /// Version definition sections #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -1720,9 +1725,10 @@ pub const VER_DEF_NONE: u16 = 0; /// Current version pub const VER_DEF_CURRENT: u16 = 1; -// Legal values for vd_flags and vna_flags (version information flags). +// Legal values for vd_flags (version information flags). /// Version definition of file itself pub const VER_FLG_BASE: u16 = 0x1; +// Legal values for vd_flags and vna_flags (version information flags). /// Weak version identifier pub const VER_FLG_WEAK: u16 = 0x2; @@ -1731,8 +1737,6 @@ pub const VER_FLG_WEAK: u16 = 0x2; pub const VER_NDX_LOCAL: u16 = 0; /// Symbol is global. pub const VER_NDX_GLOBAL: u16 = 1; -/// Symbol is hidden. -pub const VER_NDX_HIDDEN: u16 = 0x8000; /// Auxiliary version information. #[derive(Debug, Clone, Copy)] diff --git a/src/read/elf/hash.rs b/src/read/elf/hash.rs index fab484e4..aa1039ac 100644 --- a/src/read/elf/hash.rs +++ b/src/read/elf/hash.rs @@ -4,7 +4,7 @@ use crate::elf; use crate::read::{ReadError, ReadRef, Result}; use crate::{U32, U64}; -use super::{FileHeader, Sym, SymbolTable}; +use super::{FileHeader, Sym, SymbolTable, Version, VersionTable}; /// A SysV symbol hash table in an ELF file. #[derive(Debug)] @@ -39,14 +39,16 @@ impl<'data, Elf: FileHeader> HashTable<'data, Elf> { self.chains.len() as u32 } - /// Use the hash table to find the symbol table entry with the given name and hash. + /// Use the hash table to find the symbol table entry with the given name, hash and version. pub fn find>( &self, endian: Elf::Endian, name: &[u8], hash: u32, + version: Option<&Version>, symbols: &SymbolTable<'data, Elf, R>, - ) -> Option<&'data Elf::Sym> { + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { // Get the chain start from the bucket for this hash. let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; // Avoid infinite loop. @@ -54,8 +56,10 @@ impl<'data, Elf: FileHeader> HashTable<'data, Elf> { let strings = symbols.strings(); while index != 0 && i < self.chains.len() { if let Ok(symbol) = symbols.symbol(index) { - if symbol.name(endian, strings) == Ok(name) { - return Some(symbol); + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); } } index = self.chains.get(index)?.get(endian) as usize; @@ -150,14 +154,16 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { None } - /// Use the hash table to find the symbol table entry with the given name and hash. + /// Use the hash table to find the symbol table entry with the given name, hash, and version. pub fn find>( &self, endian: Elf::Endian, name: &[u8], hash: u32, + version: Option<&Version>, symbols: &SymbolTable<'data, Elf, R>, - ) -> Option<&'data Elf::Sym> { + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { let word_bits = mem::size_of::() as u32 * 8; // Test against bloom filter. @@ -184,7 +190,7 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { } // Get the chain start from the bucket for this hash. - let index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; + let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; if index == 0 { return None; } @@ -198,13 +204,16 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { for (symbol, value) in symbols.iter().zip(values.iter()) { let value = value.get(endian); if value | 1 == hash | 1 { - if symbol.name(endian, strings) == Ok(name) { - return Some(symbol); + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); } } if value & 1 != 0 { break; } + index += 1; } None } diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index 5d93ed47..6e1a6ff6 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -11,7 +11,7 @@ use crate::read::{ use super::{ CompressionHeader, ElfFile, ElfSectionRelocationIterator, FileHeader, GnuHashTable, HashTable, - NoteIterator, RelocationSections, SymbolTable, VerdefIterator, VerneedIterator, + NoteIterator, RelocationSections, SymbolTable, VerdefIterator, VerneedIterator, VersionTable, }; /// The table of section headers in an ELF file. @@ -276,6 +276,22 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { } Ok(None) } + + /// Returns the symbol version table. + /// + /// Returns an empty table if symbol versions are not present. + pub fn versions(&self, endian: Elf::Endian, data: R) -> read::Result> { + let (versyms, strings) = match self.gnu_versym(endian, data)? { + Some((versyms, link)) => ( + versyms, + self.symbol_table_by_index(endian, data, link)?.strings(), + ), + None => return Ok(VersionTable::default()), + }; + let verdefs = self.gnu_verdef(endian, data)?.map(|x| x.0); + let verneeds = self.gnu_verneed(endian, data)?.map(|x| x.0); + VersionTable::new(endian, versyms, verdefs, verneeds, strings) + } } /// An iterator over the sections of an `ElfFile32`. diff --git a/src/read/elf/version.rs b/src/read/elf/version.rs index 4ead4224..6af7645b 100644 --- a/src/read/elf/version.rs +++ b/src/read/elf/version.rs @@ -1,8 +1,207 @@ +use alloc::vec::Vec; + use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable}; use crate::{elf, endian}; use super::FileHeader; +/// A version index. +#[derive(Debug, Default, Clone, Copy)] +pub struct VersionIndex(pub u16); + +impl VersionIndex { + /// Return the version index. + pub fn index(&self) -> u16 { + self.0 & elf::VERSYM_VERSION + } + + /// Return true if it is the local index. + pub fn is_local(&self) -> bool { + self.index() == elf::VER_NDX_LOCAL + } + + /// Return true if it is the global index. + pub fn is_global(&self) -> bool { + self.index() == elf::VER_NDX_GLOBAL + } + + /// Return the hidden flag. + pub fn is_hidden(&self) -> bool { + self.0 & elf::VERSYM_HIDDEN != 0 + } +} + +/// A version definition or requirement. +/// +/// This is derived from entries in the `SHT_GNU_verdef` and `SHT_GNU_verneed` sections. +#[derive(Debug, Default, Clone, Copy)] +pub struct Version<'data> { + name: &'data [u8], + hash: u32, + // Used to keep track of valid indices in `VersionTable`. + valid: bool, +} + +impl<'data> Version<'data> { + /// Return the version name. + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Return hash of the version name. + pub fn hash(&self) -> u32 { + self.hash + } +} + +/// A table of version definitions and requirements. +/// +/// It allows looking up the version information for a given symbol index. +/// +/// This is derived from entries in the `SHT_GNU_versym`, `SHT_GNU_verdef` and `SHT_GNU_verneed` sections. +#[derive(Debug, Clone)] +pub struct VersionTable<'data, Elf: FileHeader> { + symbols: &'data [elf::Versym], + versions: Vec>, +} + +impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> { + fn default() -> Self { + VersionTable { + symbols: &[], + versions: Vec::new(), + } + } +} + +impl<'data, Elf: FileHeader> VersionTable<'data, Elf> { + /// Parse the version sections. + pub fn new>( + endian: Elf::Endian, + versyms: &'data [elf::Versym], + verdefs: Option>, + verneeds: Option>, + strings: StringTable<'data, R>, + ) -> Result { + let mut max_index = 0; + if let Some(mut verdefs) = verdefs.clone() { + while let Some((verdef, _)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + if let Some(mut verneeds) = verneeds.clone() { + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + } + + // Indices should be sequential, but this could be up to + // 32k * size_of::() if max_index is bad. + let mut versions = vec![Version::default(); max_index as usize + 1]; + + if let Some(mut verdefs) = verdefs { + while let Some((verdef, mut verdauxs)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + if let Some(verdaux) = verdauxs.next()? { + versions[usize::from(index)] = Version { + name: verdaux.name(endian, strings)?, + hash: verdef.vd_hash.get(endian), + valid: true, + }; + } + } + } + if let Some(mut verneeds) = verneeds { + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + versions[usize::from(index)] = Version { + name: vernaux.name(endian, strings)?, + hash: vernaux.vna_hash.get(endian), + valid: true, + }; + } + } + } + + Ok(VersionTable { + symbols: versyms, + versions, + }) + } + + /// Return true if the version table is empty. + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// Return version index for a given symbol index. + pub fn version_index(&self, endian: Elf::Endian, index: usize) -> VersionIndex { + let version_index = match self.symbols.get(index) { + Some(x) => x.0.get(endian), + // Ideally this would be VER_NDX_LOCAL for undefined symbols, + // but currently there are no checks that need this distinction. + None => elf::VER_NDX_GLOBAL, + }; + VersionIndex(version_index) + } + + /// Return version information for a given symbol version index. + /// + /// Returns `None` if index is invalid. This may occur for local and global versions. + pub fn version(&self, index: VersionIndex) -> Option<&Version<'data>> { + if index.index() <= elf::VER_NDX_GLOBAL { + return None; + } + self.versions + .get(usize::from(index.index())) + .filter(|version| version.valid) + } + + /// Return true if the given symbol index satisifies the requirements of `need`. + /// + /// Note: this function hasn't been fully tested and is likely to be incomplete. + pub fn matches(&self, endian: Elf::Endian, index: usize, need: Option<&Version>) -> bool { + let version_index = self.version_index(endian, index); + let def = self.version(version_index); + match (def, need) { + (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name, + (None, Some(_need)) => { + // Version must be present if needed. + false + } + (Some(_def), None) => { + // For a dlsym call, use the newest version. + // TODO: if not a dlsym call, then use the oldest version. + !version_index.is_hidden() + } + (None, None) => true, + } + } +} + /// An iterator over the entries in an ELF `SHT_GNU_verdef` section. #[derive(Debug, Clone)] pub struct VerdefIterator<'data, Elf: FileHeader> {