symtool-backend 0.3.0

symtool-backend provides the symbol manipulation framework for the symtool utility
Documentation
//! Manipulate Mach-O binaries.

use crate::error::Result;
use crate::patch::{Location, Rooted};
use goblin::container::{Container, Ctx, Endian};
use goblin::mach::load_command::{CommandVariant, SymtabCommand};
use goblin::mach::symbols::Nlist;
use goblin::mach::MachO;
use scroll::ctx::{SizeWith, TryFromCtx};
use scroll::Pread;

fn context_from_macho(macho: &MachO) -> Ctx {
    let container = if macho.is_64 {
        Container::Big
    } else {
        Container::Little
    };
    let endian = if macho.little_endian {
        Endian::Little
    } else {
        Endian::Big
    };
    Ctx::new(container, endian)
}

/// An iterator over a Mach-O symbol table.
pub struct SymtabIter<'a> {
    bytes: &'a [u8],
    ctx: Ctx,
    symoff: usize,
    stroff: usize,
    count: usize,
    index: usize,
}

impl<'a> SymtabIter<'a> {
    /// Construct a `SymtabIter` from a load command.
    ///
    /// Symbol tables are indicated by a `symtab_command`, with type `LC_SYMTAB`.
    pub fn from_load_command(bytes: &'a [u8], command: &SymtabCommand, ctx: Ctx) -> Self {
        Self {
            bytes,
            ctx,
            symoff: command.symoff as usize,
            stroff: command.stroff as usize,
            count: command.nsyms as usize,
            index: 0,
        }
    }

    /// Construct a `SymtabIter` from a Mach-O binary's static symbol table.
    ///
    /// The static symbol table is in the `LC_SYMTAB` load command.
    pub fn from_mach(bytes: &'a [u8], mach: &MachO) -> Option<Self> {
        let ctx = context_from_macho(mach);
        for command in &mach.load_commands {
            if let CommandVariant::Symtab(command) = command.command {
                return Some(Self::from_load_command(bytes, &command, ctx));
            }
        }
        None
    }
}

impl<'a> std::iter::Iterator for SymtabIter<'a> {
    type Item = Result<(Option<Rooted<&'a str>>, Rooted<Nlist>)>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.count {
            None
        } else {
            Some((|| {
                let nlist_offset = self.symoff + self.index * Nlist::size_with(&self.ctx);
                self.index += 1;
                let nlist = {
                    let (nlist, nlist_size) =
                        Nlist::try_from_ctx(&self.bytes[nlist_offset..], self.ctx)?;
                    let location = Location {
                        offset: nlist_offset,
                        size: nlist_size,
                        ctx: self.ctx,
                    };
                    Rooted::new(location, nlist)
                };
                let name = if nlist.n_strx != 0 {
                    let offset = self.stroff + nlist.n_strx as usize;
                    let name: &str = self.bytes.pread(offset)?;
                    let location = Location {
                        offset,
                        size: name.len(),
                        ctx: self.ctx,
                    };
                    Some(Rooted::new(location, name))
                } else {
                    None
                };
                Ok((name, nlist))
            })())
        }
    }
}