summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/elf/sym.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/goblin/src/elf/sym.rs')
-rw-r--r--third_party/rust/goblin/src/elf/sym.rs607
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