summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object/src/read/macho/symbol.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/object/src/read/macho/symbol.rs')
-rw-r--r--third_party/rust/object/src/read/macho/symbol.rs488
1 files changed, 488 insertions, 0 deletions
diff --git a/third_party/rust/object/src/read/macho/symbol.rs b/third_party/rust/object/src/read/macho/symbol.rs
new file mode 100644
index 0000000000..e102c5d0b6
--- /dev/null
+++ b/third_party/rust/object/src/read/macho/symbol.rs
@@ -0,0 +1,488 @@
+use alloc::vec::Vec;
+use core::fmt::Debug;
+use core::{fmt, slice, str};
+
+use crate::endian::{self, Endianness};
+use crate::macho;
+use crate::pod::Pod;
+use crate::read::util::StringTable;
+use crate::read::{
+ self, ObjectMap, ObjectMapEntry, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result,
+ SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry,
+ SymbolScope, SymbolSection,
+};
+
+use super::{MachHeader, MachOFile};
+
+/// A table of symbol entries in a Mach-O file.
+///
+/// Also includes the string table used for the symbol names.
+#[derive(Debug, Clone, Copy)]
+pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]>
+where
+ R: ReadRef<'data>,
+{
+ symbols: &'data [Mach::Nlist],
+ strings: StringTable<'data, R>,
+}
+
+impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> {
+ fn default() -> Self {
+ SymbolTable {
+ symbols: &[],
+ strings: Default::default(),
+ }
+ }
+}
+
+impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> {
+ #[inline]
+ pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self {
+ SymbolTable { symbols, strings }
+ }
+
+ /// Return the string table used for the symbol names.
+ #[inline]
+ pub fn strings(&self) -> StringTable<'data, R> {
+ self.strings
+ }
+
+ /// Iterate over the symbols.
+ #[inline]
+ pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> {
+ self.symbols.iter()
+ }
+
+ /// Return true if the symbol table is empty.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.symbols.is_empty()
+ }
+
+ /// The number of symbols.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.symbols.len()
+ }
+
+ /// Return the symbol at the given index.
+ pub fn symbol(&self, index: usize) -> Result<&'data Mach::Nlist> {
+ self.symbols
+ .get(index)
+ .read_error("Invalid Mach-O symbol index")
+ }
+
+ /// Construct a map from addresses to a user-defined map entry.
+ pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>(
+ &self,
+ f: F,
+ ) -> SymbolMap<Entry> {
+ let mut symbols = Vec::new();
+ for nlist in self.symbols {
+ if !nlist.is_definition() {
+ continue;
+ }
+ if let Some(entry) = f(nlist) {
+ symbols.push(entry);
+ }
+ }
+ SymbolMap::new(symbols)
+ }
+
+ /// Construct a map from addresses to symbol names and object file names.
+ pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> {
+ let mut symbols = Vec::new();
+ let mut objects = Vec::new();
+ let mut object = None;
+ let mut current_function = None;
+ // Each module starts with one or two N_SO symbols (path, or directory + filename)
+ // and one N_OSO symbol. The module is terminated by an empty N_SO symbol.
+ for nlist in self.symbols {
+ let n_type = nlist.n_type();
+ if n_type & macho::N_STAB == 0 {
+ continue;
+ }
+ // TODO: includes variables too (N_GSYM, N_STSYM). These may need to get their
+ // address from regular symbols though.
+ match n_type {
+ macho::N_SO => {
+ object = None;
+ }
+ macho::N_OSO => {
+ object = None;
+ if let Ok(name) = nlist.name(endian, self.strings) {
+ if !name.is_empty() {
+ object = Some(objects.len());
+ objects.push(name);
+ }
+ }
+ }
+ macho::N_FUN => {
+ if let Ok(name) = nlist.name(endian, self.strings) {
+ if !name.is_empty() {
+ current_function = Some((name, nlist.n_value(endian).into()))
+ } else if let Some((name, address)) = current_function.take() {
+ if let Some(object) = object {
+ symbols.push(ObjectMapEntry {
+ address,
+ size: nlist.n_value(endian).into(),
+ name,
+ object,
+ });
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ ObjectMap {
+ symbols: SymbolMap::new(symbols),
+ objects,
+ }
+ }
+}
+
+/// An iterator over the symbols of a `MachOFile32`.
+pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>;
+/// An iterator over the symbols of a `MachOFile64`.
+pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>;
+
+/// A symbol table of a `MachOFile`.
+#[derive(Debug, Clone, Copy)]
+pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ pub(super) file: &'file MachOFile<'data, Mach, R>,
+}
+
+impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ type Symbol = MachOSymbol<'data, 'file, Mach, R>;
+ type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>;
+
+ fn symbols(&self) -> Self::SymbolIterator {
+ MachOSymbolIterator {
+ file: self.file,
+ index: 0,
+ }
+ }
+
+ fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
+ let nlist = self.file.symbols.symbol(index.0)?;
+ MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index")
+ }
+}
+
+/// An iterator over the symbols of a `MachOFile32`.
+pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
+/// An iterator over the symbols of a `MachOFile64`.
+pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
+
+/// An iterator over the symbols of a `MachOFile`.
+pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ pub(super) file: &'file MachOFile<'data, Mach, R>,
+ pub(super) index: usize,
+}
+
+impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MachOSymbolIterator").finish()
+ }
+}
+
+impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ type Item = MachOSymbol<'data, 'file, Mach, R>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ let index = self.index;
+ let nlist = self.file.symbols.symbols.get(index)?;
+ self.index += 1;
+ if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) {
+ return Some(symbol);
+ }
+ }
+ }
+}
+
+/// A symbol of a `MachOFile32`.
+pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>;
+/// A symbol of a `MachOFile64`.
+pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
+ MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>;
+
+/// A symbol of a `MachOFile`.
+#[derive(Debug, Clone, Copy)]
+pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ file: &'file MachOFile<'data, Mach, R>,
+ index: SymbolIndex,
+ nlist: &'data Mach::Nlist,
+}
+
+impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ pub(super) fn new(
+ file: &'file MachOFile<'data, Mach, R>,
+ index: SymbolIndex,
+ nlist: &'data Mach::Nlist,
+ ) -> Option<Self> {
+ if nlist.n_type() & macho::N_STAB != 0 {
+ return None;
+ }
+ Some(MachOSymbol { file, index, nlist })
+ }
+}
+
+impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+}
+
+impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R>
+where
+ Mach: MachHeader,
+ R: ReadRef<'data>,
+{
+ #[inline]
+ fn index(&self) -> SymbolIndex {
+ self.index
+ }
+
+ fn name_bytes(&self) -> Result<&'data [u8]> {
+ self.nlist.name(self.file.endian, self.file.symbols.strings)
+ }
+
+ fn name(&self) -> Result<&'data str> {
+ let name = self.name_bytes()?;
+ str::from_utf8(name)
+ .ok()
+ .read_error("Non UTF-8 Mach-O symbol name")
+ }
+
+ #[inline]
+ fn address(&self) -> u64 {
+ self.nlist.n_value(self.file.endian).into()
+ }
+
+ #[inline]
+ fn size(&self) -> u64 {
+ 0
+ }
+
+ fn kind(&self) -> SymbolKind {
+ self.section()
+ .index()
+ .and_then(|index| self.file.section_internal(index).ok())
+ .map(|section| match section.kind {
+ SectionKind::Text => SymbolKind::Text,
+ SectionKind::Data
+ | SectionKind::ReadOnlyData
+ | SectionKind::ReadOnlyString
+ | SectionKind::UninitializedData
+ | SectionKind::Common => SymbolKind::Data,
+ SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
+ SymbolKind::Tls
+ }
+ _ => SymbolKind::Unknown,
+ })
+ .unwrap_or(SymbolKind::Unknown)
+ }
+
+ fn section(&self) -> SymbolSection {
+ match self.nlist.n_type() & macho::N_TYPE {
+ macho::N_UNDF => SymbolSection::Undefined,
+ macho::N_ABS => SymbolSection::Absolute,
+ macho::N_SECT => {
+ let n_sect = self.nlist.n_sect();
+ if n_sect != 0 {
+ SymbolSection::Section(SectionIndex(n_sect as usize))
+ } else {
+ SymbolSection::Unknown
+ }
+ }
+ _ => SymbolSection::Unknown,
+ }
+ }
+
+ #[inline]
+ fn is_undefined(&self) -> bool {
+ self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF
+ }
+
+ #[inline]
+ fn is_definition(&self) -> bool {
+ self.nlist.is_definition()
+ }
+
+ #[inline]
+ fn is_common(&self) -> bool {
+ // Mach-O common symbols are based on section, not symbol
+ false
+ }
+
+ #[inline]
+ fn is_weak(&self) -> bool {
+ self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0
+ }
+
+ fn scope(&self) -> SymbolScope {
+ let n_type = self.nlist.n_type();
+ if n_type & macho::N_TYPE == macho::N_UNDF {
+ SymbolScope::Unknown
+ } else if n_type & macho::N_EXT == 0 {
+ SymbolScope::Compilation
+ } else if n_type & macho::N_PEXT != 0 {
+ SymbolScope::Linkage
+ } else {
+ SymbolScope::Dynamic
+ }
+ }
+
+ #[inline]
+ fn is_global(&self) -> bool {
+ self.scope() != SymbolScope::Compilation
+ }
+
+ #[inline]
+ fn is_local(&self) -> bool {
+ self.scope() == SymbolScope::Compilation
+ }
+
+ #[inline]
+ fn flags(&self) -> SymbolFlags<SectionIndex> {
+ let n_desc = self.nlist.n_desc(self.file.endian);
+ SymbolFlags::MachO { n_desc }
+ }
+}
+
+/// A trait for generic access to `Nlist32` and `Nlist64`.
+#[allow(missing_docs)]
+pub trait Nlist: Debug + Pod {
+ type Word: Into<u64>;
+ type Endian: endian::Endian;
+
+ fn n_strx(&self, endian: Self::Endian) -> u32;
+ fn n_type(&self) -> u8;
+ fn n_sect(&self) -> u8;
+ fn n_desc(&self, endian: Self::Endian) -> u16;
+ fn n_value(&self, endian: Self::Endian) -> Self::Word;
+
+ fn name<'data, R: ReadRef<'data>>(
+ &self,
+ endian: Self::Endian,
+ strings: StringTable<'data, R>,
+ ) -> Result<&'data [u8]> {
+ strings
+ .get(self.n_strx(endian))
+ .read_error("Invalid Mach-O symbol name offset")
+ }
+
+ /// Return true if this is a STAB symbol.
+ ///
+ /// This determines the meaning of the `n_type` field.
+ fn is_stab(&self) -> bool {
+ self.n_type() & macho::N_STAB != 0
+ }
+
+ /// Return true if this is an undefined symbol.
+ fn is_undefined(&self) -> bool {
+ let n_type = self.n_type();
+ n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF
+ }
+
+ /// Return true if the symbol is a definition of a function or data object.
+ fn is_definition(&self) -> bool {
+ let n_type = self.n_type();
+ n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF
+ }
+
+ /// Return the library ordinal.
+ ///
+ /// This is either a 1-based index into the dylib load commands,
+ /// or a special ordinal.
+ #[inline]
+ fn library_ordinal(&self, endian: Self::Endian) -> u8 {
+ (self.n_desc(endian) >> 8) as u8
+ }
+}
+
+impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
+ type Word = u32;
+ type Endian = Endian;
+
+ fn n_strx(&self, endian: Self::Endian) -> u32 {
+ self.n_strx.get(endian)
+ }
+ fn n_type(&self) -> u8 {
+ self.n_type
+ }
+ fn n_sect(&self) -> u8 {
+ self.n_sect
+ }
+ fn n_desc(&self, endian: Self::Endian) -> u16 {
+ self.n_desc.get(endian)
+ }
+ fn n_value(&self, endian: Self::Endian) -> Self::Word {
+ self.n_value.get(endian)
+ }
+}
+
+impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
+ type Word = u64;
+ type Endian = Endian;
+
+ fn n_strx(&self, endian: Self::Endian) -> u32 {
+ self.n_strx.get(endian)
+ }
+ fn n_type(&self) -> u8 {
+ self.n_type
+ }
+ fn n_sect(&self) -> u8 {
+ self.n_sect
+ }
+ fn n_desc(&self, endian: Self::Endian) -> u16 {
+ self.n_desc.get(endian)
+ }
+ fn n_value(&self, endian: Self::Endian) -> Self::Word {
+ self.n_value.get(endian)
+ }
+}