use super::hash::{PreCompute, SymbolHash};
use super::{
layout::{ElfLayout, NativeElfLayout},
raw::ElfSymRaw,
};
use crate::{elf::HashTable, memory::MappedView};
use core::{
ffi::CStr,
fmt::{self, Debug, Display},
};
use elf::abi::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElfSymbolBind(u8);
impl ElfSymbolBind {
pub const LOCAL: Self = Self(STB_LOCAL);
pub const GLOBAL: Self = Self(STB_GLOBAL);
pub const WEAK: Self = Self(STB_WEAK);
pub const GNU_UNIQUE: Self = Self(STB_GNU_UNIQUE);
#[inline]
pub const fn new(raw: u8) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(self) -> u8 {
self.0
}
}
impl From<u8> for ElfSymbolBind {
#[inline]
fn from(value: u8) -> Self {
Self::new(value)
}
}
impl From<ElfSymbolBind> for u8 {
#[inline]
fn from(value: ElfSymbolBind) -> Self {
value.raw()
}
}
impl Display for ElfSymbolBind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
STB_LOCAL => f.write_str("STB_LOCAL"),
STB_GLOBAL => f.write_str("STB_GLOBAL"),
STB_WEAK => f.write_str("STB_WEAK"),
STB_GNU_UNIQUE => f.write_str("STB_GNU_UNIQUE"),
raw => write!(f, "unknown ELF symbol bind {raw}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElfSymbolType(u8);
impl ElfSymbolType {
pub const NOTYPE: Self = Self(STT_NOTYPE);
pub const OBJECT: Self = Self(STT_OBJECT);
pub const FUNC: Self = Self(STT_FUNC);
pub const SECTION: Self = Self(STT_SECTION);
pub const FILE: Self = Self(STT_FILE);
pub const COMMON: Self = Self(STT_COMMON);
pub const TLS: Self = Self(STT_TLS);
pub const GNU_IFUNC: Self = Self(STT_GNU_IFUNC);
#[inline]
pub const fn new(raw: u8) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(self) -> u8 {
self.0
}
}
impl From<u8> for ElfSymbolType {
#[inline]
fn from(value: u8) -> Self {
Self::new(value)
}
}
impl From<ElfSymbolType> for u8 {
#[inline]
fn from(value: ElfSymbolType) -> Self {
value.raw()
}
}
impl Display for ElfSymbolType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
STT_NOTYPE => f.write_str("STT_NOTYPE"),
STT_OBJECT => f.write_str("STT_OBJECT"),
STT_FUNC => f.write_str("STT_FUNC"),
STT_SECTION => f.write_str("STT_SECTION"),
STT_FILE => f.write_str("STT_FILE"),
STT_COMMON => f.write_str("STT_COMMON"),
STT_TLS => f.write_str("STT_TLS"),
STT_GNU_IFUNC => f.write_str("STT_GNU_IFUNC"),
raw => write!(f, "unknown ELF symbol type {raw}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElfSymbolVisibility(u8);
impl ElfSymbolVisibility {
pub const DEFAULT: Self = Self(STV_DEFAULT);
pub const INTERNAL: Self = Self(STV_INTERNAL);
pub const HIDDEN: Self = Self(STV_HIDDEN);
pub const PROTECTED: Self = Self(STV_PROTECTED);
#[inline]
pub const fn new(st_other: u8) -> Self {
Self(st_other & 0x3)
}
#[inline]
pub const fn raw(self) -> u8 {
self.0
}
}
impl From<u8> for ElfSymbolVisibility {
#[inline]
fn from(value: u8) -> Self {
Self::new(value)
}
}
impl From<ElfSymbolVisibility> for u8 {
#[inline]
fn from(value: ElfSymbolVisibility) -> Self {
value.raw()
}
}
impl Display for ElfSymbolVisibility {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
STV_DEFAULT => f.write_str("STV_DEFAULT"),
STV_INTERNAL => f.write_str("STV_INTERNAL"),
STV_HIDDEN => f.write_str("STV_HIDDEN"),
STV_PROTECTED => f.write_str("STV_PROTECTED"),
raw => write!(f, "unknown ELF symbol visibility {raw}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElfSectionIndex(u16);
impl ElfSectionIndex {
pub const UNDEF: Self = Self(SHN_UNDEF);
pub const ABS: Self = Self(SHN_ABS);
pub const COMMON: Self = Self(SHN_COMMON);
pub const XINDEX: Self = Self(SHN_XINDEX);
#[inline]
pub const fn new(raw: u16) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(self) -> u16 {
self.0
}
#[inline]
pub const fn index(self) -> usize {
self.0 as usize
}
#[inline]
pub const fn is_undef(self) -> bool {
self.0 == SHN_UNDEF
}
#[inline]
pub const fn is_abs(self) -> bool {
self.0 == SHN_ABS
}
#[inline]
pub const fn is_common(self) -> bool {
self.0 == SHN_COMMON
}
#[inline]
pub const fn is_xindex(self) -> bool {
self.0 == SHN_XINDEX
}
}
impl Display for ElfSectionIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
SHN_UNDEF => f.write_str("SHN_UNDEF"),
SHN_ABS => f.write_str("SHN_ABS"),
SHN_COMMON => f.write_str("SHN_COMMON"),
SHN_XINDEX => f.write_str("SHN_XINDEX"),
raw => write!(f, "ELF symbol section index {raw}"),
}
}
}
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> Clone for ElfSymbol<L> {
fn clone(&self) -> Self {
Self {
sym: L::Sym::from_fields(
self.st_name(),
self.st_value(),
self.st_size(),
self.sym.st_info(),
self.st_other(),
self.st_shndx().raw(),
),
}
}
}
impl<L: ElfLayout> ElfSymbol<L> {
pub(crate) fn synthetic(
name: usize,
value: usize,
size: usize,
bind: ElfSymbolBind,
symbol_type: ElfSymbolType,
other: u8,
section_index: ElfSectionIndex,
) -> Self {
let st_info = (bind.raw() << 4) | (symbol_type.raw() & 0xf);
Self {
sym: L::Sym::from_fields(name, value, size, st_info, other, 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 visibility(&self) -> ElfSymbolVisibility {
ElfSymbolVisibility::new(self.sym.st_other())
}
#[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_exported(&self) -> bool {
!self.is_undef()
&& (1 << self.bind().raw()) & OK_BINDS != 0
&& (1 << self.symbol_type().raw()) & OK_TYPES != 0
&& matches!(
self.visibility(),
ElfSymbolVisibility::DEFAULT | ElfSymbolVisibility::PROTECTED
)
}
#[inline]
pub fn binds_local(&self) -> bool {
self.bind() == ElfSymbolBind::LOCAL || self.visibility() != ElfSymbolVisibility::DEFAULT
}
#[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 {
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)
.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 version_name(&self) -> Option<&str> {
#[cfg(feature = "version")]
{
self.info.version().map(|version| version.name())
}
#[cfg(not(feature = "version"))]
{
None
}
}
#[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(crate) fn get_raw(&self, index: usize) -> Option<&'symtab ElfSymbol<L>> {
self.symbols.get(index)
}
pub fn entry(&self, index: usize) -> SymbolEntry<'symtab, L> {
let symbol = &self.symbols[index];
SymbolEntry::new(
symbol,
SymbolInfo {
name: self.strtab.get_str(symbol.st_name()),
#[cfg(feature = "version")]
version: self.get_requirement(index),
},
)
}
}
impl<'symtab, L: ElfLayout, H: SymbolHash<L>> SymbolTableView<'symtab, L, H> {
pub(crate) fn lookup(&self, lookup: &mut SymbolLookup<'_>) -> Option<&'symtab ElfSymbol<L>> {
self.hashtab.lookup(*self, lookup)
}
}
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)
}
}