1 use alloc::vec::Vec;
2 use core::fmt::Debug;
3 use core::{fmt, slice, str};
4 
5 use crate::endian::{self, Endianness};
6 use crate::macho;
7 use crate::pod::Pod;
8 use crate::read::util::StringTable;
9 use crate::read::{
10     self, ObjectMap, ObjectMapEntry, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result,
11     SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry,
12     SymbolScope, SymbolSection,
13 };
14 
15 use super::{MachHeader, MachOFile};
16 
17 /// A table of symbol entries in a Mach-O file.
18 ///
19 /// Also includes the string table used for the symbol names.
20 #[derive(Debug, Clone, Copy)]
21 pub struct SymbolTable<'data, Mach: MachHeader> {
22     symbols: &'data [Mach::Nlist],
23     strings: StringTable<'data>,
24 }
25 
26 impl<'data, Mach: MachHeader> Default for SymbolTable<'data, Mach> {
default() -> Self27     fn default() -> Self {
28         SymbolTable {
29             symbols: &[],
30             strings: Default::default(),
31         }
32     }
33 }
34 
35 impl<'data, Mach: MachHeader> SymbolTable<'data, Mach> {
36     #[inline]
new(symbols: &'data [Mach::Nlist], strings: StringTable<'data>) -> Self37     pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data>) -> Self {
38         SymbolTable { symbols, strings }
39     }
40 
41     /// Return the string table used for the symbol names.
42     #[inline]
strings(&self) -> StringTable<'data>43     pub fn strings(&self) -> StringTable<'data> {
44         self.strings
45     }
46 
47     /// Iterate over the symbols.
48     #[inline]
iter(&self) -> slice::Iter<'data, Mach::Nlist>49     pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> {
50         self.symbols.iter()
51     }
52 
53     /// Return true if the symbol table is empty.
54     #[inline]
is_empty(&self) -> bool55     pub fn is_empty(&self) -> bool {
56         self.symbols.is_empty()
57     }
58 
59     /// The number of symbols.
60     #[inline]
len(&self) -> usize61     pub fn len(&self) -> usize {
62         self.symbols.len()
63     }
64 
65     /// Return the symbol at the given index.
symbol(&self, index: usize) -> Result<&'data Mach::Nlist>66     pub fn symbol(&self, index: usize) -> Result<&'data Mach::Nlist> {
67         self.symbols
68             .get(index)
69             .read_error("Invalid Mach-O symbol index")
70     }
71 
72     /// Construct a map from addresses to a user-defined map entry.
map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>( &self, f: F, ) -> SymbolMap<Entry>73     pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>(
74         &self,
75         f: F,
76     ) -> SymbolMap<Entry> {
77         let mut symbols = Vec::new();
78         for nlist in self.symbols {
79             if !nlist.is_definition() {
80                 continue;
81             }
82             if let Some(entry) = f(nlist) {
83                 symbols.push(entry);
84             }
85         }
86         SymbolMap::new(symbols)
87     }
88 
89     /// Construct a map from addresses to symbol names and object file names.
object_map(&self, endian: Mach::Endian) -> ObjectMap<'data>90     pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> {
91         let mut symbols = Vec::new();
92         let mut objects = Vec::new();
93         let mut object = None;
94         let mut current_function = None;
95         // Each module starts with one or two N_SO symbols (path, or directory + filename)
96         // and one N_OSO symbol. The module is terminated by an empty N_SO symbol.
97         for nlist in self.symbols {
98             let n_type = nlist.n_type();
99             if n_type & macho::N_STAB == 0 {
100                 continue;
101             }
102             // TODO: includes variables too (N_GSYM, N_STSYM). These may need to get their
103             // address from regular symbols though.
104             match n_type {
105                 macho::N_SO => {
106                     object = None;
107                 }
108                 macho::N_OSO => {
109                     object = None;
110                     if let Ok(name) = nlist.name(endian, self.strings) {
111                         if !name.is_empty() {
112                             object = Some(objects.len());
113                             objects.push(name);
114                         }
115                     }
116                 }
117                 macho::N_FUN => {
118                     if let Ok(name) = nlist.name(endian, self.strings) {
119                         if !name.is_empty() {
120                             current_function = Some((name, nlist.n_value(endian).into()))
121                         } else if let Some((name, address)) = current_function.take() {
122                             if let Some(object) = object {
123                                 symbols.push(ObjectMapEntry {
124                                     address,
125                                     size: nlist.n_value(endian).into(),
126                                     name,
127                                     object,
128                                 });
129                             }
130                         }
131                     }
132                 }
133                 _ => {}
134             }
135         }
136         ObjectMap {
137             symbols: SymbolMap::new(symbols),
138             objects,
139         }
140     }
141 }
142 
143 /// An iterator over the symbols of a `MachOFile32`.
144 pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
145     MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>;
146 /// An iterator over the symbols of a `MachOFile64`.
147 pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
148     MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>;
149 
150 /// A symbol table of a `MachOFile`.
151 #[derive(Debug, Clone, Copy)]
152 pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]>
153 where
154     Mach: MachHeader,
155     R: ReadRef<'data>,
156 {
157     pub(super) file: &'file MachOFile<'data, Mach, R>,
158 }
159 
160 impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R>
161 where
162     Mach: MachHeader,
163     R: ReadRef<'data>,
164 {
165 }
166 
167 impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R>
168 where
169     Mach: MachHeader,
170     R: ReadRef<'data>,
171 {
172     type Symbol = MachOSymbol<'data, 'file, Mach, R>;
173     type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>;
174 
symbols(&self) -> Self::SymbolIterator175     fn symbols(&self) -> Self::SymbolIterator {
176         MachOSymbolIterator {
177             file: self.file,
178             index: 0,
179         }
180     }
181 
symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol>182     fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
183         let nlist = self.file.symbols.symbol(index.0)?;
184         MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index")
185     }
186 }
187 
188 /// An iterator over the symbols of a `MachOFile32`.
189 pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
190     MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
191 /// An iterator over the symbols of a `MachOFile64`.
192 pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
193     MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
194 
195 /// An iterator over the symbols of a `MachOFile`.
196 pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]>
197 where
198     Mach: MachHeader,
199     R: ReadRef<'data>,
200 {
201     pub(super) file: &'file MachOFile<'data, Mach, R>,
202     pub(super) index: usize,
203 }
204 
205 impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R>
206 where
207     Mach: MachHeader,
208     R: ReadRef<'data>,
209 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result210     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211         f.debug_struct("MachOSymbolIterator").finish()
212     }
213 }
214 
215 impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R>
216 where
217     Mach: MachHeader,
218     R: ReadRef<'data>,
219 {
220     type Item = MachOSymbol<'data, 'file, Mach, R>;
221 
next(&mut self) -> Option<Self::Item>222     fn next(&mut self) -> Option<Self::Item> {
223         loop {
224             let index = self.index;
225             let nlist = self.file.symbols.symbols.get(index)?;
226             self.index += 1;
227             if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) {
228                 return Some(symbol);
229             }
230         }
231     }
232 }
233 
234 /// A symbol of a `MachOFile32`.
235 pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
236     MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>;
237 /// A symbol of a `MachOFile64`.
238 pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
239     MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>;
240 
241 /// A symbol of a `MachOFile`.
242 #[derive(Debug, Clone, Copy)]
243 pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]>
244 where
245     Mach: MachHeader,
246     R: ReadRef<'data>,
247 {
248     file: &'file MachOFile<'data, Mach, R>,
249     index: SymbolIndex,
250     nlist: &'data Mach::Nlist,
251 }
252 
253 impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R>
254 where
255     Mach: MachHeader,
256     R: ReadRef<'data>,
257 {
new( file: &'file MachOFile<'data, Mach, R>, index: SymbolIndex, nlist: &'data Mach::Nlist, ) -> Option<Self>258     pub(super) fn new(
259         file: &'file MachOFile<'data, Mach, R>,
260         index: SymbolIndex,
261         nlist: &'data Mach::Nlist,
262     ) -> Option<Self> {
263         if nlist.n_type() & macho::N_STAB != 0 {
264             return None;
265         }
266         Some(MachOSymbol { file, index, nlist })
267     }
268 }
269 
270 impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R>
271 where
272     Mach: MachHeader,
273     R: ReadRef<'data>,
274 {
275 }
276 
277 impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R>
278 where
279     Mach: MachHeader,
280     R: ReadRef<'data>,
281 {
282     #[inline]
index(&self) -> SymbolIndex283     fn index(&self) -> SymbolIndex {
284         self.index
285     }
286 
name(&self) -> Result<&'data str>287     fn name(&self) -> Result<&'data str> {
288         let name = self
289             .nlist
290             .name(self.file.endian, self.file.symbols.strings)?;
291         str::from_utf8(name)
292             .ok()
293             .read_error("Non UTF-8 Mach-O symbol name")
294     }
295 
296     #[inline]
address(&self) -> u64297     fn address(&self) -> u64 {
298         self.nlist.n_value(self.file.endian).into()
299     }
300 
301     #[inline]
size(&self) -> u64302     fn size(&self) -> u64 {
303         0
304     }
305 
kind(&self) -> SymbolKind306     fn kind(&self) -> SymbolKind {
307         self.section()
308             .index()
309             .and_then(|index| self.file.section_internal(index).ok())
310             .map(|section| match section.kind {
311                 SectionKind::Text => SymbolKind::Text,
312                 SectionKind::Data
313                 | SectionKind::ReadOnlyData
314                 | SectionKind::ReadOnlyString
315                 | SectionKind::UninitializedData
316                 | SectionKind::Common => SymbolKind::Data,
317                 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
318                     SymbolKind::Tls
319                 }
320                 _ => SymbolKind::Unknown,
321             })
322             .unwrap_or(SymbolKind::Unknown)
323     }
324 
section(&self) -> SymbolSection325     fn section(&self) -> SymbolSection {
326         match self.nlist.n_type() & macho::N_TYPE {
327             macho::N_UNDF => SymbolSection::Undefined,
328             macho::N_ABS => SymbolSection::Absolute,
329             macho::N_SECT => {
330                 let n_sect = self.nlist.n_sect();
331                 if n_sect != 0 {
332                     SymbolSection::Section(SectionIndex(n_sect as usize))
333                 } else {
334                     SymbolSection::Unknown
335                 }
336             }
337             _ => SymbolSection::Unknown,
338         }
339     }
340 
341     #[inline]
is_undefined(&self) -> bool342     fn is_undefined(&self) -> bool {
343         self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF
344     }
345 
346     #[inline]
is_definition(&self) -> bool347     fn is_definition(&self) -> bool {
348         self.nlist.is_definition()
349     }
350 
351     #[inline]
is_common(&self) -> bool352     fn is_common(&self) -> bool {
353         // Mach-O common symbols are based on section, not symbol
354         false
355     }
356 
357     #[inline]
is_weak(&self) -> bool358     fn is_weak(&self) -> bool {
359         self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0
360     }
361 
scope(&self) -> SymbolScope362     fn scope(&self) -> SymbolScope {
363         let n_type = self.nlist.n_type();
364         if n_type & macho::N_TYPE == macho::N_UNDF {
365             SymbolScope::Unknown
366         } else if n_type & macho::N_EXT == 0 {
367             SymbolScope::Compilation
368         } else if n_type & macho::N_PEXT != 0 {
369             SymbolScope::Linkage
370         } else {
371             SymbolScope::Dynamic
372         }
373     }
374 
375     #[inline]
is_global(&self) -> bool376     fn is_global(&self) -> bool {
377         self.scope() != SymbolScope::Compilation
378     }
379 
380     #[inline]
is_local(&self) -> bool381     fn is_local(&self) -> bool {
382         self.scope() == SymbolScope::Compilation
383     }
384 
385     #[inline]
flags(&self) -> SymbolFlags<SectionIndex>386     fn flags(&self) -> SymbolFlags<SectionIndex> {
387         let n_desc = self.nlist.n_desc(self.file.endian);
388         SymbolFlags::MachO { n_desc }
389     }
390 }
391 
392 /// A trait for generic access to `Nlist32` and `Nlist64`.
393 #[allow(missing_docs)]
394 pub trait Nlist: Debug + Pod {
395     type Word: Into<u64>;
396     type Endian: endian::Endian;
397 
n_strx(&self, endian: Self::Endian) -> u32398     fn n_strx(&self, endian: Self::Endian) -> u32;
n_type(&self) -> u8399     fn n_type(&self) -> u8;
n_sect(&self) -> u8400     fn n_sect(&self) -> u8;
n_desc(&self, endian: Self::Endian) -> u16401     fn n_desc(&self, endian: Self::Endian) -> u16;
n_value(&self, endian: Self::Endian) -> Self::Word402     fn n_value(&self, endian: Self::Endian) -> Self::Word;
403 
name<'data>( &self, endian: Self::Endian, strings: StringTable<'data>, ) -> Result<&'data [u8]>404     fn name<'data>(
405         &self,
406         endian: Self::Endian,
407         strings: StringTable<'data>,
408     ) -> Result<&'data [u8]> {
409         strings
410             .get(self.n_strx(endian))
411             .read_error("Invalid Mach-O symbol name offset")
412     }
413 
414     /// Return true if this is a STAB symbol.
415     ///
416     /// This determines the meaning of the `n_type` field.
is_stab(&self) -> bool417     fn is_stab(&self) -> bool {
418         self.n_type() & macho::N_STAB != 0
419     }
420 
421     /// Return true if this is an undefined symbol.
is_undefined(&self) -> bool422     fn is_undefined(&self) -> bool {
423         let n_type = self.n_type();
424         n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF
425     }
426 
427     /// Return true if the symbol is a definition of a function or data object.
is_definition(&self) -> bool428     fn is_definition(&self) -> bool {
429         let n_type = self.n_type();
430         n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF
431     }
432 
433     /// Return the library ordinal.
434     ///
435     /// This is either a 1-based index into the dylib load commands,
436     /// or a special ordinal.
437     #[inline]
library_ordinal(&self, endian: Self::Endian) -> u8438     fn library_ordinal(&self, endian: Self::Endian) -> u8 {
439         (self.n_desc(endian) >> 8) as u8
440     }
441 }
442 
443 impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
444     type Word = u32;
445     type Endian = Endian;
446 
n_strx(&self, endian: Self::Endian) -> u32447     fn n_strx(&self, endian: Self::Endian) -> u32 {
448         self.n_strx.get(endian)
449     }
n_type(&self) -> u8450     fn n_type(&self) -> u8 {
451         self.n_type
452     }
n_sect(&self) -> u8453     fn n_sect(&self) -> u8 {
454         self.n_sect
455     }
n_desc(&self, endian: Self::Endian) -> u16456     fn n_desc(&self, endian: Self::Endian) -> u16 {
457         self.n_desc.get(endian)
458     }
n_value(&self, endian: Self::Endian) -> Self::Word459     fn n_value(&self, endian: Self::Endian) -> Self::Word {
460         self.n_value.get(endian)
461     }
462 }
463 
464 impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
465     type Word = u64;
466     type Endian = Endian;
467 
n_strx(&self, endian: Self::Endian) -> u32468     fn n_strx(&self, endian: Self::Endian) -> u32 {
469         self.n_strx.get(endian)
470     }
n_type(&self) -> u8471     fn n_type(&self) -> u8 {
472         self.n_type
473     }
n_sect(&self) -> u8474     fn n_sect(&self) -> u8 {
475         self.n_sect
476     }
n_desc(&self, endian: Self::Endian) -> u16477     fn n_desc(&self, endian: Self::Endian) -> u16 {
478         self.n_desc.get(endian)
479     }
n_value(&self, endian: Self::Endian) -> Self::Word480     fn n_value(&self, endian: Self::Endian) -> Self::Word {
481         self.n_value.get(endian)
482     }
483 }
484