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