use crate::{
ParseDynamicError, Result,
elf::{ElfLayout, ElfSymbol, SymbolLookup, SymbolTableView},
memory::{ImageMemory, MappedView, RegionAccess, VmAddr},
segment::ElfSegments,
};
use core::mem::size_of;
use super::sysv_hash;
#[repr(C)]
#[derive(Clone, Copy)]
struct ElfHashHeader {
nbucket: u32,
nchain: u32,
}
impl ElfHashHeader {
#[inline]
fn from_bytes(bytes: [u8; size_of::<Self>()]) -> Self {
let [n0, n1, n2, n3, c0, c1, c2, c3] = bytes;
Self {
nbucket: u32::from_ne_bytes([n0, n1, n2, n3]),
nchain: u32::from_ne_bytes([c0, c1, c2, c3]),
}
}
}
#[derive(Clone)]
pub(crate) struct ElfHash {
header: ElfHashHeader,
buckets: MappedView<u32>,
chains: MappedView<u32>,
}
impl ElfHash {
#[inline]
pub(crate) fn parse<R: RegionAccess>(
segments: &ElfSegments<R>,
addr: VmAddr,
) -> Result<ElfHash> {
const HEADER_SIZE: usize = size_of::<ElfHashHeader>();
let start = addr
.checked_offset_from(segments.base())
.ok_or(ParseDynamicError::AddressOverflow)?;
let mut bytes = [0u8; HEADER_SIZE];
segments.read_bytes(addr, &mut bytes)?;
let header = ElfHashHeader::from_bytes(bytes);
if header.nbucket == 0 {
return Err(ParseDynamicError::EmptyHashTable {
table: "DT_HASH bucket table",
}
.into());
}
let bucket_size = (header.nbucket as usize)
.checked_mul(size_of::<u32>())
.ok_or(ParseDynamicError::AddressOverflow)?;
let buckets_off = start
.checked_add(HEADER_SIZE)
.ok_or(ParseDynamicError::AddressOverflow)?;
let chains_off = buckets_off
.checked_add(bucket_size)
.ok_or(ParseDynamicError::AddressOverflow)?;
let buckets = segments.read_view(buckets_off, bucket_size).ok_or(
ParseDynamicError::MalformedHashTable {
detail: "DT_HASH bucket table size is malformed",
},
)?;
let chain_size = (header.nchain as usize)
.checked_mul(size_of::<u32>())
.ok_or(ParseDynamicError::AddressOverflow)?;
let chains = segments.read_view(chains_off, chain_size).ok_or(
ParseDynamicError::MalformedHashTable {
detail: "DT_HASH chain table size is malformed",
},
)?;
Ok(ElfHash {
header,
buckets,
chains,
})
}
}
impl ElfHash {
pub(crate) fn for_each<L: ElfLayout, H>(
&self,
table: SymbolTableView<'_, L, H>,
visitor: &mut dyn FnMut(&ElfSymbol<L>),
) {
for index in 0..self.header.nchain as usize {
let Some(symbol) = table.get_raw(index) else {
break;
};
visitor(symbol);
}
}
pub(crate) fn lookup<'sym, L: ElfLayout, H>(
&self,
table: SymbolTableView<'sym, L, H>,
lookup: &mut SymbolLookup<'_>,
) -> Option<&'sym ElfSymbol<L>> {
let hash = lookup.sysv_hash(|name| sysv_hash(name.as_bytes()));
let buckets = self.buckets.as_slice();
let chains = self.chains.as_slice();
let bucket_idx = (hash as usize) % self.header.nbucket as usize;
let mut chain_idx = *buckets.get(bucket_idx)? as usize;
loop {
if chain_idx == 0 {
return None;
}
let next_chain = *chains.get(chain_idx)? as usize;
let cur_symbol = table.get_raw(chain_idx)?;
let sym_name = table.strtab.get_str(cur_symbol.st_name());
#[cfg(feature = "version")]
if sym_name == lookup.name() && table.check_match(chain_idx, lookup.version()) {
return Some(cur_symbol);
}
#[cfg(not(feature = "version"))]
if sym_name == lookup.name() {
return Some(cur_symbol);
}
chain_idx = next_chain;
}
}
}