diff options
Diffstat (limited to 'third_party/rust/goblin/src/elf/sym.rs')
-rw-r--r-- | third_party/rust/goblin/src/elf/sym.rs | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/elf/sym.rs b/third_party/rust/goblin/src/elf/sym.rs new file mode 100644 index 0000000000..a13e7abcfd --- /dev/null +++ b/third_party/rust/goblin/src/elf/sym.rs @@ -0,0 +1,607 @@ +/// === 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; +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>> { + 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<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! { + 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::<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> 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<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 |