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 [`elf::SHT_GNU_VERDEF`] and [`elf::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 [`elf::SHT_GNU_VERSYM`], [`elf::SHT_GNU_VERDEF`] /// and [`elf::SHT_GNU_VERNEED`] sections. /// /// Returned by [`SectionTable::versions`](super::SectionTable::versions). #[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 parse>( 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 `Ok(None)` for local and global versions. /// Returns `Err(_)` if index is invalid. pub fn version(&self, index: VersionIndex) -> Result>> { if index.index() <= elf::VER_NDX_GLOBAL { return Ok(None); } self.versions .get(usize::from(index.index())) .filter(|version| version.valid) .read_error("Invalid ELF symbol version index") .map(Some) } /// Return true if the given symbol index satisfies the requirements of `need`. /// /// Returns false for any error. /// /// 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 = match self.version(version_index) { Ok(def) => def, Err(_) => return false, }; 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 for the entries in an ELF [`elf::SHT_GNU_VERDEF`] section. #[derive(Debug, Clone)] pub struct VerdefIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { VerdefIterator { endian, data: Bytes(data), } } /// Return the next `Verdef` entry. pub fn next( &mut self, ) -> Result, VerdauxIterator<'data, Elf>)>> { if self.data.is_empty() { return Ok(None); } let verdef = self .data .read_at::>(0) .read_error("ELF verdef is too short")?; let mut verdaux_data = self.data; verdaux_data .skip(verdef.vd_aux.get(self.endian) as usize) .read_error("Invalid ELF vd_aux")?; let verdaux = VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian)); let next = verdef.vd_next.get(self.endian); if next != 0 { self.data .skip(next as usize) .read_error("Invalid ELF vd_next")?; } else { self.data = Bytes(&[]); } Ok(Some((verdef, verdaux))) } } /// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERDEF`] section. #[derive(Debug, Clone)] pub struct VerdauxIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, count: u16, } impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { VerdauxIterator { endian, data: Bytes(data), count, } } /// Return the next `Verdaux` entry. pub fn next(&mut self) -> Result>> { if self.count == 0 { return Ok(None); } let verdaux = self .data .read_at::>(0) .read_error("ELF verdaux is too short")?; self.data .skip(verdaux.vda_next.get(self.endian) as usize) .read_error("Invalid ELF vda_next")?; self.count -= 1; Ok(Some(verdaux)) } } /// An iterator for the entries in an ELF [`elf::SHT_GNU_VERNEED`] section. #[derive(Debug, Clone)] pub struct VerneedIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { VerneedIterator { endian, data: Bytes(data), } } /// Return the next `Verneed` entry. pub fn next( &mut self, ) -> Result< Option<( &'data elf::Verneed, VernauxIterator<'data, Elf>, )>, > { if self.data.is_empty() { return Ok(None); } let verneed = self .data .read_at::>(0) .read_error("ELF verneed is too short")?; let mut vernaux_data = self.data; vernaux_data .skip(verneed.vn_aux.get(self.endian) as usize) .read_error("Invalid ELF vn_aux")?; let vernaux = VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian)); let next = verneed.vn_next.get(self.endian); if next != 0 { self.data .skip(next as usize) .read_error("Invalid ELF vn_next")?; } else { self.data = Bytes(&[]); } Ok(Some((verneed, vernaux))) } } /// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERNEED`] section. #[derive(Debug, Clone)] pub struct VernauxIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, count: u16, } impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { VernauxIterator { endian, data: Bytes(data), count, } } /// Return the next `Vernaux` entry. pub fn next(&mut self) -> Result>> { if self.count == 0 { return Ok(None); } let vernaux = self .data .read_at::>(0) .read_error("ELF vernaux is too short")?; self.data .skip(vernaux.vna_next.get(self.endian) as usize) .read_error("Invalid ELF vna_next")?; self.count -= 1; 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") } }