summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/mach/symbols.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/goblin/src/mach/symbols.rs
parentInitial commit. (diff)
downloadfirefox-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.rs488
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, "}}")
+ }
+}