symtool_backend/
mach.rs

1//! Manipulate Mach-O binaries.
2
3use crate::error::Result;
4use crate::patch::{Location, Rooted};
5use goblin::container::{Container, Ctx, Endian};
6use goblin::mach::load_command::{CommandVariant, SymtabCommand};
7use goblin::mach::symbols::Nlist;
8use goblin::mach::MachO;
9use scroll::ctx::{SizeWith, TryFromCtx};
10use scroll::Pread;
11
12fn context_from_macho(macho: &MachO) -> Ctx {
13    let container = if macho.is_64 {
14        Container::Big
15    } else {
16        Container::Little
17    };
18    let endian = if macho.little_endian {
19        Endian::Little
20    } else {
21        Endian::Big
22    };
23    Ctx::new(container, endian)
24}
25
26/// An iterator over a Mach-O symbol table.
27pub struct SymtabIter<'a> {
28    bytes: &'a [u8],
29    ctx: Ctx,
30    symoff: usize,
31    stroff: usize,
32    count: usize,
33    index: usize,
34}
35
36impl<'a> SymtabIter<'a> {
37    /// Construct a `SymtabIter` from a load command.
38    ///
39    /// Symbol tables are indicated by a `symtab_command`, with type `LC_SYMTAB`.
40    pub fn from_load_command(bytes: &'a [u8], command: &SymtabCommand, ctx: Ctx) -> Self {
41        Self {
42            bytes,
43            ctx,
44            symoff: command.symoff as usize,
45            stroff: command.stroff as usize,
46            count: command.nsyms as usize,
47            index: 0,
48        }
49    }
50
51    /// Construct a `SymtabIter` from a Mach-O binary's static symbol table.
52    ///
53    /// The static symbol table is in the `LC_SYMTAB` load command.
54    pub fn from_mach(bytes: &'a [u8], mach: &MachO) -> Option<Self> {
55        let ctx = context_from_macho(mach);
56        for command in &mach.load_commands {
57            if let CommandVariant::Symtab(command) = command.command {
58                return Some(Self::from_load_command(bytes, &command, ctx));
59            }
60        }
61        None
62    }
63}
64
65impl<'a> std::iter::Iterator for SymtabIter<'a> {
66    type Item = Result<(Option<Rooted<&'a str>>, Rooted<Nlist>)>;
67
68    fn next(&mut self) -> Option<Self::Item> {
69        if self.index >= self.count {
70            None
71        } else {
72            Some((|| {
73                let nlist_offset = self.symoff + self.index * Nlist::size_with(&self.ctx);
74                self.index += 1;
75                let nlist = {
76                    let (nlist, nlist_size) =
77                        Nlist::try_from_ctx(&self.bytes[nlist_offset..], self.ctx)?;
78                    let location = Location {
79                        offset: nlist_offset,
80                        size: nlist_size,
81                        ctx: self.ctx,
82                    };
83                    Rooted::new(location, nlist)
84                };
85                let name = if nlist.n_strx != 0 {
86                    let offset = self.stroff + nlist.n_strx as usize;
87                    let name: &str = self.bytes.pread(offset)?;
88                    let location = Location {
89                        offset,
90                        size: name.len(),
91                        ctx: self.ctx,
92                    };
93                    Some(Rooted::new(location, name))
94                } else {
95                    None
96                };
97                Ok((name, nlist))
98            })())
99        }
100    }
101}