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;
pub const VER_NDX_LOCAL: u16 = 0;
pub const VER_NDX_GLOBAL: u16 = 1;
pub const VERSYM_HIDDEN: u16 = 0x8000;
pub const VERSYM_VERSION: u16 = 0x7fff;
pub const VER_FLG_BASE: u16 = 0x1;
pub const VER_FLG_WEAK: u16 = 0x2;
pub const VER_FLG_INFO: u16 = 0x4;
#[repr(C)]
#[derive(Debug, Pread)]
struct ElfVersym {
vs_val: u16,
}
#[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,
}
#[repr(C)]
#[derive(Debug, Pread)]
struct ElfVerdaux {
vda_name: u32,
vda_next: u32,
}
#[repr(C)]
#[derive(Debug, Pread)]
struct ElfVerneed {
vn_version: u16,
vn_cnt: u16,
vn_file: u32,
vn_aux: u32,
vn_next: u32,
}
#[repr(C)]
#[derive(Debug, Pread)]
struct ElfVernaux {
vna_hash: u32,
vna_flags: u16,
vna_other: u16,
vna_name: u32,
vna_next: u32,
}
#[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>>> {
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);
};
let bytes: &'a [u8] = bytes.pread_with(offset, size)?;
Ok(Some(VersymSection { bytes, ctx }))
}
#[inline]
pub fn iter(&'a self) -> VersymIter<'a> {
self.into_iter()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
let entsize = core::mem::size_of::<ElfVersym>();
self.bytes.len() / entsize
}
#[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,
}
}
}
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.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<'_> {}
#[derive(Debug)]
pub struct Versym {
pub vs_val: u16,
}
impl Versym {
#[inline]
pub fn is_local(&self) -> bool {
self.vs_val == VER_NDX_LOCAL
}
#[inline]
pub fn is_global(&self) -> bool {
self.vs_val == VER_NDX_GLOBAL
}
#[inline]
pub fn is_hidden(&self) -> bool {
(self.vs_val & VERSYM_HIDDEN) == VERSYM_HIDDEN
}
#[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 }
}
}
#[derive(Debug)]
pub struct VerdefSection<'a> {
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>>> {
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, )
} else {
return Ok(None);
};
let bytes: &'a [u8] = bytes.pread_with(offset, size)?;
Ok(Some(VerdefSection { bytes, count, ctx }))
}
#[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,
}
}
}
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()?;
let offset = iter.offset.checked_add(vd_aux as usize)?;
if offset >= iter.bytes.len() {
return None;
}
let bytes: &'a [u8] = &iter.bytes[offset..];
iter.offset = iter.offset.checked_add(vd_next as usize)?;
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(|| {
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<'_> {}
#[derive(Debug)]
pub struct Verdef<'a> {
pub vd_version: u16,
pub vd_flags: u16,
pub vd_ndx: u16,
pub vd_cnt: u16,
pub vd_hash: u32,
pub vd_aux: u32,
pub vd_next: u32,
bytes: &'a [u8],
ctx: container::Ctx,
}
impl<'a> Verdef<'a> {
#[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,
}
}
}
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()?;
iter.offset = iter.offset.checked_add(vda_next as usize)?;
if vda_next == 0 {
iter.index = iter.count;
}
Some(Verdaux {
vda_name: vda_name as usize,
vda_next,
})
};
do_next(self).or_else(|| {
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<'_> {}
#[derive(Debug)]
pub struct Verdaux {
pub vda_name: usize,
pub vda_next: u32,
}
#[derive(Debug)]
pub struct VerneedSection<'a> {
bytes: &'a [u8],
count: usize,
ctx: container::Ctx,
}
impl<'a> VerneedSection<'a> {
pub fn parse(
bytes: &'a [u8],
shdrs: &[SectionHeader],
ctx: container::Ctx,
) -> Result<Option<VerneedSection<'a>>> {
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, )
} else {
return Ok(None);
};
let bytes: &'a [u8] = bytes.pread_with(offset, size)?;
Ok(Some(VerneedSection { bytes, count, ctx }))
}
#[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,
}
}
}
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()?;
let offset = iter.offset.checked_add(vn_aux as usize)?;
if offset >= iter.bytes.len() {
return None;
}
let bytes: &'a [u8] = &iter.bytes[offset..];
iter.offset = iter.offset.checked_add(vn_next as usize)?;
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(|| {
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<'_> {}
#[derive(Debug)]
pub struct Verneed<'a> {
pub vn_version: u16,
pub vn_cnt: u16,
pub vn_file: usize,
pub vn_aux: u32,
pub vn_next: u32,
bytes: &'a [u8],
ctx: container::Ctx,
}
impl<'a> Verneed<'a> {
#[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,
}
}
}
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()?;
iter.offset = iter.offset.checked_add(vna_next as usize)?;
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(|| {
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<'_> {}
#[derive(Debug)]
pub struct Vernaux {
pub vna_hash: u32,
pub vna_flags: u16,
pub vna_other: u16,
pub vna_name: usize,
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());
}
}