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