]>
Commit | Line | Data |
---|---|---|
9c376795 FG |
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> { | |
30 | 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] | |
40 | 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] | |
46 | pub fn strings(&self) -> StringTable<'data, R> { | |
47 | self.strings | |
48 | } | |
49 | ||
50 | /// Iterate over the symbols. | |
51 | #[inline] | |
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] | |
58 | pub fn is_empty(&self) -> bool { | |
59 | self.symbols.is_empty() | |
60 | } | |
61 | ||
62 | /// The number of symbols. | |
63 | #[inline] | |
64 | pub fn len(&self) -> usize { | |
65 | self.symbols.len() | |
66 | } | |
67 | ||
68 | /// Return the symbol at the given index. | |
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. | |
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. | |
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 | ||
178 | fn symbols(&self) -> Self::SymbolIterator { | |
179 | MachOSymbolIterator { | |
180 | file: self.file, | |
181 | index: 0, | |
182 | } | |
183 | } | |
184 | ||
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 | { | |
213 | 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 | ||
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 | { | |
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] | |
286 | fn index(&self) -> SymbolIndex { | |
287 | self.index | |
288 | } | |
289 | ||
290 | fn name_bytes(&self) -> Result<&'data [u8]> { | |
291 | self.nlist.name(self.file.endian, self.file.symbols.strings) | |
292 | } | |
293 | ||
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] | |
302 | fn address(&self) -> u64 { | |
303 | self.nlist.n_value(self.file.endian).into() | |
304 | } | |
305 | ||
306 | #[inline] | |
307 | fn size(&self) -> u64 { | |
308 | 0 | |
309 | } | |
310 | ||
311 | 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 | ||
330 | 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] | |
347 | fn is_undefined(&self) -> bool { | |
348 | self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF | |
349 | } | |
350 | ||
351 | #[inline] | |
352 | fn is_definition(&self) -> bool { | |
353 | self.nlist.is_definition() | |
354 | } | |
355 | ||
356 | #[inline] | |
357 | fn is_common(&self) -> bool { | |
358 | // Mach-O common symbols are based on section, not symbol | |
359 | false | |
360 | } | |
361 | ||
362 | #[inline] | |
363 | fn is_weak(&self) -> bool { | |
364 | self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0 | |
365 | } | |
366 | ||
367 | 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] | |
381 | fn is_global(&self) -> bool { | |
382 | self.scope() != SymbolScope::Compilation | |
383 | } | |
384 | ||
385 | #[inline] | |
386 | fn is_local(&self) -> bool { | |
387 | self.scope() == SymbolScope::Compilation | |
388 | } | |
389 | ||
390 | #[inline] | |
fe692bf9 | 391 | fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { |
9c376795 FG |
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 | ||
403 | fn n_strx(&self, endian: Self::Endian) -> u32; | |
404 | fn n_type(&self) -> u8; | |
405 | fn n_sect(&self) -> u8; | |
406 | fn n_desc(&self, endian: Self::Endian) -> u16; | |
407 | fn n_value(&self, endian: Self::Endian) -> Self::Word; | |
408 | ||
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. | |
422 | fn is_stab(&self) -> bool { | |
423 | self.n_type() & macho::N_STAB != 0 | |
424 | } | |
425 | ||
426 | /// Return true if this is an undefined symbol. | |
427 | 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. | |
433 | 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] | |
443 | 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 | ||
452 | fn n_strx(&self, endian: Self::Endian) -> u32 { | |
453 | self.n_strx.get(endian) | |
454 | } | |
455 | fn n_type(&self) -> u8 { | |
456 | self.n_type | |
457 | } | |
458 | fn n_sect(&self) -> u8 { | |
459 | self.n_sect | |
460 | } | |
461 | fn n_desc(&self, endian: Self::Endian) -> u16 { | |
462 | self.n_desc.get(endian) | |
463 | } | |
464 | 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 | ||
473 | fn n_strx(&self, endian: Self::Endian) -> u32 { | |
474 | self.n_strx.get(endian) | |
475 | } | |
476 | fn n_type(&self) -> u8 { | |
477 | self.n_type | |
478 | } | |
479 | fn n_sect(&self) -> u8 { | |
480 | self.n_sect | |
481 | } | |
482 | fn n_desc(&self, endian: Self::Endian) -> u16 { | |
483 | self.n_desc.get(endian) | |
484 | } | |
485 | fn n_value(&self, endian: Self::Endian) -> Self::Word { | |
486 | self.n_value.get(endian) | |
487 | } | |
488 | } |