use crate::elf::{ElfStringTable, SymbolTable};
use alloc::vec::Vec;
use core::num::NonZeroUsize;
use elf::abi;
#[repr(C)]
struct VerDef {
vd_version: u16,
vd_flags: u16,
vd_ndx: u16,
vd_cnt: u16,
vd_hash: u32,
vd_aux: u32,
vd_next: u32,
}
impl VerDef {
fn index(&self) -> usize {
(self.vd_ndx & abi::VER_NDX_VERSION) as usize
}
}
#[repr(C)]
struct VerDefAux {
vda_name: u32,
vda_next: u32,
}
#[repr(C)]
struct VerNeed {
vn_version: u16,
vn_cnt: u16,
vn_file: u32,
vn_aux: u32,
vn_next: u32,
}
#[repr(C)]
struct VerNeedAux {
vna_hash: u32,
vna_flags: u16,
vna_other: u16,
vna_name: u32,
vna_next: u32,
}
impl VerNeedAux {
fn index(&self) -> usize {
(self.vna_other & abi::VER_NDX_VERSION) as usize
}
}
struct VersionIndex(u16);
impl VersionIndex {
fn index(&self) -> u16 {
self.0 & abi::VER_NDX_VERSION
}
pub fn is_hidden(&self) -> bool {
(self.0 & abi::VER_NDX_HIDDEN) != 0
}
}
#[derive(Clone)]
struct VersionIndexTable {
ptr: *const VersionIndex,
}
impl core::fmt::Debug for VersionIndexTable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VersionIndexTable")
.field("ptr", &self.ptr)
.finish()
}
}
impl VersionIndexTable {
fn get(&self, sym_idx: usize) -> &VersionIndex {
unsafe { &*self.ptr.add(sym_idx) }
}
}
struct VerNeedAuxIterator {
ptr: *const VerNeedAux,
count: usize,
num: usize,
}
impl Iterator for VerNeedAuxIterator {
type Item = VerNeedAux;
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.num {
let verneed_aux = unsafe { self.ptr.read() };
self.ptr = unsafe { self.ptr.add(1) };
self.count += 1;
Some(verneed_aux)
} else {
None
}
}
}
#[derive(Clone)]
struct VerNeedTable {
ptr: *const VerNeed,
num: usize,
}
struct VerNeedIterator {
ptr: *const VerNeed,
count: usize,
num: usize,
}
impl Iterator for VerNeedIterator {
type Item = (VerNeed, VerNeedAuxIterator);
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.num {
let verneed = unsafe { self.ptr.read() };
let verneed_aux = VerNeedAuxIterator {
ptr: unsafe { self.ptr.byte_add(verneed.vn_aux as usize) } as *const VerNeedAux,
count: 0,
num: verneed.vn_cnt as usize,
};
self.ptr = unsafe { self.ptr.byte_add(verneed.vn_next as usize) };
self.count += 1;
Some((verneed, verneed_aux))
} else {
None
}
}
}
impl IntoIterator for &VerNeedTable {
type IntoIter = VerNeedIterator;
type Item = (VerNeed, VerNeedAuxIterator);
fn into_iter(self) -> Self::IntoIter {
VerNeedIterator {
ptr: self.ptr,
count: 0,
num: self.num,
}
}
}
#[derive(Clone)]
struct VerDefTable {
ptr: *const VerDef,
num: usize,
}
struct VerDefIterator {
ptr: *const VerDef,
count: usize,
num: usize,
}
struct VerDefAuxIterator {
ptr: *const VerDefAux,
count: usize,
num: usize,
}
impl Iterator for VerDefAuxIterator {
type Item = VerDefAux;
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.num {
let verdef_aux = unsafe { self.ptr.read() };
self.ptr = unsafe { self.ptr.add(1) };
self.count += 1;
Some(verdef_aux)
} else {
None
}
}
}
impl Iterator for VerDefIterator {
type Item = (VerDef, VerDefAuxIterator);
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.num {
let verdef = unsafe { self.ptr.read() };
let verdef_aux = VerDefAuxIterator {
ptr: unsafe { self.ptr.byte_add(verdef.vd_aux as usize) } as *const VerDefAux,
count: 0,
num: verdef.vd_cnt as usize,
};
self.ptr = unsafe { self.ptr.byte_add(verdef.vd_next as usize) };
self.count += 1;
Some((verdef, verdef_aux))
} else {
None
}
}
}
impl IntoIterator for &VerDefTable {
type IntoIter = VerDefIterator;
type Item = (VerDef, VerDefAuxIterator);
fn into_iter(self) -> Self::IntoIter {
VerDefIterator {
ptr: self.ptr,
count: 0,
num: self.num,
}
}
}
struct Version {
name: &'static str,
hash: u32,
}
impl core::fmt::Debug for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Version")
.field("name", &self.name)
.field("hash", &format_args!("0x{:x}", self.hash))
.finish()
}
}
pub(crate) struct ELFVersion {
version_ids: VersionIndexTable,
versions: Vec<Version>,
}
impl core::fmt::Debug for ELFVersion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ELFVersion")
.field("version_ids", &self.version_ids)
.field("versions", &self.versions)
.finish()
}
}
impl ELFVersion {
pub(crate) fn new(
version_ids_off: Option<NonZeroUsize>,
verneeds: Option<(NonZeroUsize, NonZeroUsize)>,
verdefs: Option<(NonZeroUsize, NonZeroUsize)>,
strtab: &ElfStringTable,
) -> Option<ELFVersion> {
let version_ids_off = version_ids_off?;
let mut versions = Vec::new();
let mut ndx_max = 0;
if let Some((ptr, num)) = verdefs {
let verdef_table = VerDefTable {
ptr: ptr.get() as _,
num: num.get(),
};
for (verdef, _) in verdef_table.into_iter() {
if ndx_max < verdef.index() {
ndx_max = verdef.index();
}
}
}
if let Some((ptr, num)) = verneeds {
let verneed_table = VerNeedTable {
ptr: ptr.get() as _,
num: num.get(),
};
for (_, vna_iter) in verneed_table.into_iter() {
for aux in vna_iter {
if ndx_max < aux.index() {
ndx_max = aux.index();
}
}
}
}
versions.reserve(ndx_max + 1);
unsafe { versions.set_len(ndx_max + 1) };
if let Some((ptr, num)) = verdefs {
let verdef_table = VerDefTable {
ptr: ptr.get() as _,
num: num.get(),
};
for (verdef, mut vd_iter) in verdef_table.into_iter() {
let name = strtab.get_str(vd_iter.next().unwrap().vda_name as usize);
versions[verdef.index()] = Version {
name,
hash: verdef.vd_hash,
};
}
}
if let Some((ptr, num)) = verneeds {
let verneed_table = VerNeedTable {
ptr: ptr.get() as _,
num: num.get(),
};
for (_, vna_iter) in verneed_table.into_iter() {
for aux in vna_iter {
let name = strtab.get_str(aux.vna_name as usize);
versions[aux.index()] = Version {
name,
hash: aux.vna_hash,
};
}
}
}
Some(ELFVersion {
version_ids: VersionIndexTable {
ptr: version_ids_off.get() as _,
},
versions,
})
}
}
pub(crate) struct SymbolVersion<'a> {
name: &'a str,
hash: u32,
hidden: bool,
}
impl core::fmt::Debug for SymbolVersion<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SymbolVersion")
.field("name", &self.name)
.field("hash", &format_args!("0x{:x}", self.hash))
.field("hidden", &self.hidden)
.finish()
}
}
impl<'a> SymbolVersion<'a> {
fn dl_elf_hash(name: &str) -> u32 {
let bytes = name.as_bytes();
let mut hash: u32 = u32::from(bytes[0]);
if hash != 0 && bytes.len() > 1 {
hash = (hash << 4) + u32::from(bytes[1]);
if bytes.len() > 2 {
hash = (hash << 4) + u32::from(bytes[2]);
if bytes.len() > 3 {
hash = (hash << 4) + u32::from(bytes[3]);
if bytes.len() > 4 {
hash = (hash << 4) + u32::from(bytes[4]);
let mut name = &bytes[5..];
while let Some(&byte) = name.first() {
hash = (hash << 4) + u32::from(byte);
let hi = hash & 0xf0000000;
hash ^= hi >> 24;
name = &name[1..];
}
hash &= 0x0fffffff;
}
}
}
}
hash
}
pub(crate) fn new(name: &'a str) -> Self {
let hash = Self::dl_elf_hash(name);
SymbolVersion {
name,
hash,
hidden: true,
}
}
}
impl SymbolTable {
pub(crate) fn get_requirement(&self, sym_idx: usize) -> Option<SymbolVersion<'_>> {
if let Some(gnu_version) = &self.version {
let ver_ndx = gnu_version.version_ids.get(sym_idx);
if ver_ndx.index() <= 1 {
return None;
}
let hidden = ver_ndx.is_hidden();
let version = &gnu_version.versions[ver_ndx.index() as usize];
return Some(SymbolVersion {
name: version.name,
hash: version.hash,
hidden,
});
}
None
}
pub(crate) fn check_match(&self, sym_idx: usize, version: Option<&SymbolVersion>) -> bool {
if let Some(version) = version {
let gnu_version = self.version.as_ref().unwrap();
let ver_ndx = gnu_version.version_ids.get(sym_idx);
let def_hidden = ver_ndx.is_hidden();
let def_version = &gnu_version.versions[ver_ndx.index() as usize];
if (def_version.hash == version.hash && def_version.name == version.name)
|| (!version.hidden && !def_hidden)
{
return true;
}
return false;
}
true
}
}