| /// === 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::<Sym>(), 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<Sym> 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<ElfSym> 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<Vec<Sym>> { |
| // 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::{self, Debug}; |
| use core::result; |
| 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<Vec<Sym>> { |
| 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<Ctx> for Sym { |
| #[inline] |
| fn size_with(&Ctx { container, .. }: &Ctx) -> usize { |
| match container { |
| Container::Little => sym32::SIZEOF_SYM, |
| Container::Big => sym64::SIZEOF_SYM, |
| } |
| } |
| } |
| |
| if_alloc! { |
| 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::<sym32::Sym>(0, le)?.into(), sym32::SIZEOF_SYM) |
| }, |
| Container::Big => { |
| (bytes.pread_with::<sym64::Sym>(0, le)?.into(), sym64::SIZEOF_SYM) |
| } |
| }; |
| Ok(sym) |
| } |
| } |
| |
| impl ctx::TryIntoCtx<Ctx> for Sym { |
| type Error = crate::error::Error; |
| #[inline] |
| fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result<usize, Self::Error> { |
| 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<Ctx> 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> 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<Symtab<'a>> { |
| 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<Sym> { |
| 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<Sym> { |
| self.iter().collect() |
| } |
| } |
| |
| impl<'a, 'b> IntoIterator for &'b Symtab<'a> { |
| type Item = <SymIterator<'a> 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<Self::Item> { |
| 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 |