diff options
Diffstat (limited to 'third_party/rust/goblin/src/elf/symver.rs')
-rw-r--r-- | third_party/rust/goblin/src/elf/symver.rs | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/elf/symver.rs b/third_party/rust/goblin/src/elf/symver.rs new file mode 100644 index 0000000000..0dd55e3d25 --- /dev/null +++ b/third_party/rust/goblin/src/elf/symver.rs @@ -0,0 +1,880 @@ +//! 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()); + } +} |