use crate::{
elf::{ElfDynamic, HashTable, PreCompute},
elf::{ElfShdr, ElfSymbol},
};
use core::ffi::CStr;
use core::fmt::Debug;
pub(crate) struct ElfStringTable {
data: *const u8,
}
impl ElfStringTable {
const fn new(data: *const u8) -> Self {
ElfStringTable { data }
}
#[inline]
pub(crate) fn get_cstr(&self, offset: usize) -> &'static CStr {
unsafe {
let start = self.data.add(offset).cast();
CStr::from_ptr(start)
}
}
#[inline]
fn convert_cstr(s: &CStr) -> &str {
unsafe { core::str::from_utf8_unchecked(s.to_bytes()) }
}
#[inline]
pub(crate) fn get_str(&self, offset: usize) -> &'static str {
Self::convert_cstr(self.get_cstr(offset))
}
}
pub struct SymbolTable {
pub(crate) hashtab: HashTable,
pub(crate) symtab: *const ElfSymbol,
pub(crate) strtab: ElfStringTable,
#[cfg(feature = "version")]
pub(crate) version: Option<super::version::ELFVersion>,
}
impl Debug for SymbolTable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SymbolTable")
.field("hashtab", &self.hashtab)
.field("symtab_ptr", &self.symtab)
.finish()
}
}
pub struct SymbolInfo<'symtab> {
name: &'symtab str,
cname: Option<&'symtab CStr>,
#[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 fn from_str(name: &'symtab str, version: Option<&'symtab str>) -> Self {
SymbolInfo {
name,
cname: None,
#[cfg(feature = "version")]
version: version.map(super::version::SymbolVersion::new),
}
}
#[inline]
pub fn name(&self) -> &'symtab str {
self.name
}
#[inline]
pub fn cname(&self) -> Option<&'symtab CStr> {
self.cname
}
#[cfg(feature = "version")]
pub(crate) fn version(&self) -> Option<&super::version::SymbolVersion<'symtab>> {
self.version.as_ref()
}
}
impl SymbolTable {
pub(crate) fn from_dynamic(dynamic: &ElfDynamic) -> Self {
let hashtab = HashTable::from_dynamic(dynamic);
let symtab = dynamic.symtab as *const ElfSymbol;
let strtab = ElfStringTable::new(dynamic.strtab as *const u8);
#[cfg(feature = "version")]
let version = super::version::ELFVersion::new(
dynamic.version_idx,
dynamic.verneed,
dynamic.verdef,
&strtab,
);
SymbolTable {
hashtab,
symtab,
strtab,
#[cfg(feature = "version")]
version,
}
}
pub(crate) fn from_shdrs(symtab: &ElfShdr, shdrs: &[ElfShdr]) -> Self {
let strtab_shdr = &shdrs[symtab.sh_link as usize];
let strtab = ElfStringTable::new(strtab_shdr.sh_addr as *const u8);
let hashtab = HashTable::from_shdr(symtab, &strtab);
Self {
hashtab,
symtab: symtab.sh_addr as *const ElfSymbol,
strtab,
#[cfg(feature = "version")]
version: None,
}
}
pub(crate) fn strtab(&self) -> &ElfStringTable {
&self.strtab
}
pub fn lookup_by_name(&self, name: impl AsRef<str>) -> Option<&ElfSymbol> {
let info = SymbolInfo::from_str(name.as_ref(), None);
let mut precompute = info.precompute();
self.lookup(&info, &mut precompute)
}
fn lookup(&self, symbol: &SymbolInfo, precompute: &mut PreCompute) -> Option<&ElfSymbol> {
self.hashtab.lookup(self, symbol, precompute)
}
#[inline]
pub(crate) fn lookup_filter(
&self,
symbol: &SymbolInfo,
precompute: &mut PreCompute,
) -> Option<&ElfSymbol> {
if let Some(sym) = self.lookup(symbol, precompute) {
if !sym.is_undef() && sym.is_ok_bind() && sym.is_ok_type() {
return Some(sym);
}
}
None
}
pub fn symbol_idx<'symtab>(
&'symtab self,
idx: usize,
) -> (&'symtab ElfSymbol, SymbolInfo<'symtab>) {
let symbol = unsafe { &*self.symtab.add(idx) };
let cname = self.strtab.get_cstr(symbol.st_name());
let name = ElfStringTable::convert_cstr(cname);
(
symbol,
SymbolInfo {
name,
cname: Some(cname),
#[cfg(feature = "version")]
version: self.get_requirement(idx),
},
)
}
#[inline]
pub fn count_syms(&self) -> usize {
self.hashtab.count_syms()
}
}