| //! Symbol versioning |
| //! |
| //! Implementation of the GNU symbol versioning extension according to |
| //! [LSB Core Specification - Symbol Versioning][lsb-symver]. |
| //! |
| //! # Examples |
| //! |
| //! List the dependencies of an ELF file that have [version needed][lsb-verneed] information along |
| //! with the versions needed for each dependency. |
| //! ```rust |
| //! use goblin::error::Error; |
| //! |
| //! pub fn show_verneed(bytes: &[u8]) -> Result<(), Error> { |
| //! let binary = goblin::elf::Elf::parse(&bytes)?; |
| //! |
| //! if let Some(verneed) = binary.verneed { |
| //! for need_file in verneed.iter() { |
| //! println!( |
| //! "Depend on {:?} with version(s):", |
| //! binary.dynstrtab.get_at(need_file.vn_file) |
| //! ); |
| //! for need_ver in need_file.iter() { |
| //! println!("{:?}", binary.dynstrtab.get_at(need_ver.vna_name)); |
| //! } |
| //! } |
| //! } |
| //! |
| //! Ok(()) |
| //! } |
| //! ``` |
| //! |
| //! List the [version defined][lsb-verdef] information of an ELF file, effectively listing the version |
| //! defined by this ELF file. |
| //! ```rust |
| //! use goblin::error::Error; |
| //! |
| //! pub fn show_verdef(bytes: &[u8]) -> Result<(), Error> { |
| //! let binary = goblin::elf::Elf::parse(&bytes)?; |
| //! |
| //! if let Some(verdef) = &binary.verdef { |
| //! for def in verdef.iter() { |
| //! for (n, aux) in def.iter().enumerate() { |
| //! let name = binary.dynstrtab.get_at(aux.vda_name); |
| //! match n { |
| //! 0 => print!("Name: {:?}", name), |
| //! 1 => print!(" Parent: {:?}", name), |
| //! _ => print!(", {:?}", name), |
| //! } |
| //! } |
| //! print!("\n"); |
| //! } |
| //! } |
| //! |
| //! Ok(()) |
| //! } |
| //! ``` |
| //! |
| //! [lsb-symver]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html |
| //! [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERRQMTS |
| //! [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERDEFS |
| |
| use crate::container; |
| use crate::elf::section_header::{SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED, SHT_GNU_VERSYM}; |
| use crate::error::Result; |
| use core::iter::FusedIterator; |
| use scroll::Pread; |
| |
| /****************** |
| * ELF Constants * |
| ******************/ |
| |
| // Versym constants. |
| |
| /// Constant describing a local symbol, see [`Versym::is_local`]. |
| pub const VER_NDX_LOCAL: u16 = 0; |
| /// Constant describing a global symbol, see [`Versym::is_global`]. |
| pub const VER_NDX_GLOBAL: u16 = 1; |
| /// Bitmask to check hidden bit, see [`Versym::is_hidden`]. |
| pub const VERSYM_HIDDEN: u16 = 0x8000; |
| /// Bitmask to get version information, see [`Versym::version`]. |
| pub const VERSYM_VERSION: u16 = 0x7fff; |
| |
| // Verdef constants. |
| |
| /// Bitmask to check `base` flag in [`Verdef::vd_flags`]. |
| pub const VER_FLG_BASE: u16 = 0x1; |
| /// Bitmask to check `weak` flag in [`Verdef::vd_flags`]. |
| pub const VER_FLG_WEAK: u16 = 0x2; |
| /// Bitmask to check `info` flag in [`Verdef::vd_flags`]. |
| pub const VER_FLG_INFO: u16 = 0x4; |
| |
| /******************** |
| * ELF Structures * |
| ********************/ |
| |
| /// An ELF `Symbol Version` entry. |
| /// |
| /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL |
| #[repr(C)] |
| #[derive(Debug, Pread)] |
| struct ElfVersym { |
| vs_val: u16, |
| } |
| |
| /// An ELF `Version Definition` entry Elfxx_Verdef. |
| /// |
| /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES |
| #[repr(C)] |
| #[derive(Debug, Pread)] |
| struct ElfVerdef { |
| vd_version: u16, |
| vd_flags: u16, |
| vd_ndx: u16, |
| vd_cnt: u16, |
| vd_hash: u32, |
| vd_aux: u32, |
| vd_next: u32, |
| } |
| |
| /// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. |
| /// |
| /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS |
| #[repr(C)] |
| #[derive(Debug, Pread)] |
| struct ElfVerdaux { |
| vda_name: u32, |
| vda_next: u32, |
| } |
| |
| /// An ELF `Version Need` entry Elfxx_Verneed. |
| /// |
| /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG |
| #[repr(C)] |
| #[derive(Debug, Pread)] |
| struct ElfVerneed { |
| vn_version: u16, |
| vn_cnt: u16, |
| vn_file: u32, |
| vn_aux: u32, |
| vn_next: u32, |
| } |
| |
| /// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. |
| /// |
| /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG |
| #[repr(C)] |
| #[derive(Debug, Pread)] |
| struct ElfVernaux { |
| vna_hash: u32, |
| vna_flags: u16, |
| vna_other: u16, |
| vna_name: u32, |
| vna_next: u32, |
| } |
| |
| /******************** |
| * Symbol Version * |
| ********************/ |
| |
| /// Helper struct to iterate over [Symbol Version][Versym] entries. |
| #[derive(Debug)] |
| pub struct VersymSection<'a> { |
| bytes: &'a [u8], |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> VersymSection<'a> { |
| pub fn parse( |
| bytes: &'a [u8], |
| shdrs: &[SectionHeader], |
| ctx: container::Ctx, |
| ) -> Result<Option<VersymSection<'a>>> { |
| // Get fields needed from optional `symbol version` section. |
| let (offset, size) = |
| if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERSYM) { |
| (shdr.sh_offset as usize, shdr.sh_size as usize) |
| } else { |
| return Ok(None); |
| }; |
| |
| // Get a slice of bytes of the `version definition` section content. |
| let bytes: &'a [u8] = bytes.pread_with(offset, size)?; |
| |
| Ok(Some(VersymSection { bytes, ctx })) |
| } |
| |
| /// Get an iterator over the [`Versym`] entries. |
| #[inline] |
| pub fn iter(&'a self) -> VersymIter<'a> { |
| self.into_iter() |
| } |
| |
| /// True if there are no [`Versym`] entries. |
| #[inline] |
| pub fn is_empty(&self) -> bool { |
| self.bytes.is_empty() |
| } |
| |
| /// Number of [`Versym`] entries. |
| #[inline] |
| pub fn len(&self) -> usize { |
| let entsize = core::mem::size_of::<ElfVersym>(); |
| |
| self.bytes.len() / entsize |
| } |
| |
| /// Get [`Versym`] entry at index. |
| #[inline] |
| pub fn get_at(&self, idx: usize) -> Option<Versym> { |
| let entsize = core::mem::size_of::<ElfVersym>(); |
| let offset = idx.checked_mul(entsize)?; |
| |
| self.bytes |
| .pread_with::<ElfVersym>(offset, self.ctx.le) |
| .ok() |
| .map(Versym::from) |
| } |
| } |
| |
| impl<'a> IntoIterator for &'_ VersymSection<'a> { |
| type Item = <VersymIter<'a> as Iterator>::Item; |
| type IntoIter = VersymIter<'a>; |
| |
| fn into_iter(self) -> Self::IntoIter { |
| VersymIter { |
| bytes: self.bytes, |
| offset: 0, |
| ctx: self.ctx, |
| } |
| } |
| } |
| |
| /// Iterator over the [`Versym`] entries from the [`SHT_GNU_VERSYM`] section. |
| pub struct VersymIter<'a> { |
| bytes: &'a [u8], |
| offset: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Iterator for VersymIter<'a> { |
| type Item = Versym; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.offset >= self.bytes.len() { |
| None |
| } else { |
| self.bytes |
| .gread_with::<ElfVersym>(&mut self.offset, self.ctx.le) |
| .ok() |
| .map(Versym::from) |
| .or_else(|| { |
| // self.bytes are not a multiple of ElfVersym. |
| // Adjust offset to continue yielding Nones. |
| self.offset = self.bytes.len(); |
| None |
| }) |
| } |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = (self.bytes.len() - self.offset) / core::mem::size_of::<Self::Item>(); |
| (len, Some(len)) |
| } |
| } |
| |
| impl ExactSizeIterator for VersymIter<'_> {} |
| |
| impl FusedIterator for VersymIter<'_> {} |
| |
| /// An ELF [Symbol Version][lsb-versym] entry. |
| /// |
| /// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL |
| #[derive(Debug)] |
| pub struct Versym { |
| pub vs_val: u16, |
| } |
| |
| impl Versym { |
| /// Returns true if the symbol is local and not available outside the object according to |
| /// [`VER_NDX_LOCAL`]. |
| #[inline] |
| pub fn is_local(&self) -> bool { |
| self.vs_val == VER_NDX_LOCAL |
| } |
| |
| /// Returns true if the symbol is defined in this object and globally available according |
| /// to [`VER_NDX_GLOBAL`]. |
| #[inline] |
| pub fn is_global(&self) -> bool { |
| self.vs_val == VER_NDX_GLOBAL |
| } |
| |
| /// Returns true if the `hidden` bit is set according to the [`VERSYM_HIDDEN`] bitmask. |
| #[inline] |
| pub fn is_hidden(&self) -> bool { |
| (self.vs_val & VERSYM_HIDDEN) == VERSYM_HIDDEN |
| } |
| |
| /// Returns the symbol version index according to the [`VERSYM_VERSION`] bitmask. |
| #[inline] |
| pub fn version(&self) -> u16 { |
| self.vs_val & VERSYM_VERSION |
| } |
| } |
| |
| impl From<ElfVersym> for Versym { |
| fn from(ElfVersym { vs_val }: ElfVersym) -> Self { |
| Versym { vs_val } |
| } |
| } |
| |
| /************************ |
| * Version Definition * |
| ************************/ |
| |
| /// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition |
| /// Auxiliary][Verdaux] entries. |
| #[derive(Debug)] |
| pub struct VerdefSection<'a> { |
| /// String table used to resolve version strings. |
| bytes: &'a [u8], |
| count: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> VerdefSection<'a> { |
| pub fn parse( |
| bytes: &'a [u8], |
| shdrs: &[SectionHeader], |
| ctx: container::Ctx, |
| ) -> Result<Option<VerdefSection<'a>>> { |
| // Get fields needed from optional `version definition` section. |
| let (offset, size, count) = |
| if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { |
| ( |
| shdr.sh_offset as usize, |
| shdr.sh_size as usize, |
| shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. |
| ) |
| } else { |
| return Ok(None); |
| }; |
| |
| // Get a slice of bytes of the `version definition` section content. |
| let bytes: &'a [u8] = bytes.pread_with(offset, size)?; |
| |
| Ok(Some(VerdefSection { bytes, count, ctx })) |
| } |
| |
| /// Get an iterator over the [`Verdef`] entries. |
| #[inline] |
| pub fn iter(&'a self) -> VerdefIter<'a> { |
| self.into_iter() |
| } |
| } |
| |
| impl<'a> IntoIterator for &'_ VerdefSection<'a> { |
| type Item = <VerdefIter<'a> as Iterator>::Item; |
| type IntoIter = VerdefIter<'a>; |
| |
| #[inline] |
| fn into_iter(self) -> Self::IntoIter { |
| VerdefIter { |
| bytes: self.bytes, |
| count: self.count, |
| index: 0, |
| offset: 0, |
| ctx: self.ctx, |
| } |
| } |
| } |
| |
| /// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. |
| pub struct VerdefIter<'a> { |
| bytes: &'a [u8], |
| count: usize, |
| index: usize, |
| offset: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Iterator for VerdefIter<'a> { |
| type Item = Verdef<'a>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.index >= self.count { |
| None |
| } else { |
| self.index += 1; |
| |
| let do_next = |iter: &mut Self| { |
| let ElfVerdef { |
| vd_version, |
| vd_flags, |
| vd_ndx, |
| vd_cnt, |
| vd_hash, |
| vd_aux, |
| vd_next, |
| } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; |
| |
| // Validate offset to first ElfVerdaux entry. |
| let offset = iter.offset.checked_add(vd_aux as usize)?; |
| |
| // Validate if offset is valid index into bytes slice. |
| if offset >= iter.bytes.len() { |
| return None; |
| } |
| |
| // Get a slice of bytes starting with the first ElfVerdaux entry. |
| let bytes: &'a [u8] = &iter.bytes[offset..]; |
| |
| // Bump the offset to the next ElfVerdef entry. |
| iter.offset = iter.offset.checked_add(vd_next as usize)?; |
| |
| // Start yielding None on the next call if there is no next offset. |
| if vd_next == 0 { |
| iter.index = iter.count; |
| } |
| |
| Some(Verdef { |
| vd_version, |
| vd_flags, |
| vd_ndx, |
| vd_cnt, |
| vd_hash, |
| vd_aux, |
| vd_next, |
| bytes, |
| ctx: iter.ctx, |
| }) |
| }; |
| |
| do_next(self).or_else(|| { |
| // Adjust current index to count in case of an error. |
| self.index = self.count; |
| None |
| }) |
| } |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = self.count - self.index; |
| (0, Some(len)) |
| } |
| } |
| |
| impl ExactSizeIterator for VerdefIter<'_> {} |
| |
| impl FusedIterator for VerdefIter<'_> {} |
| |
| /// An ELF [Version Definition][lsb-verdef] entry . |
| /// |
| /// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES |
| #[derive(Debug)] |
| pub struct Verdef<'a> { |
| /// Version revision. This field shall be set to 1. |
| pub vd_version: u16, |
| /// Version information flag bitmask. |
| pub vd_flags: u16, |
| /// Version index numeric value referencing the SHT_GNU_versym section. |
| pub vd_ndx: u16, |
| /// Number of associated verdaux array entries. |
| pub vd_cnt: u16, |
| /// Version name hash value (ELF hash function). |
| pub vd_hash: u32, |
| /// Offset in bytes to a corresponding entry in an array of Elfxx_Verdaux structures. |
| pub vd_aux: u32, |
| /// Offset to the next verdef entry, in bytes. |
| pub vd_next: u32, |
| |
| bytes: &'a [u8], |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Verdef<'a> { |
| /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. |
| #[inline] |
| pub fn iter(&'a self) -> VerdauxIter<'a> { |
| self.into_iter() |
| } |
| } |
| |
| impl<'a> IntoIterator for &'_ Verdef<'a> { |
| type Item = <VerdauxIter<'a> as Iterator>::Item; |
| type IntoIter = VerdauxIter<'a>; |
| |
| fn into_iter(self) -> Self::IntoIter { |
| VerdauxIter { |
| bytes: self.bytes, |
| count: self.vd_cnt, |
| index: 0, |
| offset: 0, |
| ctx: self.ctx, |
| } |
| } |
| } |
| |
| /// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. |
| pub struct VerdauxIter<'a> { |
| bytes: &'a [u8], |
| count: u16, |
| index: u16, |
| offset: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Iterator for VerdauxIter<'a> { |
| type Item = Verdaux; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.index >= self.count { |
| None |
| } else { |
| self.index += 1; |
| |
| let do_next = |iter: &mut Self| { |
| let ElfVerdaux { vda_name, vda_next } = |
| iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; |
| |
| // Bump the offset to the next ElfVerdaux entry. |
| iter.offset = iter.offset.checked_add(vda_next as usize)?; |
| |
| // Start yielding None on the next call if there is no next offset. |
| if vda_next == 0 { |
| iter.index = iter.count; |
| } |
| |
| Some(Verdaux { |
| vda_name: vda_name as usize, |
| vda_next, |
| }) |
| }; |
| |
| do_next(self).or_else(|| { |
| // Adjust current index to count in case of an error. |
| self.index = self.count; |
| None |
| }) |
| } |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = usize::from(self.count - self.index); |
| (0, Some(len)) |
| } |
| } |
| |
| impl ExactSizeIterator for VerdauxIter<'_> {} |
| |
| impl FusedIterator for VerdauxIter<'_> {} |
| |
| /// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. |
| /// |
| /// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS |
| #[derive(Debug)] |
| pub struct Verdaux { |
| /// Offset to the version or dependency name string in the section header, in bytes. |
| pub vda_name: usize, |
| /// Offset to the next verdaux entry, in bytes. |
| pub vda_next: u32, |
| } |
| |
| /************************** |
| * Version Requirements * |
| **************************/ |
| |
| /// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed |
| /// Auxiliary][Vernaux] entries. |
| #[derive(Debug)] |
| pub struct VerneedSection<'a> { |
| bytes: &'a [u8], |
| count: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> VerneedSection<'a> { |
| /// Try to parse the optional [`SHT_GNU_VERNEED`] section. |
| pub fn parse( |
| bytes: &'a [u8], |
| shdrs: &[SectionHeader], |
| ctx: container::Ctx, |
| ) -> Result<Option<VerneedSection<'a>>> { |
| // Get fields needed from optional `version needed` section. |
| let (offset, size, count) = |
| if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { |
| ( |
| shdr.sh_offset as usize, |
| shdr.sh_size as usize, |
| shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. |
| ) |
| } else { |
| return Ok(None); |
| }; |
| |
| // Get a slice of bytes of the `version needed` section content. |
| let bytes: &'a [u8] = bytes.pread_with(offset, size)?; |
| |
| Ok(Some(VerneedSection { bytes, count, ctx })) |
| } |
| |
| /// Get an iterator over the [`Verneed`] entries. |
| #[inline] |
| pub fn iter(&'a self) -> VerneedIter<'a> { |
| self.into_iter() |
| } |
| } |
| |
| impl<'a> IntoIterator for &'_ VerneedSection<'a> { |
| type Item = <VerneedIter<'a> as Iterator>::Item; |
| type IntoIter = VerneedIter<'a>; |
| |
| #[inline] |
| fn into_iter(self) -> Self::IntoIter { |
| VerneedIter { |
| bytes: self.bytes, |
| count: self.count, |
| index: 0, |
| offset: 0, |
| ctx: self.ctx, |
| } |
| } |
| } |
| |
| /// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. |
| pub struct VerneedIter<'a> { |
| bytes: &'a [u8], |
| count: usize, |
| index: usize, |
| offset: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Iterator for VerneedIter<'a> { |
| type Item = Verneed<'a>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.index >= self.count { |
| None |
| } else { |
| self.index += 1; |
| |
| let do_next = |iter: &mut Self| { |
| let ElfVerneed { |
| vn_version, |
| vn_cnt, |
| vn_file, |
| vn_aux, |
| vn_next, |
| } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; |
| |
| // Validate offset to first ElfVernaux entry. |
| let offset = iter.offset.checked_add(vn_aux as usize)?; |
| |
| // Validate if offset is valid index into bytes slice. |
| if offset >= iter.bytes.len() { |
| return None; |
| } |
| |
| // Get a slice of bytes starting with the first ElfVernaux entry. |
| let bytes: &'a [u8] = &iter.bytes[offset..]; |
| |
| // Bump the offset to the next ElfVerneed entry. |
| iter.offset = iter.offset.checked_add(vn_next as usize)?; |
| |
| // Start yielding None on the next call if there is no next offset. |
| if vn_next == 0 { |
| iter.index = iter.count; |
| } |
| |
| Some(Verneed { |
| vn_version, |
| vn_cnt, |
| vn_file: vn_file as usize, |
| vn_aux, |
| vn_next, |
| bytes, |
| ctx: iter.ctx, |
| }) |
| }; |
| |
| do_next(self).or_else(|| { |
| // Adjust current index to count in case of an error. |
| self.index = self.count; |
| None |
| }) |
| } |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = self.count - self.index; |
| (0, Some(len)) |
| } |
| } |
| |
| impl ExactSizeIterator for VerneedIter<'_> {} |
| |
| impl FusedIterator for VerneedIter<'_> {} |
| |
| /// An ELF [Version Need][lsb-verneed] entry . |
| /// |
| /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG |
| #[derive(Debug)] |
| pub struct Verneed<'a> { |
| /// Version of structure. This value is currently set to 1, and will be reset if the versioning |
| /// implementation is incompatibly altered. |
| pub vn_version: u16, |
| /// Number of associated verneed array entries. |
| pub vn_cnt: u16, |
| /// Offset to the file name string in the section header, in bytes. |
| pub vn_file: usize, |
| /// Offset to a corresponding entry in the vernaux array, in bytes. |
| pub vn_aux: u32, |
| /// Offset to the next verneed entry, in bytes. |
| pub vn_next: u32, |
| |
| bytes: &'a [u8], |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Verneed<'a> { |
| /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. |
| #[inline] |
| pub fn iter(&'a self) -> VernauxIter<'a> { |
| self.into_iter() |
| } |
| } |
| |
| impl<'a> IntoIterator for &'_ Verneed<'a> { |
| type Item = <VernauxIter<'a> as Iterator>::Item; |
| type IntoIter = VernauxIter<'a>; |
| |
| #[inline] |
| fn into_iter(self) -> Self::IntoIter { |
| VernauxIter { |
| bytes: self.bytes, |
| count: self.vn_cnt, |
| index: 0, |
| offset: 0, |
| ctx: self.ctx, |
| } |
| } |
| } |
| |
| /// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. |
| pub struct VernauxIter<'a> { |
| bytes: &'a [u8], |
| count: u16, |
| index: u16, |
| offset: usize, |
| ctx: container::Ctx, |
| } |
| |
| impl<'a> Iterator for VernauxIter<'a> { |
| type Item = Vernaux; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.index >= self.count { |
| None |
| } else { |
| self.index += 1; |
| |
| let do_next = |iter: &mut Self| { |
| let ElfVernaux { |
| vna_hash, |
| vna_flags, |
| vna_other, |
| vna_name, |
| vna_next, |
| } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; |
| |
| // Bump the offset to the next ElfVernaux entry. |
| iter.offset = iter.offset.checked_add(vna_next as usize)?; |
| |
| // Start yielding None on the next call if there is no next offset. |
| if vna_next == 0 { |
| iter.index = iter.count; |
| } |
| |
| Some(Vernaux { |
| vna_hash, |
| vna_flags, |
| vna_other, |
| vna_name: vna_name as usize, |
| vna_next, |
| }) |
| }; |
| |
| do_next(self).or_else(|| { |
| // Adjust current index to count in case of an error. |
| self.index = self.count; |
| None |
| }) |
| } |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = usize::from(self.count - self.index); |
| (0, Some(len)) |
| } |
| } |
| |
| impl ExactSizeIterator for VernauxIter<'_> {} |
| |
| impl FusedIterator for VernauxIter<'_> {} |
| |
| /// An ELF [Version Need Auxiliary][lsb-vernaux] entry. |
| /// |
| /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG |
| #[derive(Debug)] |
| pub struct Vernaux { |
| /// Dependency name hash value (ELF hash function). |
| pub vna_hash: u32, |
| /// Dependency information flag bitmask. |
| pub vna_flags: u16, |
| /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 |
| /// controls whether or not the object is hidden; if this bit is set, the object cannot be used |
| /// and the static linker will ignore the symbol's presence in the object. |
| pub vna_other: u16, |
| /// Offset to the dependency name string in the section header, in bytes. |
| pub vna_name: usize, |
| /// Offset to the next vernaux entry, in bytes. |
| pub vna_next: u32, |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed, ElfVersym}; |
| use super::{Versym, VERSYM_HIDDEN, VER_NDX_GLOBAL, VER_NDX_LOCAL}; |
| use core::mem::size_of; |
| |
| #[test] |
| fn check_size() { |
| assert_eq!(2, size_of::<ElfVersym>()); |
| assert_eq!(20, size_of::<ElfVerdef>()); |
| assert_eq!(8, size_of::<ElfVerdaux>()); |
| assert_eq!(16, size_of::<ElfVerneed>()); |
| assert_eq!(16, size_of::<ElfVernaux>()); |
| } |
| |
| #[test] |
| fn check_versym() { |
| let local = Versym { |
| vs_val: VER_NDX_LOCAL, |
| }; |
| assert_eq!(true, local.is_local()); |
| assert_eq!(false, local.is_global()); |
| assert_eq!(false, local.is_hidden()); |
| assert_eq!(VER_NDX_LOCAL, local.version()); |
| |
| let global = Versym { |
| vs_val: VER_NDX_GLOBAL, |
| }; |
| assert_eq!(false, global.is_local()); |
| assert_eq!(true, global.is_global()); |
| assert_eq!(false, global.is_hidden()); |
| assert_eq!(VER_NDX_GLOBAL, global.version()); |
| |
| let hidden = Versym { |
| vs_val: VERSYM_HIDDEN, |
| }; |
| assert_eq!(false, hidden.is_local()); |
| assert_eq!(false, hidden.is_global()); |
| assert_eq!(true, hidden.is_hidden()); |
| assert_eq!(0, hidden.version()); |
| |
| let hidden = Versym { |
| vs_val: VERSYM_HIDDEN | 0x123, |
| }; |
| assert_eq!(false, hidden.is_local()); |
| assert_eq!(false, hidden.is_global()); |
| assert_eq!(true, hidden.is_hidden()); |
| assert_eq!(0x123, hidden.version()); |
| } |
| } |