diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/goblin/src/mach/symbols.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/goblin/src/mach/symbols.rs')
-rw-r--r-- | third_party/rust/goblin/src/mach/symbols.rs | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/mach/symbols.rs b/third_party/rust/goblin/src/mach/symbols.rs new file mode 100644 index 0000000000..adfc4d665b --- /dev/null +++ b/third_party/rust/goblin/src/mach/symbols.rs @@ -0,0 +1,488 @@ +//! "Nlist" style symbols in this binary - beware, like most symbol tables in most binary formats, they are strippable, and should not be relied upon, see the imports and exports modules for something more permanent. +//! +//! Symbols are essentially a type, offset, and the symbol name + +use crate::container::{self, Container}; +use crate::error; +use crate::mach::load_command; +use core::fmt::{self, Debug}; +use scroll::ctx; +use scroll::ctx::SizeWith; +use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith}; + +// The n_type field really contains four fields which are used via the following masks. +/// if any of these bits set, a symbolic debugging entry +pub const N_STAB: u8 = 0xe0; +/// private external symbol bit +pub const N_PEXT: u8 = 0x10; +/// mask for the type bits +pub const N_TYPE: u8 = 0x0e; +/// external symbol bit, set for external symbols +pub const N_EXT: u8 = 0x01; + +// If the type is N_SECT then the n_sect field contains an ordinal of the +// section the symbol is defined in. The sections are numbered from 1 and +// refer to sections in order they appear in the load commands for the file +// they are in. This means the same ordinal may very well refer to different +// sections in different files. + +// The n_value field for all symbol table entries (including N_STAB's) gets +// updated by the link editor based on the value of it's n_sect field and where +// the section n_sect references gets relocated. If the value of the n_sect +// field is NO_SECT then it's n_value field is not changed by the link editor. +/// symbol is not in any section +pub const NO_SECT: u8 = 0; +/// 1 thru 255 inclusive +pub const MAX_SECT: u8 = 255; + +/// undefined, n_sect == NO_SECT +pub const N_UNDF: u8 = 0x0; +/// absolute, n_sect == NO_SECT +pub const N_ABS: u8 = 0x2; +/// defined in section number n_sect +pub const N_SECT: u8 = 0xe; +/// prebound undefined (defined in a dylib) +pub const N_PBUD: u8 = 0xc; +/// indirect +pub const N_INDR: u8 = 0xa; + +// n_types when N_STAB +pub const N_GSYM: u8 = 0x20; +pub const N_FNAME: u8 = 0x22; +pub const N_FUN: u8 = 0x24; +pub const N_STSYM: u8 = 0x26; +pub const N_LCSYM: u8 = 0x28; +pub const N_BNSYM: u8 = 0x2e; +pub const N_PC: u8 = 0x30; +pub const N_AST: u8 = 0x32; +pub const N_OPT: u8 = 0x3c; +pub const N_RSYM: u8 = 0x40; +pub const N_SLINE: u8 = 0x44; +pub const N_ENSYM: u8 = 0x4e; +pub const N_SSYM: u8 = 0x60; +pub const N_SO: u8 = 0x64; +pub const N_OSO: u8 = 0x66; +pub const N_LSYM: u8 = 0x80; +pub const N_BINCL: u8 = 0x82; +pub const N_SOL: u8 = 0x84; +pub const N_PARAMS: u8 = 0x86; +pub const N_VERSION: u8 = 0x88; +pub const N_OLEVEL: u8 = 0x8a; +pub const N_PSYM: u8 = 0xa0; +pub const N_EINCL: u8 = 0xa2; +pub const N_ENTRY: u8 = 0xa4; +pub const N_LBRAC: u8 = 0xc0; +pub const N_EXCL: u8 = 0xc2; +pub const N_RBRAC: u8 = 0xe0; +pub const N_BCOMM: u8 = 0xe2; +pub const N_ECOMM: u8 = 0xe4; +pub const N_ECOML: u8 = 0xe8; +pub const N_LENG: u8 = 0xfe; + +pub const NLIST_TYPE_MASK: u8 = 0xe; +pub const NLIST_TYPE_GLOBAL: u8 = 0x1; +pub const NLIST_TYPE_LOCAL: u8 = 0x0; + +/// Mask for reference flags of `n_desc` field. +pub const REFERENCE_TYPE: u16 = 0xf; +/// This symbol is a reference to an external non-lazy (data) symbol. +pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0; +/// This symbol is a reference to an external lazy symbol—that is, to a function call. +pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1; +/// This symbol is defined in this module. +pub const REFERENCE_FLAG_DEFINED: u16 = 0x2; +/// This symbol is defined in this module and is visible only to modules within this +/// shared library. +pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 0x3; +/// This symbol is defined in another module in this file, is a non-lazy (data) symbol, +/// and is visible only to modules within this shared library. +pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 0x4; +/// This symbol is defined in another module in this file, is a lazy (function) symbol, +/// and is visible only to modules within this shared library. +pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 0x5; + +// Additional flags of n_desc field. + +/// Must be set for any defined symbol that is referenced by dynamic-loader APIs +/// (such as dlsym and NSLookupSymbolInImage) and not ordinary undefined symbol +/// references. The `strip` tool uses this bit to avoid removing symbols that must +/// exist: If the symbol has this bit set, `strip` does not strip it. +pub const REFERENCED_DYNAMICALLY: u16 = 0x10; +/// Sometimes used by the dynamic linker at runtime in a fully linked image. Do not +/// set this bit in a fully linked image. +pub const N_DESC_DISCARDED: u16 = 0x20; +/// When set in a relocatable object file (file type MH_OBJECT) on a defined symbol, +/// indicates to the static linker to never dead-strip the symbol. +// (Note that the same bit (0x20) is used for two nonoverlapping purposes.) +pub const N_NO_DEAD_STRIP: u16 = 0x20; +/// Indicates that this undefined symbol is a weak reference. If the dynamic linker +/// cannot find a definition for this symbol, it sets the address of this symbol to 0. +/// The static linker sets this symbol given the appropriate weak-linking flags. +pub const N_WEAK_REF: u16 = 0x40; +/// Indicates that this symbol is a weak definition. If the static linker or the +/// dynamic linker finds another (non-weak) definition for this symbol, the weak +/// definition is ignored. Only symbols in a coalesced section can be marked as a +/// weak definition. +pub const N_WEAK_DEF: u16 = 0x80; + +pub fn n_type_to_str(n_type: u8) -> &'static str { + match n_type { + N_UNDF => "N_UNDF", + N_ABS => "N_ABS", + N_SECT => "N_SECT", + N_PBUD => "N_PBUD", + N_INDR => "N_INDR", + _ => "UNKNOWN_N_TYPE", + } +} + +#[repr(C)] +#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)] +pub struct Nlist32 { + /// index into the string table + pub n_strx: u32, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: u16, + /// value of this symbol (or stab offset) + pub n_value: u32, +} + +pub const SIZEOF_NLIST_32: usize = 12; + +impl Debug for Nlist32 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Nlist32") + .field("n_strx", &format_args!("{:04}", self.n_strx)) + .field("n_type", &format_args!("{:#02x}", self.n_type)) + .field("n_sect", &format_args!("{:#x}", self.n_sect)) + .field("n_desc", &format_args!("{:#03x}", self.n_desc)) + .field("n_value", &format_args!("{:#x}", self.n_value)) + .finish() + } +} + +#[repr(C)] +#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)] +pub struct Nlist64 { + /// index into the string table + pub n_strx: u32, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: u16, + /// value of this symbol (or stab offset) + pub n_value: u64, +} + +pub const SIZEOF_NLIST_64: usize = 16; + +impl Debug for Nlist64 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Nlist64") + .field("n_strx", &format_args!("{:04}", self.n_strx)) + .field("n_type", &format_args!("{:#02x}", self.n_type)) + .field("n_sect", &format_args!("{:#x}", self.n_sect)) + .field("n_desc", &format_args!("{:#03x}", self.n_desc)) + .field("n_value", &format_args!("{:#x}", self.n_value)) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct Nlist { + /// index into the string table + pub n_strx: usize, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: usize, + /// see <mach-o/stab.h> + pub n_desc: u16, + /// value of this symbol (or stab offset) + pub n_value: u64, +} + +impl Nlist { + /// Gets this symbol's type in bits 0xe + pub fn get_type(&self) -> u8 { + self.n_type & N_TYPE + } + /// Gets the str representation of the type of this symbol + pub fn type_str(&self) -> &'static str { + n_type_to_str(self.get_type()) + } + /// Whether this symbol is global or not + pub fn is_global(&self) -> bool { + self.n_type & N_EXT != 0 + } + /// Whether this symbol is weak or not + pub fn is_weak(&self) -> bool { + self.n_desc & (N_WEAK_REF | N_WEAK_DEF) != 0 + } + /// Whether this symbol is undefined or not + pub fn is_undefined(&self) -> bool { + self.n_sect == 0 && self.n_type & N_TYPE == N_UNDF + } + /// Whether this symbol is a symbolic debugging entry + pub fn is_stab(&self) -> bool { + self.n_type & N_STAB != 0 + } +} + +impl ctx::SizeWith<container::Ctx> for Nlist { + fn size_with(ctx: &container::Ctx) -> usize { + match ctx.container { + Container::Little => SIZEOF_NLIST_32, + Container::Big => SIZEOF_NLIST_64, + } + } +} + +impl From<Nlist32> for Nlist { + fn from(nlist: Nlist32) -> Self { + Nlist { + n_strx: nlist.n_strx as usize, + n_type: nlist.n_type, + n_sect: nlist.n_sect as usize, + n_desc: nlist.n_desc, + n_value: u64::from(nlist.n_value), + } + } +} + +impl From<Nlist64> for Nlist { + fn from(nlist: Nlist64) -> Self { + Nlist { + n_strx: nlist.n_strx as usize, + n_type: nlist.n_type, + n_sect: nlist.n_sect as usize, + n_desc: nlist.n_desc, + n_value: nlist.n_value, + } + } +} + +impl From<Nlist> for Nlist32 { + fn from(nlist: Nlist) -> Self { + Nlist32 { + n_strx: nlist.n_strx as u32, + n_type: nlist.n_type, + n_sect: nlist.n_sect as u8, + n_desc: nlist.n_desc, + n_value: nlist.n_value as u32, + } + } +} + +impl From<Nlist> for Nlist64 { + fn from(nlist: Nlist) -> Self { + Nlist64 { + n_strx: nlist.n_strx as u32, + n_type: nlist.n_type, + n_sect: nlist.n_sect as u8, + n_desc: nlist.n_desc, + n_value: nlist.n_value, + } + } +} + +impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Nlist { + type Error = crate::error::Error; + fn try_from_ctx( + bytes: &'a [u8], + container::Ctx { container, le }: container::Ctx, + ) -> crate::error::Result<(Self, usize)> { + let nlist = match container { + Container::Little => (bytes.pread_with::<Nlist32>(0, le)?.into(), SIZEOF_NLIST_32), + Container::Big => (bytes.pread_with::<Nlist64>(0, le)?.into(), SIZEOF_NLIST_64), + }; + Ok(nlist) + } +} + +impl ctx::TryIntoCtx<container::Ctx> for Nlist { + type Error = crate::error::Error; + fn try_into_ctx( + self, + bytes: &mut [u8], + container::Ctx { container, le }: container::Ctx, + ) -> Result<usize, Self::Error> { + let size = match container { + Container::Little => bytes.pwrite_with::<Nlist32>(self.into(), 0, le)?, + Container::Big => bytes.pwrite_with::<Nlist64>(self.into(), 0, le)?, + }; + Ok(size) + } +} + +impl ctx::IntoCtx<container::Ctx> for Nlist { + fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { + bytes.pwrite_with(self, 0, ctx).unwrap(); + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct SymbolsCtx { + pub nsyms: usize, + pub strtab: usize, + pub ctx: container::Ctx, +} + +impl<'a, T: ?Sized> ctx::TryFromCtx<'a, SymbolsCtx, T> for Symbols<'a> +where + T: AsRef<[u8]>, +{ + type Error = crate::error::Error; + fn try_from_ctx( + bytes: &'a T, + SymbolsCtx { nsyms, strtab, ctx }: SymbolsCtx, + ) -> crate::error::Result<(Self, usize)> { + let data = bytes.as_ref(); + Ok(( + Symbols { + data, + start: 0, + nsyms, + strtab, + ctx, + }, + data.len(), + )) + } +} + +#[derive(Default)] +pub struct SymbolIterator<'a> { + data: &'a [u8], + nsyms: usize, + offset: usize, + count: usize, + ctx: container::Ctx, + strtab: usize, +} + +impl<'a> Iterator for SymbolIterator<'a> { + type Item = error::Result<(&'a str, Nlist)>; + fn next(&mut self) -> Option<Self::Item> { + if self.count >= self.nsyms { + None + } else { + self.count += 1; + match self.data.gread_with::<Nlist>(&mut self.offset, self.ctx) { + Ok(symbol) => match self.data.pread(self.strtab + symbol.n_strx) { + Ok(name) => Some(Ok((name, symbol))), + Err(e) => Some(Err(e.into())), + }, + Err(e) => Some(Err(e)), + } + } + } +} + +/// A zero-copy "nlist" style symbol table ("stab"), including the string table +pub struct Symbols<'a> { + data: &'a [u8], + start: usize, + nsyms: usize, + // TODO: we can use an actual strtab here and tie it to symbols lifetime + strtab: usize, + ctx: container::Ctx, +} + +impl<'a, 'b> IntoIterator for &'b Symbols<'a> { + type Item = <SymbolIterator<'a> as Iterator>::Item; + type IntoIter = SymbolIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> Symbols<'a> { + /// Creates a new symbol table with `count` elements, from the `start` offset, using the string table at `strtab`, with a _default_ ctx. + //// + /// **Beware**, this will provide incorrect results if you construct this on a 32-bit mach binary, using a 64-bit machine; use `parse` instead if you want 32/64 bit support + pub fn new( + bytes: &'a [u8], + start: usize, + count: usize, + strtab: usize, + ) -> error::Result<Symbols<'a>> { + let nsyms = count; + Ok(Symbols { + data: bytes, + start, + nsyms, + strtab, + ctx: container::Ctx::default(), + }) + } + pub fn parse( + bytes: &'a [u8], + symtab: &load_command::SymtabCommand, + ctx: container::Ctx, + ) -> error::Result<Symbols<'a>> { + // we need to normalize the strtab offset before we receive the truncated bytes in pread_with + let strtab = symtab + .stroff + .checked_sub(symtab.symoff) + .ok_or_else(|| error::Error::Malformed("invalid symbol table offset".into()))?; + bytes.pread_with( + symtab.symoff as usize, + SymbolsCtx { + nsyms: symtab.nsyms as usize, + strtab: strtab as usize, + ctx, + }, + ) + } + + pub fn iter(&self) -> SymbolIterator<'a> { + SymbolIterator { + offset: self.start as usize, + nsyms: self.nsyms as usize, + count: 0, + data: self.data, + ctx: self.ctx, + strtab: self.strtab, + } + } + + /// Parses a single Nlist symbol from the binary, with its accompanying name + pub fn get(&self, index: usize) -> crate::error::Result<(&'a str, Nlist)> { + let sym: Nlist = self + .data + .pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)?; + let name = self.data.pread(self.strtab + sym.n_strx)?; + Ok((name, sym)) + } +} + +impl<'a> Debug for Symbols<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Symbols") + .field("data", &self.data.len()) + .field("start", &format_args!("{:#?}", self.start)) + .field("nsyms", &self.nsyms) + .field("strtab", &format_args!("{:#x}", self.strtab)) + .finish()?; + + writeln!(fmt, "Symbol List {{")?; + for (i, res) in self.iter().enumerate() { + match res { + Ok((name, nlist)) => writeln!( + fmt, + "{: >10x} {} sect: {:#x} type: {:#02x} desc: {:#03x}", + nlist.n_value, name, nlist.n_sect, nlist.n_type, nlist.n_desc + )?, + Err(error) => writeln!(fmt, " Bad symbol, index: {}, sym: {:?}", i, error)?, + } + } + writeln!(fmt, "}}") + } +} |