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(&self) -> Result<&'data str>290     fn name(&self) -> Result<&'data str> {
291         let name = self
292             .nlist
293             .name(self.file.endian, self.file.symbols.strings)?;
294         str::from_utf8(name)
295             .ok()
296             .read_error("Non UTF-8 Mach-O symbol name")
297     }
298 
299     #[inline]
address(&self) -> u64300     fn address(&self) -> u64 {
301         self.nlist.n_value(self.file.endian).into()
302     }
303 
304     #[inline]
size(&self) -> u64305     fn size(&self) -> u64 {
306         0
307     }
308 
kind(&self) -> SymbolKind309     fn kind(&self) -> SymbolKind {
310         self.section()
311             .index()
312             .and_then(|index| self.file.section_internal(index).ok())
313             .map(|section| match section.kind {
314                 SectionKind::Text => SymbolKind::Text,
315                 SectionKind::Data
316                 | SectionKind::ReadOnlyData
317                 | SectionKind::ReadOnlyString
318                 | SectionKind::UninitializedData
319                 | SectionKind::Common => SymbolKind::Data,
320                 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
321                     SymbolKind::Tls
322                 }
323                 _ => SymbolKind::Unknown,
324             })
325             .unwrap_or(SymbolKind::Unknown)
326     }
327 
section(&self) -> SymbolSection328     fn section(&self) -> SymbolSection {
329         match self.nlist.n_type() & macho::N_TYPE {
330             macho::N_UNDF => SymbolSection::Undefined,
331             macho::N_ABS => SymbolSection::Absolute,
332             macho::N_SECT => {
333                 let n_sect = self.nlist.n_sect();
334                 if n_sect != 0 {
335                     SymbolSection::Section(SectionIndex(n_sect as usize))
336                 } else {
337                     SymbolSection::Unknown
338                 }
339             }
340             _ => SymbolSection::Unknown,
341         }
342     }
343 
344     #[inline]
is_undefined(&self) -> bool345     fn is_undefined(&self) -> bool {
346         self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF
347     }
348 
349     #[inline]
is_definition(&self) -> bool350     fn is_definition(&self) -> bool {
351         self.nlist.is_definition()
352     }
353 
354     #[inline]
is_common(&self) -> bool355     fn is_common(&self) -> bool {
356         // Mach-O common symbols are based on section, not symbol
357         false
358     }
359 
360     #[inline]
is_weak(&self) -> bool361     fn is_weak(&self) -> bool {
362         self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0
363     }
364 
scope(&self) -> SymbolScope365     fn scope(&self) -> SymbolScope {
366         let n_type = self.nlist.n_type();
367         if n_type & macho::N_TYPE == macho::N_UNDF {
368             SymbolScope::Unknown
369         } else if n_type & macho::N_EXT == 0 {
370             SymbolScope::Compilation
371         } else if n_type & macho::N_PEXT != 0 {
372             SymbolScope::Linkage
373         } else {
374             SymbolScope::Dynamic
375         }
376     }
377 
378     #[inline]
is_global(&self) -> bool379     fn is_global(&self) -> bool {
380         self.scope() != SymbolScope::Compilation
381     }
382 
383     #[inline]
is_local(&self) -> bool384     fn is_local(&self) -> bool {
385         self.scope() == SymbolScope::Compilation
386     }
387 
388     #[inline]
flags(&self) -> SymbolFlags<SectionIndex>389     fn flags(&self) -> SymbolFlags<SectionIndex> {
390         let n_desc = self.nlist.n_desc(self.file.endian);
391         SymbolFlags::MachO { n_desc }
392     }
393 }
394 
395 /// A trait for generic access to `Nlist32` and `Nlist64`.
396 #[allow(missing_docs)]
397 pub trait Nlist: Debug + Pod {
398     type Word: Into<u64>;
399     type Endian: endian::Endian;
400 
n_strx(&self, endian: Self::Endian) -> u32401     fn n_strx(&self, endian: Self::Endian) -> u32;
n_type(&self) -> u8402     fn n_type(&self) -> u8;
n_sect(&self) -> u8403     fn n_sect(&self) -> u8;
n_desc(&self, endian: Self::Endian) -> u16404     fn n_desc(&self, endian: Self::Endian) -> u16;
n_value(&self, endian: Self::Endian) -> Self::Word405     fn n_value(&self, endian: Self::Endian) -> Self::Word;
406 
name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]>407     fn name<'data, R: ReadRef<'data>>(
408         &self,
409         endian: Self::Endian,
410         strings: StringTable<'data, R>,
411     ) -> Result<&'data [u8]> {
412         strings
413             .get(self.n_strx(endian))
414             .read_error("Invalid Mach-O symbol name offset")
415     }
416 
417     /// Return true if this is a STAB symbol.
418     ///
419     /// This determines the meaning of the `n_type` field.
is_stab(&self) -> bool420     fn is_stab(&self) -> bool {
421         self.n_type() & macho::N_STAB != 0
422     }
423 
424     /// Return true if this is an undefined symbol.
is_undefined(&self) -> bool425     fn is_undefined(&self) -> bool {
426         let n_type = self.n_type();
427         n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF
428     }
429 
430     /// Return true if the symbol is a definition of a function or data object.
is_definition(&self) -> bool431     fn is_definition(&self) -> bool {
432         let n_type = self.n_type();
433         n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF
434     }
435 
436     /// Return the library ordinal.
437     ///
438     /// This is either a 1-based index into the dylib load commands,
439     /// or a special ordinal.
440     #[inline]
library_ordinal(&self, endian: Self::Endian) -> u8441     fn library_ordinal(&self, endian: Self::Endian) -> u8 {
442         (self.n_desc(endian) >> 8) as u8
443     }
444 }
445 
446 impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
447     type Word = u32;
448     type Endian = Endian;
449 
n_strx(&self, endian: Self::Endian) -> u32450     fn n_strx(&self, endian: Self::Endian) -> u32 {
451         self.n_strx.get(endian)
452     }
n_type(&self) -> u8453     fn n_type(&self) -> u8 {
454         self.n_type
455     }
n_sect(&self) -> u8456     fn n_sect(&self) -> u8 {
457         self.n_sect
458     }
n_desc(&self, endian: Self::Endian) -> u16459     fn n_desc(&self, endian: Self::Endian) -> u16 {
460         self.n_desc.get(endian)
461     }
n_value(&self, endian: Self::Endian) -> Self::Word462     fn n_value(&self, endian: Self::Endian) -> Self::Word {
463         self.n_value.get(endian)
464     }
465 }
466 
467 impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
468     type Word = u64;
469     type Endian = Endian;
470 
n_strx(&self, endian: Self::Endian) -> u32471     fn n_strx(&self, endian: Self::Endian) -> u32 {
472         self.n_strx.get(endian)
473     }
n_type(&self) -> u8474     fn n_type(&self) -> u8 {
475         self.n_type
476     }
n_sect(&self) -> u8477     fn n_sect(&self) -> u8 {
478         self.n_sect
479     }
n_desc(&self, endian: Self::Endian) -> u16480     fn n_desc(&self, endian: Self::Endian) -> u16 {
481         self.n_desc.get(endian)
482     }
n_value(&self, endian: Self::Endian) -> Self::Word483     fn n_value(&self, endian: Self::Endian) -> Self::Word {
484         self.n_value.get(endian)
485     }
486 }
487