use alloc::vec::Vec;
use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable};
use crate::{elf, endian};
use super::FileHeader;
#[derive(Debug, Default, Clone, Copy)]
pub struct VersionIndex(pub u16);
impl VersionIndex {
pub fn index(&self) -> u16 {
self.0 & elf::VERSYM_VERSION
}
pub fn is_local(&self) -> bool {
self.index() == elf::VER_NDX_LOCAL
}
pub fn is_global(&self) -> bool {
self.index() == elf::VER_NDX_GLOBAL
}
pub fn is_hidden(&self) -> bool {
self.0 & elf::VERSYM_HIDDEN != 0
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Version<'data> {
name: &'data [u8],
hash: u32,
valid: bool,
}
impl<'data> Version<'data> {
pub fn name(&self) -> &'data [u8] {
self.name
}
pub fn hash(&self) -> u32 {
self.hash
}
}
#[derive(Debug, Clone)]
pub struct VersionTable<'data, Elf: FileHeader> {
symbols: &'data [elf::Versym<Elf::Endian>],
versions: Vec<Version<'data>>,
}
impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> {
fn default() -> Self {
VersionTable {
symbols: &[],
versions: Vec::new(),
}
}
}
impl<'data, Elf: FileHeader> VersionTable<'data, Elf> {
pub fn parse<R: ReadRef<'data>>(
endian: Elf::Endian,
versyms: &'data [elf::Versym<Elf::Endian>],
verdefs: Option<VerdefIterator<'data, Elf>>,
verneeds: Option<VerneedIterator<'data, Elf>>,
strings: StringTable<'data, R>,
) -> Result<Self> {
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;
}
}
}
}
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 {
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 {
continue;
}
versions[usize::from(index)] = Version {
name: vernaux.name(endian, strings)?,
hash: vernaux.vna_hash.get(endian),
valid: true,
};
}
}
}
Ok(VersionTable {
symbols: versyms,
versions,
})
}
pub fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
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),
None => elf::VER_NDX_GLOBAL,
};
VersionIndex(version_index)
}
pub fn version(&self, index: VersionIndex) -> Result<Option<&Version<'data>>> {
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)
}
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)) => {
false
}
(Some(_def), None) => {
!version_index.is_hidden()
}
(None, None) => true,
}
}
}
#[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),
}
}
pub fn next(
&mut self,
) -> Result<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> {
if self.data.is_empty() {
return Ok(None);
}
let verdef = self
.data
.read_at::<elf::Verdef<_>>(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)))
}
}
#[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,
}
}
pub fn next(&mut self) -> Result<Option<&'data elf::Verdaux<Elf::Endian>>> {
if self.count == 0 {
return Ok(None);
}
let verdaux = self
.data
.read_at::<elf::Verdaux<_>>(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))
}
}
#[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),
}
}
pub fn next(
&mut self,
) -> Result<
Option<(
&'data elf::Verneed<Elf::Endian>,
VernauxIterator<'data, Elf>,
)>,
> {
if self.data.is_empty() {
return Ok(None);
}
let verneed = self
.data
.read_at::<elf::Verneed<_>>(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)))
}
}
#[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,
}
}
pub fn next(&mut self) -> Result<Option<&'data elf::Vernaux<Elf::Endian>>> {
if self.count == 0 {
return Ok(None);
}
let vernaux = self
.data
.read_at::<elf::Vernaux<_>>(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<Endian: endian::Endian> elf::Verdaux<Endian> {
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<Endian: endian::Endian> elf::Verneed<Endian> {
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<Endian: endian::Endian> elf::Vernaux<Endian> {
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")
}
}