use super::defs::{ElfLayout, ElfSymbol, NativeElfLayout};
use super::hash::{PreCompute, SymbolHash};
use crate::{elf::HashTable, memory::MappedView};
use core::{ffi::CStr, fmt::Debug};
pub(crate) struct ElfStringTable {
view: MappedView<u8>,
}
impl Clone for ElfStringTable {
#[inline]
fn clone(&self) -> Self {
Self {
view: self.view.clone(),
}
}
}
impl ElfStringTable {
#[inline]
pub(crate) const fn new(view: MappedView<u8>) -> Self {
Self { view }
}
#[inline]
fn bytes_from(&self, offset: usize) -> &'static [u8] {
let bytes = self.view.as_slice();
assert!(
offset <= bytes.len(),
"ELF string table offset is out of bounds"
);
&bytes[offset..]
}
#[inline]
pub(crate) fn get_cstr(&self, offset: usize) -> &'static CStr {
let bytes = self.bytes_from(offset);
CStr::from_bytes_until_nul(bytes)
.expect("ELF string table entry is missing a NUL terminator")
}
#[inline]
fn convert_cstr(s: &CStr) -> &str {
core::str::from_utf8(s.to_bytes()).expect("ELF string table entry is not valid UTF-8")
}
#[inline]
pub(crate) fn get_str(&self, offset: usize) -> &'static str {
Self::convert_cstr(self.get_cstr(offset))
}
}
pub struct SymbolTableView<'symtab, L: ElfLayout = NativeElfLayout, H = HashTable<L>> {
pub(crate) hashtab: &'symtab H,
pub(crate) symbols: &'symtab [ElfSymbol<L>],
pub(crate) strtab: &'symtab ElfStringTable,
#[cfg(feature = "version")]
pub(crate) version: Option<&'symtab super::version::ELFVersion>,
}
impl<'symtab, L: ElfLayout, H> Copy for SymbolTableView<'symtab, L, H> {}
impl<'symtab, L: ElfLayout, H> Clone for SymbolTableView<'symtab, L, H> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
pub struct SymbolTable<L: ElfLayout = NativeElfLayout, H = HashTable<L>> {
pub(crate) hashtab: H,
pub(crate) symbols: &'static [ElfSymbol<L>],
pub(crate) strtab: ElfStringTable,
#[cfg(feature = "version")]
pub(crate) version: Option<super::version::ELFVersion>,
}
impl<L: ElfLayout, H: Debug> Debug for SymbolTable<L, H> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SymbolTable")
.field("hashtab", &self.hashtab)
.field("symbol_count", &self.symbols.len())
.finish()
}
}
unsafe impl<L: ElfLayout> Send for SymbolTable<L, HashTable<L>> {}
unsafe impl<L: ElfLayout> Sync for SymbolTable<L, HashTable<L>> {}
impl<L: ElfLayout, H: Clone> Clone for SymbolTable<L, H> {
#[inline]
fn clone(&self) -> Self {
Self {
hashtab: self.hashtab.clone(),
symbols: self.symbols,
strtab: self.strtab.clone(),
#[cfg(feature = "version")]
version: self.version.clone(),
}
}
}
#[derive(Clone)]
pub(crate) struct SymbolInfo<'symtab> {
name: &'symtab str,
#[cfg(feature = "version")]
version: Option<super::version::SymbolVersion<'symtab>>,
}
impl<'symtab> Debug for SymbolInfo<'symtab> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_struct("SymbolInfo");
debug.field("name", &self.name);
#[cfg(feature = "version")]
{
if let Some(v) = &self.version {
debug.field("version", v);
}
}
debug.finish()
}
}
impl<'symtab> SymbolInfo<'symtab> {
#[allow(unused_variables)]
pub(crate) fn from_str(name: &'symtab str, version: Option<&'symtab str>) -> Self {
SymbolInfo {
name,
#[cfg(feature = "version")]
version: version.map(super::version::SymbolVersion::new),
}
}
#[inline]
pub(crate) fn name(&self) -> &'symtab str {
self.name
}
#[cfg(feature = "version")]
pub(crate) fn version(&self) -> Option<&super::version::SymbolVersion<'symtab>> {
self.version.as_ref()
}
}
pub struct SymbolLookup<'symbol> {
info: SymbolInfo<'symbol>,
precompute: PreCompute,
}
impl<'symbol> SymbolLookup<'symbol> {
#[inline]
pub fn new(name: &'symbol str) -> Self {
Self::from_info(SymbolInfo::from_str(name, None))
}
#[cfg(feature = "version")]
#[inline]
pub fn with_version(name: &'symbol str, version: &'symbol str) -> Self {
Self::from_info(SymbolInfo::from_str(name, Some(version)))
}
#[inline]
pub(crate) fn from_info(info: SymbolInfo<'symbol>) -> Self {
let precompute = PreCompute::new(info.name());
Self { info, precompute }
}
#[inline]
pub fn name(&self) -> &'symbol str {
self.info.name()
}
#[inline]
pub(crate) fn gnu_hash(&self) -> u32 {
self.precompute.gnuhash
}
#[inline]
pub(crate) fn sysv_hash(&mut self, hash: impl FnOnce(&str) -> u32) -> u32 {
if let Some(hash) = self.precompute.hash {
hash
} else {
let hash = hash(self.name());
self.precompute.hash = Some(hash);
hash
}
}
#[inline]
#[cfg(feature = "object")]
pub(crate) fn custom_hash(&mut self, hash: impl FnOnce(&str) -> u64) -> u64 {
if let Some(hash) = self.precompute.custom {
hash
} else {
let hash = hash(self.name());
self.precompute.custom = Some(hash);
hash
}
}
#[inline]
#[cfg(feature = "version")]
pub(crate) fn version(&self) -> Option<&super::version::SymbolVersion<'symbol>> {
self.info.version()
}
}
pub struct SymbolEntry<'symtab, L: ElfLayout> {
symbol: &'symtab ElfSymbol<L>,
info: SymbolInfo<'symtab>,
}
impl<'symtab, L: ElfLayout> SymbolEntry<'symtab, L> {
#[inline]
pub(crate) const fn new(symbol: &'symtab ElfSymbol<L>, info: SymbolInfo<'symtab>) -> Self {
Self { symbol, info }
}
#[inline]
pub const fn symbol(&self) -> &'symtab ElfSymbol<L> {
self.symbol
}
#[inline]
pub(crate) const fn info(&self) -> &SymbolInfo<'symtab> {
&self.info
}
#[inline]
pub fn name(&self) -> &'symtab str {
self.info.name()
}
}
impl<L: ElfLayout, H> SymbolTable<L, H> {
#[inline]
pub fn view(&self) -> SymbolTableView<'_, L, H> {
SymbolTableView {
hashtab: &self.hashtab,
symbols: self.symbols,
strtab: &self.strtab,
#[cfg(feature = "version")]
version: self.version.as_ref(),
}
}
pub(crate) fn strtab(&self) -> &ElfStringTable {
&self.strtab
}
}
impl<'symtab, L: ElfLayout, H> SymbolTableView<'symtab, L, H> {
#[inline]
pub fn symbols(&self) -> &'symtab [ElfSymbol<L>] {
self.symbols
}
pub fn symbol_idx(&self, idx: usize) -> SymbolEntry<'symtab, L> {
let symbol = self
.symbols
.get(idx)
.expect("ELF symbol index is out of bounds");
let cname = self.strtab.get_cstr(symbol.st_name());
let name = ElfStringTable::convert_cstr(cname);
SymbolEntry::new(
symbol,
SymbolInfo {
name,
#[cfg(feature = "version")]
version: self.get_requirement(idx),
},
)
}
}
impl<'symtab, L: ElfLayout, H: SymbolHash<L>> SymbolTableView<'symtab, L, H> {
fn lookup(&self, lookup: &mut SymbolLookup<'_>) -> Option<&'symtab ElfSymbol<L>> {
self.hashtab.lookup(*self, lookup)
}
#[inline]
pub(crate) fn lookup_filter(
&self,
lookup: &mut SymbolLookup<'_>,
) -> Option<&'symtab ElfSymbol<L>> {
if let Some(sym) = self.lookup(lookup) {
if !sym.is_undef() && sym.is_ok_bind() && sym.is_ok_type() {
return Some(sym);
}
}
None
}
}
impl<'symtab, L: ElfLayout> SymbolTableView<'symtab, L> {
pub fn lookup_by_name(&self, name: impl AsRef<str>) -> Option<&'symtab ElfSymbol<L>> {
let name = name.as_ref();
let mut lookup = SymbolLookup::new(name);
self.lookup(&mut lookup)
}
#[inline]
pub fn count_syms(&self) -> usize {
self.hashtab.count_syms()
}
}