/// === Sym bindings === /// Local symbol. pub const STB_LOCAL: u8 = 0; /// Global symbol. pub const STB_GLOBAL: u8 = 1; /// Weak symbol. pub const STB_WEAK: u8 = 2; /// Number of defined types.. pub const STB_NUM: u8 = 3; /// Start of OS-specific. pub const STB_LOOS: u8 = 10; /// Unique symbol.. pub const STB_GNU_UNIQUE: u8 = 10; /// End of OS-specific. pub const STB_HIOS: u8 = 12; /// Start of processor-specific. pub const STB_LOPROC: u8 = 13; /// End of processor-specific. pub const STB_HIPROC: u8 = 15; /// === Sym types === /// Symbol type is unspecified. pub const STT_NOTYPE: u8 = 0; /// Symbol is a data object. pub const STT_OBJECT: u8 = 1; /// Symbol is a code object. pub const STT_FUNC: u8 = 2; /// Symbol associated with a section. pub const STT_SECTION: u8 = 3; /// Symbol's name is file name. pub const STT_FILE: u8 = 4; /// Symbol is a common data object. pub const STT_COMMON: u8 = 5; /// Symbol is thread-local data object. pub const STT_TLS: u8 = 6; /// Number of defined types. pub const STT_NUM: u8 = 7; /// Start of OS-specific. pub const STT_LOOS: u8 = 10; /// Symbol is indirect code object. pub const STT_GNU_IFUNC: u8 = 10; /// End of OS-specific. pub const STT_HIOS: u8 = 12; /// Start of processor-specific. pub const STT_LOPROC: u8 = 13; /// End of processor-specific. pub const STT_HIPROC: u8 = 15; /// === Sym visibility === /// Default: Visibility is specified by the symbol's binding type pub const STV_DEFAULT: u8 = 0; /// Internal: use of this attribute is currently reserved. pub const STV_INTERNAL: u8 = 1; /// Hidden: Not visible to other components, necessarily protected. Binding scope becomes local /// when the object is included in an executable or shared object. pub const STV_HIDDEN: u8 = 2; /// Protected: Symbol defined in current component is visible in other components, but cannot be preempted. /// Any reference from within the defining component must be resolved to the definition in that /// component. pub const STV_PROTECTED: u8 = 3; /// Exported: ensures a symbol remains global, cannot be demoted or eliminated by any other symbol /// visibility technique. pub const STV_EXPORTED: u8 = 4; /// Singleton: ensures a symbol remains global, and that a single instance of the definition is /// bound to by all references within a process. Cannot be demoted or eliminated. pub const STV_SINGLETON: u8 = 5; /// Eliminate: extends the hidden attribute. Not written in any symbol table of a dynamic /// executable or shared object. pub const STV_ELIMINATE: u8 = 6; /// Get the ST bind. /// /// This is the first four bits of the "info" byte. #[inline] pub fn st_bind(info: u8) -> u8 { info >> 4 } /// Get the ST type. /// /// This is the last four bits of the "info" byte. #[inline] pub fn st_type(info: u8) -> u8 { info & 0xf } /// Get the ST visibility. /// /// This is the last three bits of the "other" byte. #[inline] pub fn st_visibility(other: u8) -> u8 { other & 0x7 } /// Is this information defining an import? #[inline] pub fn is_import(info: u8, value: u64) -> bool { let bind = st_bind(info); bind == STB_GLOBAL && value == 0 } /// Convenience function to get the &'static str type from the symbols `st_info`. #[inline] pub fn get_type(info: u8) -> &'static str { type_to_str(st_type(info)) } /// Get the string for some bind. #[inline] pub fn bind_to_str(typ: u8) -> &'static str { match typ { STB_LOCAL => "LOCAL", STB_GLOBAL => "GLOBAL", STB_WEAK => "WEAK", STB_NUM => "NUM", STB_GNU_UNIQUE => "GNU_UNIQUE", _ => "UNKNOWN_STB", } } /// Get the string for some type. #[inline] pub fn type_to_str(typ: u8) -> &'static str { match typ { STT_NOTYPE => "NOTYPE", STT_OBJECT => "OBJECT", STT_FUNC => "FUNC", STT_SECTION => "SECTION", STT_FILE => "FILE", STT_COMMON => "COMMON", STT_TLS => "TLS", STT_NUM => "NUM", STT_GNU_IFUNC => "GNU_IFUNC", _ => "UNKNOWN_STT", } } /// Get the string for some visibility #[inline] pub fn visibility_to_str(typ: u8) -> &'static str { match typ { STV_DEFAULT => "DEFAULT", STV_INTERNAL => "INTERNAL", STV_HIDDEN => "HIDDEN", STV_PROTECTED => "PROTECTED", STV_EXPORTED => "EXPORTED", STV_SINGLETON => "SINGLETON", STV_ELIMINATE => "ELIMINATE", _ => "UNKNOWN_STV", } } macro_rules! elf_sym_std_impl { ($size:ty) => { #[cfg(test)] mod tests { use super::*; #[test] fn size_of() { assert_eq!(::std::mem::size_of::(), SIZEOF_SYM); } } use crate::elf::sym::Sym as ElfSym; use core::fmt; use core::slice; impl Sym { /// Checks whether this `Sym` has `STB_GLOBAL`/`STB_WEAK` bind and a `st_value` of 0 #[inline] pub fn is_import(&self) -> bool { let bind = self.st_info >> 4; (bind == STB_GLOBAL || bind == STB_WEAK) && self.st_value == 0 } /// Checks whether this `Sym` has type `STT_FUNC` #[inline] pub fn is_function(&self) -> bool { st_type(self.st_info) == STT_FUNC } } impl From for ElfSym { #[inline] fn from(sym: Sym) -> Self { ElfSym { st_name: sym.st_name as usize, st_info: sym.st_info, st_other: sym.st_other, st_shndx: sym.st_shndx as usize, st_value: u64::from(sym.st_value), st_size: u64::from(sym.st_size), } } } impl From for Sym { #[inline] fn from(sym: ElfSym) -> Self { Sym { st_name: sym.st_name as u32, st_info: sym.st_info, st_other: sym.st_other, st_shndx: sym.st_shndx as u16, st_value: sym.st_value as $size, st_size: sym.st_size as $size, } } } impl fmt::Debug for Sym { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bind = st_bind(self.st_info); let typ = st_type(self.st_info); let vis = st_visibility(self.st_other); f.debug_struct("Sym") .field("st_name", &self.st_name) .field("st_value", &format_args!("{:x}", self.st_value)) .field("st_size", &self.st_size) .field( "st_info", &format_args!( "{:x} {} {}", self.st_info, bind_to_str(bind), type_to_str(typ) ), ) .field( "st_other", &format_args!("{} {}", self.st_other, visibility_to_str(vis)), ) .field("st_shndx", &self.st_shndx) .finish() } } /// # Safety /// /// This function creates a `Sym` slice directly from a raw pointer #[inline] pub unsafe fn from_raw<'a>(symp: *const Sym, count: usize) -> &'a [Sym] { slice::from_raw_parts(symp, count) } if_std! { use crate::error::Result; use std::fs::File; use std::io::{Read, Seek}; use std::io::SeekFrom::Start; pub fn from_fd(fd: &mut File, offset: usize, count: usize) -> Result> { // TODO: AFAIK this shouldn't work, since i pass in a byte size... let mut syms = vec![Sym::default(); count]; fd.seek(Start(offset as u64))?; unsafe { fd.read_exact(plain::as_mut_bytes(&mut *syms))?; } syms.dedup(); Ok(syms) } } }; } #[cfg(feature = "alloc")] use scroll::{Pread, Pwrite, SizeWith}; pub mod sym32 { pub use crate::elf::sym::*; #[repr(C)] #[derive(Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] /// 32-bit Sym - used for both static and dynamic symbol information in a binary pub struct Sym { /// Symbol name (string tbl index) pub st_name: u32, /// Symbol value pub st_value: u32, /// Symbol size pub st_size: u32, /// Symbol type and binding pub st_info: u8, /// Symbol visibility pub st_other: u8, /// Section index pub st_shndx: u16, } // Declare that the type is plain. unsafe impl plain::Plain for Sym {} pub const SIZEOF_SYM: usize = 4 + 1 + 1 + 2 + 4 + 4; elf_sym_std_impl!(u32); } pub mod sym64 { pub use crate::elf::sym::*; #[repr(C)] #[derive(Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] /// 64-bit Sym - used for both static and dynamic symbol information in a binary pub struct Sym { /// Symbol name (string tbl index) pub st_name: u32, /// Symbol type and binding pub st_info: u8, /// Symbol visibility pub st_other: u8, /// Section index pub st_shndx: u16, /// Symbol value pub st_value: u64, /// Symbol size pub st_size: u64, } // Declare that the type is plain. unsafe impl plain::Plain for Sym {} pub const SIZEOF_SYM: usize = 4 + 1 + 1 + 2 + 8 + 8; elf_sym_std_impl!(u64); } use crate::container::{Container, Ctx}; #[cfg(feature = "alloc")] use crate::error::Result; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::fmt; use scroll::ctx; use scroll::ctx::SizeWith; #[derive(Clone, Copy, PartialEq, Default)] /// A unified Sym definition - convertible to and from 32-bit and 64-bit variants pub struct Sym { pub st_name: usize, pub st_info: u8, pub st_other: u8, pub st_shndx: usize, pub st_value: u64, pub st_size: u64, } impl Sym { #[inline] pub fn size(container: Container) -> usize { Self::size_with(&Ctx::from(container)) } /// Checks whether this `Sym` has `STB_GLOBAL`/`STB_WEAK` bind and a `st_value` of 0 #[inline] pub fn is_import(&self) -> bool { let bind = self.st_bind(); (bind == STB_GLOBAL || bind == STB_WEAK) && self.st_value == 0 } /// Checks whether this `Sym` has type `STT_FUNC` #[inline] pub fn is_function(&self) -> bool { st_type(self.st_info) == STT_FUNC } /// Get the ST bind. /// /// This is the first four bits of the "info" byte. #[inline] pub fn st_bind(&self) -> u8 { self.st_info >> 4 } /// Get the ST type. /// /// This is the last four bits of the "info" byte. #[inline] pub fn st_type(&self) -> u8 { st_type(self.st_info) } /// Get the ST visibility. /// /// This is the last three bits of the "other" byte. #[inline] pub fn st_visibility(&self) -> u8 { st_visibility(self.st_other) } #[cfg(feature = "endian_fd")] /// Parse `count` vector of ELF symbols from `offset` pub fn parse(bytes: &[u8], mut offset: usize, count: usize, ctx: Ctx) -> Result> { if count > bytes.len() / Sym::size_with(&ctx) { return Err(crate::error::Error::BufferTooShort(count, "symbols")); } let mut syms = Vec::with_capacity(count); for _ in 0..count { let sym = bytes.gread_with(&mut offset, ctx)?; syms.push(sym); } Ok(syms) } } impl fmt::Debug for Sym { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bind = self.st_bind(); let typ = self.st_type(); let vis = self.st_visibility(); f.debug_struct("Sym") .field("st_name", &self.st_name) .field( "st_info", &format_args!( "0x{:x} {} {}", self.st_info, bind_to_str(bind), type_to_str(typ) ), ) .field( "st_other", &format_args!("{} {}", self.st_other, visibility_to_str(vis)), ) .field("st_shndx", &self.st_shndx) .field("st_value", &format_args!("0x{:x}", self.st_value)) .field("st_size", &self.st_size) .finish() } } impl ctx::SizeWith for Sym { #[inline] fn size_with(&Ctx { container, .. }: &Ctx) -> usize { match container { Container::Little => sym32::SIZEOF_SYM, Container::Big => sym64::SIZEOF_SYM, } } } if_alloc! { use core::result; impl<'a> ctx::TryFromCtx<'a, Ctx> for Sym { type Error = crate::error::Error; #[inline] fn try_from_ctx(bytes: &'a [u8], Ctx { container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> { let sym = match container { Container::Little => { (bytes.pread_with::(0, le)?.into(), sym32::SIZEOF_SYM) }, Container::Big => { (bytes.pread_with::(0, le)?.into(), sym64::SIZEOF_SYM) } }; Ok(sym) } } impl ctx::TryIntoCtx for Sym { type Error = crate::error::Error; #[inline] fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result { match container { Container::Little => { let sym: sym32::Sym = self.into(); Ok(bytes.pwrite_with(sym, 0, le)?) }, Container::Big => { let sym: sym64::Sym = self.into(); Ok(bytes.pwrite_with(sym, 0, le)?) } } } } impl ctx::IntoCtx for Sym { #[inline] fn into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) { match container { Container::Little => { let sym: sym32::Sym = self.into(); bytes.pwrite_with(sym, 0, le).unwrap(); }, Container::Big => { let sym: sym64::Sym = self.into(); bytes.pwrite_with(sym, 0, le).unwrap(); } } } } } if_alloc! { #[derive(Default)] /// An ELF symbol table, allowing lazy iteration over symbols pub struct Symtab<'a> { bytes: &'a [u8], count: usize, ctx: Ctx, start: usize, end: usize, } impl<'a> fmt::Debug for Symtab<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let len = self.bytes.len(); fmt.debug_struct("Symtab") .field("bytes", &len) .field("range", &format_args!("{:#x}..{:#x}", self.start, self.end)) .field("count", &self.count) .field("Symbols", &self.to_vec()) .finish() } } impl<'a> Symtab<'a> { /// Parse a table of `count` ELF symbols from `offset`. pub fn parse(bytes: &'a [u8], offset: usize, count: usize, ctx: Ctx) -> Result> { let size = count .checked_mul(Sym::size_with(&ctx)) .ok_or_else(|| crate::error::Error::Malformed( format!("Too many ELF symbols (offset {:#x}, count {})", offset, count) ))?; // TODO: make this a better error message when too large let bytes = bytes.pread_with(offset, size)?; Ok(Symtab { bytes, count, ctx, start: offset, end: offset+size }) } /// Try to parse a single symbol from the binary, at `index`. #[inline] pub fn get(&self, index: usize) -> Option { if index >= self.count { None } else { Some(self.bytes.pread_with(index * Sym::size_with(&self.ctx), self.ctx).unwrap()) } } /// The number of symbols in the table. #[inline] pub fn len(&self) -> usize { self.count } /// Returns true if table has no symbols. #[inline] pub fn is_empty(&self) -> bool { self.count == 0 } /// Iterate over all symbols. #[inline] pub fn iter(&self) -> SymIterator<'a> { self.into_iter() } /// Parse all symbols into a vector. pub fn to_vec(&self) -> Vec { self.iter().collect() } } impl<'a, 'b> IntoIterator for &'b Symtab<'a> { type Item = as Iterator>::Item; type IntoIter = SymIterator<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { SymIterator { bytes: self.bytes, offset: 0, index: 0, count: self.count, ctx: self.ctx, } } } /// An iterator over symbols in an ELF symbol table pub struct SymIterator<'a> { bytes: &'a [u8], offset: usize, index: usize, count: usize, ctx: Ctx, } impl<'a> Iterator for SymIterator<'a> { type Item = Sym; #[inline] fn next(&mut self) -> Option { if self.index >= self.count { None } else { self.index += 1; Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap()) } } } impl<'a> ExactSizeIterator for SymIterator<'a> { #[inline] fn len(&self) -> usize { self.count - self.index } } } // end if_alloc