use super::defs::{
ElfLayout, ElfSectionIndex, ElfSymRaw, ElfSymbolBind, ElfSymbolType, NativeElfLayout,
};
use crate::elf::{ElfDynamic, HashTable, PreCompute};
use core::ffi::CStr;
use core::fmt::Debug;
use elf::abi::{
STB_GLOBAL, STB_GNU_UNIQUE, STB_WEAK, STT_COMMON, STT_FUNC, STT_GNU_IFUNC, STT_NOTYPE,
STT_OBJECT, STT_TLS,
};
const OK_BINDS: usize = 1 << STB_GLOBAL | 1 << STB_WEAK | 1 << STB_GNU_UNIQUE;
const OK_TYPES: usize = 1 << STT_NOTYPE
| 1 << STT_OBJECT
| 1 << STT_FUNC
| 1 << STT_COMMON
| 1 << STT_TLS
| 1 << STT_GNU_IFUNC;
#[repr(transparent)]
pub struct ElfSymbol<L: ElfLayout = NativeElfLayout> {
sym: L::Sym,
}
impl<L: ElfLayout> ElfSymbol<L> {
pub(crate) fn synthetic(
value: usize,
size: usize,
bind: ElfSymbolBind,
symbol_type: ElfSymbolType,
section_index: ElfSectionIndex,
) -> Self {
let st_info = (bind.raw() << 4) | (symbol_type.raw() & 0xf);
Self {
sym: L::Sym::from_fields(0, value, size, st_info, 0, section_index.raw()),
}
}
#[inline]
pub fn st_value(&self) -> usize {
self.sym.st_value()
}
#[inline]
pub fn bind(&self) -> ElfSymbolBind {
ElfSymbolBind::new(self.sym.st_info() >> 4)
}
#[inline]
pub fn symbol_type(&self) -> ElfSymbolType {
ElfSymbolType::new(self.sym.st_info() & 0xf)
}
#[inline]
pub fn st_shndx(&self) -> ElfSectionIndex {
ElfSectionIndex::new(self.sym.st_shndx())
}
#[inline]
pub fn st_name(&self) -> usize {
self.sym.st_name()
}
#[inline]
pub fn st_size(&self) -> usize {
self.sym.st_size()
}
#[inline]
pub fn st_other(&self) -> u8 {
self.sym.st_other()
}
#[inline]
pub fn is_undef(&self) -> bool {
self.st_shndx().is_undef()
}
#[inline]
pub fn is_ok_bind(&self) -> bool {
(1 << self.bind().raw()) & OK_BINDS != 0
}
#[inline]
pub fn is_ok_type(&self) -> bool {
(1 << self.symbol_type().raw()) & OK_TYPES != 0
}
#[inline]
pub fn is_local(&self) -> bool {
self.bind() == ElfSymbolBind::LOCAL
}
#[inline]
pub fn is_weak(&self) -> bool {
self.bind() == ElfSymbolBind::WEAK
}
#[inline]
pub(crate) fn set_value(&mut self, value: usize) {
self.sym.set_st_value(value);
}
}
pub(crate) struct ElfStringTable {
data: *const u8,
}
impl ElfStringTable {
pub(crate) 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<L: ElfLayout = NativeElfLayout> {
pub(crate) hashtab: HashTable,
pub(crate) symtab: *const ElfSymbol<L>,
pub(crate) strtab: ElfStringTable,
#[cfg(feature = "version")]
pub(crate) version: Option<super::version::ELFVersion>,
}
impl<L: ElfLayout> Debug for SymbolTable<L> {
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<L: ElfLayout> SymbolTable<L> {
pub(crate) fn from_dynamic<Arch>(dynamic: &ElfDynamic<Arch>) -> Self
where
Arch: crate::relocation::RelocationArch<Layout = L>,
{
let hashtab = HashTable::from_dynamic(dynamic);
let symtab = dynamic.symtab as *const ElfSymbol<L>;
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 strtab(&self) -> &ElfStringTable {
&self.strtab
}
pub fn lookup_by_name(&self, name: impl AsRef<str>) -> Option<&ElfSymbol<L>> {
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<L>> {
self.hashtab.lookup(self, symbol, precompute)
}
#[inline]
pub(crate) fn lookup_filter(
&self,
symbol: &SymbolInfo,
precompute: &mut PreCompute,
) -> Option<&ElfSymbol<L>> {
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<L>, 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()
}
}