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, 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> =
145     MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>>;
146 /// An iterator over the symbols of a `MachOFile64`.
147 pub type MachOSymbolTable64<'data, 'file, Endian = Endianness> =
148     MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>>;
149 
150 /// A symbol table of a `MachOFile`.
151 #[derive(Debug, Clone, Copy)]
152 pub struct MachOSymbolTable<'data, 'file, Mach: MachHeader> {
153     pub(super) file: &'file MachOFile<'data, Mach>,
154 }
155 
156 impl<'data, 'file, Mach: MachHeader> read::private::Sealed
157     for MachOSymbolTable<'data, 'file, Mach>
158 {
159 }
160 
161 impl<'data, 'file, Mach: MachHeader> ObjectSymbolTable<'data>
162     for MachOSymbolTable<'data, 'file, Mach>
163 {
164     type Symbol = MachOSymbol<'data, 'file, Mach>;
165     type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach>;
166 
symbols(&self) -> Self::SymbolIterator167     fn symbols(&self) -> Self::SymbolIterator {
168         MachOSymbolIterator {
169             file: self.file,
170             index: 0,
171         }
172     }
173 
symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol>174     fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
175         let nlist = self.file.symbols.symbol(index.0)?;
176         MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index")
177     }
178 }
179 
180 /// An iterator over the symbols of a `MachOFile32`.
181 pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness> =
182     MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>>;
183 /// An iterator over the symbols of a `MachOFile64`.
184 pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness> =
185     MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>>;
186 
187 /// An iterator over the symbols of a `MachOFile`.
188 pub struct MachOSymbolIterator<'data, 'file, Mach: MachHeader> {
189     pub(super) file: &'file MachOFile<'data, Mach>,
190     pub(super) index: usize,
191 }
192 
193 impl<'data, 'file, Mach: MachHeader> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result194     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195         f.debug_struct("MachOSymbolIterator").finish()
196     }
197 }
198 
199 impl<'data, 'file, Mach: MachHeader> Iterator for MachOSymbolIterator<'data, 'file, Mach> {
200     type Item = MachOSymbol<'data, 'file, Mach>;
201 
next(&mut self) -> Option<Self::Item>202     fn next(&mut self) -> Option<Self::Item> {
203         loop {
204             let index = self.index;
205             let nlist = self.file.symbols.symbols.get(index)?;
206             self.index += 1;
207             if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) {
208                 return Some(symbol);
209             }
210         }
211     }
212 }
213 
214 /// A symbol of a `MachOFile32`.
215 pub type MachOSymbol32<'data, 'file, Endian = Endianness> =
216     MachOSymbol<'data, 'file, macho::MachHeader32<Endian>>;
217 /// A symbol of a `MachOFile64`.
218 pub type MachOSymbol64<'data, 'file, Endian = Endianness> =
219     MachOSymbol<'data, 'file, macho::MachHeader64<Endian>>;
220 
221 /// A symbol of a `MachOFile`.
222 #[derive(Debug, Clone, Copy)]
223 pub struct MachOSymbol<'data, 'file, Mach: MachHeader> {
224     file: &'file MachOFile<'data, Mach>,
225     index: SymbolIndex,
226     nlist: &'data Mach::Nlist,
227 }
228 
229 impl<'data, 'file, Mach: MachHeader> MachOSymbol<'data, 'file, Mach> {
new( file: &'file MachOFile<'data, Mach>, index: SymbolIndex, nlist: &'data Mach::Nlist, ) -> Option<Self>230     pub(super) fn new(
231         file: &'file MachOFile<'data, Mach>,
232         index: SymbolIndex,
233         nlist: &'data Mach::Nlist,
234     ) -> Option<Self> {
235         if nlist.n_type() & macho::N_STAB != 0 {
236             return None;
237         }
238         Some(MachOSymbol { file, index, nlist })
239     }
240 }
241 
242 impl<'data, 'file, Mach: MachHeader> read::private::Sealed for MachOSymbol<'data, 'file, Mach> {}
243 
244 impl<'data, 'file, Mach: MachHeader> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach> {
245     #[inline]
index(&self) -> SymbolIndex246     fn index(&self) -> SymbolIndex {
247         self.index
248     }
249 
name(&self) -> Result<&'data str>250     fn name(&self) -> Result<&'data str> {
251         let name = self
252             .nlist
253             .name(self.file.endian, self.file.symbols.strings)?;
254         str::from_utf8(name)
255             .ok()
256             .read_error("Non UTF-8 Mach-O symbol name")
257     }
258 
259     #[inline]
address(&self) -> u64260     fn address(&self) -> u64 {
261         self.nlist.n_value(self.file.endian).into()
262     }
263 
264     #[inline]
size(&self) -> u64265     fn size(&self) -> u64 {
266         0
267     }
268 
kind(&self) -> SymbolKind269     fn kind(&self) -> SymbolKind {
270         self.section()
271             .index()
272             .and_then(|index| self.file.section_internal(index).ok())
273             .map(|section| match section.kind {
274                 SectionKind::Text => SymbolKind::Text,
275                 SectionKind::Data
276                 | SectionKind::ReadOnlyData
277                 | SectionKind::ReadOnlyString
278                 | SectionKind::UninitializedData
279                 | SectionKind::Common => SymbolKind::Data,
280                 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
281                     SymbolKind::Tls
282                 }
283                 _ => SymbolKind::Unknown,
284             })
285             .unwrap_or(SymbolKind::Unknown)
286     }
287 
section(&self) -> SymbolSection288     fn section(&self) -> SymbolSection {
289         match self.nlist.n_type() & macho::N_TYPE {
290             macho::N_UNDF => SymbolSection::Undefined,
291             macho::N_ABS => SymbolSection::Absolute,
292             macho::N_SECT => {
293                 let n_sect = self.nlist.n_sect();
294                 if n_sect != 0 {
295                     SymbolSection::Section(SectionIndex(n_sect as usize))
296                 } else {
297                     SymbolSection::Unknown
298                 }
299             }
300             _ => SymbolSection::Unknown,
301         }
302     }
303 
304     #[inline]
is_undefined(&self) -> bool305     fn is_undefined(&self) -> bool {
306         self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF
307     }
308 
309     #[inline]
is_definition(&self) -> bool310     fn is_definition(&self) -> bool {
311         self.nlist.is_definition()
312     }
313 
314     #[inline]
is_common(&self) -> bool315     fn is_common(&self) -> bool {
316         // Mach-O common symbols are based on section, not symbol
317         false
318     }
319 
320     #[inline]
is_weak(&self) -> bool321     fn is_weak(&self) -> bool {
322         self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0
323     }
324 
scope(&self) -> SymbolScope325     fn scope(&self) -> SymbolScope {
326         let n_type = self.nlist.n_type();
327         if n_type & macho::N_TYPE == macho::N_UNDF {
328             SymbolScope::Unknown
329         } else if n_type & macho::N_EXT == 0 {
330             SymbolScope::Compilation
331         } else if n_type & macho::N_PEXT != 0 {
332             SymbolScope::Linkage
333         } else {
334             SymbolScope::Dynamic
335         }
336     }
337 
338     #[inline]
is_global(&self) -> bool339     fn is_global(&self) -> bool {
340         self.scope() != SymbolScope::Compilation
341     }
342 
343     #[inline]
is_local(&self) -> bool344     fn is_local(&self) -> bool {
345         self.scope() == SymbolScope::Compilation
346     }
347 
348     #[inline]
flags(&self) -> SymbolFlags<SectionIndex>349     fn flags(&self) -> SymbolFlags<SectionIndex> {
350         let n_desc = self.nlist.n_desc(self.file.endian);
351         SymbolFlags::MachO { n_desc }
352     }
353 }
354 
355 /// A trait for generic access to `Nlist32` and `Nlist64`.
356 #[allow(missing_docs)]
357 pub trait Nlist: Debug + Pod {
358     type Word: Into<u64>;
359     type Endian: endian::Endian;
360 
n_strx(&self, endian: Self::Endian) -> u32361     fn n_strx(&self, endian: Self::Endian) -> u32;
n_type(&self) -> u8362     fn n_type(&self) -> u8;
n_sect(&self) -> u8363     fn n_sect(&self) -> u8;
n_desc(&self, endian: Self::Endian) -> u16364     fn n_desc(&self, endian: Self::Endian) -> u16;
n_value(&self, endian: Self::Endian) -> Self::Word365     fn n_value(&self, endian: Self::Endian) -> Self::Word;
366 
name<'data>( &self, endian: Self::Endian, strings: StringTable<'data>, ) -> Result<&'data [u8]>367     fn name<'data>(
368         &self,
369         endian: Self::Endian,
370         strings: StringTable<'data>,
371     ) -> Result<&'data [u8]> {
372         strings
373             .get(self.n_strx(endian))
374             .read_error("Invalid Mach-O symbol name offset")
375     }
376 
377     /// Return true if the symbol is a definition of a function or data object.
is_definition(&self) -> bool378     fn is_definition(&self) -> bool {
379         let n_type = self.n_type();
380         n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF
381     }
382 }
383 
384 impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
385     type Word = u32;
386     type Endian = Endian;
387 
n_strx(&self, endian: Self::Endian) -> u32388     fn n_strx(&self, endian: Self::Endian) -> u32 {
389         self.n_strx.get(endian)
390     }
n_type(&self) -> u8391     fn n_type(&self) -> u8 {
392         self.n_type
393     }
n_sect(&self) -> u8394     fn n_sect(&self) -> u8 {
395         self.n_sect
396     }
n_desc(&self, endian: Self::Endian) -> u16397     fn n_desc(&self, endian: Self::Endian) -> u16 {
398         self.n_desc.get(endian)
399     }
n_value(&self, endian: Self::Endian) -> Self::Word400     fn n_value(&self, endian: Self::Endian) -> Self::Word {
401         self.n_value.get(endian)
402     }
403 }
404 
405 impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
406     type Word = u64;
407     type Endian = Endian;
408 
n_strx(&self, endian: Self::Endian) -> u32409     fn n_strx(&self, endian: Self::Endian) -> u32 {
410         self.n_strx.get(endian)
411     }
n_type(&self) -> u8412     fn n_type(&self) -> u8 {
413         self.n_type
414     }
n_sect(&self) -> u8415     fn n_sect(&self) -> u8 {
416         self.n_sect
417     }
n_desc(&self, endian: Self::Endian) -> u16418     fn n_desc(&self, endian: Self::Endian) -> u16 {
419         self.n_desc.get(endian)
420     }
n_value(&self, endian: Self::Endian) -> Self::Word421     fn n_value(&self, endian: Self::Endian) -> Self::Word {
422         self.n_value.get(endian)
423     }
424 }
425