1use alloc::vec::Vec;
2use core::fmt::Debug;
3use core::{fmt, slice, str};
4
5use crate::endian::{self, Endianness};
6use crate::macho;
7use crate::pod::Pod;
8use crate::read::util::StringTable;
9use crate::read::{
10 self, ObjectMap, ObjectMapEntry, ObjectMapFile, ObjectSymbol, ObjectSymbolTable, ReadError,
11 ReadRef, Result, SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap,
12 SymbolMapEntry, SymbolScope, SymbolSection,
13};
14
15use super::{MachHeader, MachOFile, Section};
16
17#[derive(Debug, Clone, Copy)]
23pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]>
24where
25 R: ReadRef<'data>,
26{
27 symbols: &'data [Mach::Nlist],
28 strings: StringTable<'data, R>,
29}
30
31impl<'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
40impl<'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 #[inline]
48 pub fn strings(&self) -> StringTable<'data, R> {
49 self.strings
50 }
51
52 #[inline]
54 pub fn symbols(&self) -> &'data [Mach::Nlist] {
55 self.symbols
56 }
57
58 #[inline]
60 pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> {
61 self.symbols.iter()
62 }
63
64 #[inline]
66 pub fn enumerate(
67 &self,
68 ) -> impl Iterator<Item = (SymbolIndex, &'data Mach::Nlist)> + use<'data, Mach, R> {
69 self.iter()
70 .enumerate()
71 .map(|(i, sym)| (SymbolIndex(i), sym))
72 }
73
74 #[inline]
76 pub fn is_empty(&self) -> bool {
77 self.symbols.is_empty()
78 }
79
80 #[inline]
82 pub fn len(&self) -> usize {
83 self.symbols.len()
84 }
85
86 pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Mach::Nlist> {
88 self.symbols
89 .get(index.0)
90 .read_error("Invalid Mach-O symbol index")
91 }
92
93 pub fn symbol_name(
95 &self,
96 endian: Mach::Endian,
97 symbol: &'data Mach::Nlist,
98 ) -> read::Result<&'data [u8]> {
99 symbol.name(endian, self.strings)
100 }
101
102 pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>(
104 &self,
105 f: F,
106 ) -> SymbolMap<Entry> {
107 let mut symbols = Vec::new();
108 for nlist in self.symbols {
109 if !nlist.is_definition() {
110 continue;
111 }
112 if let Some(entry) = f(nlist) {
113 symbols.push(entry);
114 }
115 }
116 SymbolMap::new(symbols)
117 }
118
119 pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> {
121 let mut symbols = Vec::new();
122 let mut objects = Vec::new();
123 let mut object = None;
124 let mut current_function = None;
125 for nlist in self.symbols {
128 let Some(n_type) = nlist.n_type().stab() else {
129 continue;
130 };
131 match n_type {
134 macho::N_SO => {
135 object = None;
136 }
137 macho::N_OSO => {
138 object = None;
139 if let Ok(name) = nlist.name(endian, self.strings) {
140 if !name.is_empty() {
141 object = Some(objects.len());
142 let (path, member) = name
145 .split_last()
146 .and_then(|(last, head)| {
147 if *last != b')' {
148 return None;
149 }
150 let index = head.iter().position(|&x| x == b'(')?;
151 let (archive, rest) = head.split_at(index);
152 Some((archive, Some(&rest[1..])))
153 })
154 .unwrap_or((name, None));
155 objects.push(ObjectMapFile::new(path, member));
156 }
157 }
158 }
159 macho::N_FUN => {
160 if let Ok(name) = nlist.name(endian, self.strings) {
161 if !name.is_empty() {
162 current_function = Some((name, nlist.n_value(endian).into()))
163 } else if let Some((name, address)) = current_function.take() {
164 if let Some(object) = object {
165 symbols.push(ObjectMapEntry::new(
166 address,
167 nlist.n_value(endian).into(),
168 name,
169 object,
170 ));
171 }
172 }
173 }
174 }
175 macho::N_STSYM => {
176 if let Ok(name) = nlist.name(endian, self.strings) {
179 if let Some(object) = object {
180 symbols.push(ObjectMapEntry::new(
181 nlist.n_value(endian).into(),
182 0,
183 name,
184 object,
185 ));
186 }
187 }
188 }
189 _ => {}
190 }
191 }
192 ObjectMap::new(symbols, objects)
193 }
194}
195
196pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
198 MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>;
199pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
201 MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>;
202
203#[derive(Debug, Clone, Copy)]
205pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]>
206where
207 Mach: MachHeader,
208 R: ReadRef<'data>,
209{
210 pub(super) file: &'file MachOFile<'data, Mach, R>,
211}
212
213impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R>
214where
215 Mach: MachHeader,
216 R: ReadRef<'data>,
217{
218}
219
220impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R>
221where
222 Mach: MachHeader,
223 R: ReadRef<'data>,
224{
225 type Symbol = MachOSymbol<'data, 'file, Mach, R>;
226 type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>;
227
228 fn symbols(&self) -> Self::SymbolIterator {
229 MachOSymbolIterator::new(self.file)
230 }
231
232 fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
233 let nlist = self.file.symbols.symbol(index)?;
234 MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index")
235 }
236}
237
238pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
240 MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
241pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
243 MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
244
245pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]>
247where
248 Mach: MachHeader,
249 R: ReadRef<'data>,
250{
251 file: &'file MachOFile<'data, Mach, R>,
252 index: SymbolIndex,
253}
254
255impl<'data, 'file, Mach, R> MachOSymbolIterator<'data, 'file, Mach, R>
256where
257 Mach: MachHeader,
258 R: ReadRef<'data>,
259{
260 pub(super) fn new(file: &'file MachOFile<'data, Mach, R>) -> Self {
261 MachOSymbolIterator {
262 file,
263 index: SymbolIndex(0),
264 }
265 }
266
267 pub(super) fn empty(file: &'file MachOFile<'data, Mach, R>) -> Self {
268 MachOSymbolIterator {
269 file,
270 index: SymbolIndex(file.symbols.len()),
271 }
272 }
273}
274
275impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R>
276where
277 Mach: MachHeader,
278 R: ReadRef<'data>,
279{
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("MachOSymbolIterator").finish()
282 }
283}
284
285impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R>
286where
287 Mach: MachHeader,
288 R: ReadRef<'data>,
289{
290 type Item = MachOSymbol<'data, 'file, Mach, R>;
291
292 fn next(&mut self) -> Option<Self::Item> {
293 loop {
294 let index = self.index;
295 let nlist = self.file.symbols.symbols.get(index.0)?;
296 self.index.0 += 1;
297 if let Some(symbol) = MachOSymbol::new(self.file, index, nlist) {
298 return Some(symbol);
299 }
300 }
301 }
302}
303
304pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
306 MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>;
307pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
309 MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>;
310
311#[derive(Debug, Clone, Copy)]
315pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]>
316where
317 Mach: MachHeader,
318 R: ReadRef<'data>,
319{
320 file: &'file MachOFile<'data, Mach, R>,
321 index: SymbolIndex,
322 nlist: &'data Mach::Nlist,
323}
324
325impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R>
326where
327 Mach: MachHeader,
328 R: ReadRef<'data>,
329{
330 pub(super) fn new(
331 file: &'file MachOFile<'data, Mach, R>,
332 index: SymbolIndex,
333 nlist: &'data Mach::Nlist,
334 ) -> Option<Self> {
335 if nlist.n_type().is_stab() {
336 return None;
337 }
338 Some(MachOSymbol { file, index, nlist })
339 }
340
341 pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> {
343 self.file
344 }
345
346 pub fn macho_symbol(&self) -> &'data Mach::Nlist {
348 self.nlist
349 }
350}
351
352impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R>
353where
354 Mach: MachHeader,
355 R: ReadRef<'data>,
356{
357}
358
359impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R>
360where
361 Mach: MachHeader,
362 R: ReadRef<'data>,
363{
364 #[inline]
365 fn index(&self) -> SymbolIndex {
366 self.index
367 }
368
369 fn name_bytes(&self) -> Result<&'data [u8]> {
370 self.nlist.name(self.file.endian, self.file.symbols.strings)
371 }
372
373 fn name(&self) -> Result<&'data str> {
374 let name = self.name_bytes()?;
375 str::from_utf8(name)
376 .ok()
377 .read_error("Non UTF-8 Mach-O symbol name")
378 }
379
380 #[inline]
381 fn address(&self) -> u64 {
382 if self.is_common() {
383 return 0;
385 }
386 self.nlist.n_value(self.file.endian).into()
387 }
388
389 #[inline]
390 fn size(&self) -> u64 {
391 if self.is_common() {
392 return self.nlist.n_value(self.file.endian).into();
393 }
394 0
395 }
396
397 fn kind(&self) -> SymbolKind {
398 if let Some(section) = self
399 .section()
400 .index()
401 .and_then(|index| self.file.section_internal(index).ok())
402 {
403 if let Ok(name) = self.name_bytes() {
404 if self.is_local()
406 && name.len() > 4
407 && name.starts_with(b"ltmp")
408 && name[4..].iter().all(|b| b.is_ascii_digit())
409 && self.address() == section.section.addr(self.file.endian).into()
410 {
411 return SymbolKind::Section;
412 }
413 }
414 match section.kind {
415 SectionKind::Text => SymbolKind::Text,
416 SectionKind::Data
417 | SectionKind::ReadOnlyData
418 | SectionKind::ReadOnlyString
419 | SectionKind::UninitializedData => SymbolKind::Data,
420 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
421 SymbolKind::Tls
422 }
423 _ => SymbolKind::Unknown,
424 }
425 } else if self.is_common() {
426 SymbolKind::Data
427 } else {
428 SymbolKind::Unknown
429 }
430 }
431
432 fn section(&self) -> SymbolSection {
433 match self.nlist.n_type().typ() {
434 macho::N_UNDF => {
435 if self.is_common() {
436 SymbolSection::Common
437 } else {
438 SymbolSection::Undefined
439 }
440 }
441 macho::N_ABS => SymbolSection::Absolute,
442 macho::N_SECT => {
443 let n_sect = self.nlist.n_sect();
444 if n_sect != 0 {
445 SymbolSection::Section(SectionIndex(n_sect as usize))
446 } else {
447 SymbolSection::Unknown
448 }
449 }
450 _ => SymbolSection::Unknown,
451 }
452 }
453
454 #[inline]
455 fn is_undefined(&self) -> bool {
456 self.nlist.is_undefined()
457 }
458
459 #[inline]
460 fn is_definition(&self) -> bool {
461 self.nlist.is_definition()
462 }
463
464 #[inline]
465 fn is_common(&self) -> bool {
466 self.nlist.is_common()
467 }
468
469 #[inline]
470 fn is_weak(&self) -> bool {
471 let n_desc = self.nlist.n_desc(self.file.endian);
472 n_desc.intersects(macho::N_WEAK_REF | macho::N_WEAK_DEF)
473 }
474
475 fn scope(&self) -> SymbolScope {
476 let n_type = self.nlist.n_type();
477 if self.is_undefined() {
478 SymbolScope::Unknown
479 } else if !n_type.is_ext() {
480 SymbolScope::Compilation
481 } else if n_type.is_pext() {
482 SymbolScope::Linkage
483 } else {
484 SymbolScope::Dynamic
485 }
486 }
487
488 #[inline]
489 fn is_global(&self) -> bool {
490 self.scope() != SymbolScope::Compilation
491 }
492
493 #[inline]
494 fn is_local(&self) -> bool {
495 self.scope() == SymbolScope::Compilation
496 }
497
498 #[inline]
499 fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
500 let n_type = self.nlist.n_type();
501 let n_desc = self.nlist.n_desc(self.file.endian);
502 SymbolFlags::MachO { n_type, n_desc }
503 }
504}
505
506#[allow(missing_docs)]
508pub trait Nlist: Debug + Pod + read::private::Sealed {
509 type Word: Into<u64>;
510 type Endian: endian::Endian;
511
512 fn n_strx(&self, endian: Self::Endian) -> u32;
513 fn n_type(&self) -> macho::SymbolFlags;
514 fn n_sect(&self) -> u8;
515 fn n_desc(&self, endian: Self::Endian) -> macho::SymbolDesc;
516 fn n_value(&self, endian: Self::Endian) -> Self::Word;
517
518 fn name<'data, R: ReadRef<'data>>(
519 &self,
520 endian: Self::Endian,
521 strings: StringTable<'data, R>,
522 ) -> Result<&'data [u8]> {
523 strings
524 .get(self.n_strx(endian))
525 .read_error("Invalid Mach-O symbol name offset")
526 }
527
528 fn is_stab(&self) -> bool {
532 self.n_type().is_stab()
533 }
534
535 fn stab(&self) -> Option<macho::SymbolStab> {
537 self.n_type().stab()
538 }
539
540 fn is_undefined(&self) -> bool {
544 let n_type = self.n_type();
545 !n_type.is_stab()
546 && n_type.typ() == macho::N_UNDF
547 && self.n_value(Self::Endian::default()).into() == 0
549 }
550
551 fn is_common(&self) -> bool {
553 let n_type = self.n_type();
554 !n_type.is_stab()
556 && n_type.typ() == macho::N_UNDF
557 && self.n_value(Self::Endian::default()).into() != 0
559 }
560
561 fn is_definition(&self) -> bool {
563 let n_type = self.n_type();
564 !n_type.is_stab() && n_type.typ() == macho::N_SECT
565 }
566
567 #[inline]
572 fn library_ordinal(&self, endian: Self::Endian) -> macho::SymbolLibrary {
573 self.n_desc(endian).library()
574 }
575}
576
577impl<Endian: endian::Endian> read::private::Sealed for macho::Nlist32<Endian> {}
578
579impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
580 type Word = u32;
581 type Endian = Endian;
582
583 fn n_strx(&self, endian: Self::Endian) -> u32 {
584 self.n_strx.get(endian)
585 }
586 fn n_type(&self) -> macho::SymbolFlags {
587 self.n_type
588 }
589 fn n_sect(&self) -> u8 {
590 self.n_sect
591 }
592 fn n_desc(&self, endian: Self::Endian) -> macho::SymbolDesc {
593 self.n_desc.get(endian)
594 }
595 fn n_value(&self, endian: Self::Endian) -> Self::Word {
596 self.n_value.get(endian)
597 }
598}
599
600impl<Endian: endian::Endian> read::private::Sealed for macho::Nlist64<Endian> {}
601
602impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
603 type Word = u64;
604 type Endian = Endian;
605
606 fn n_strx(&self, endian: Self::Endian) -> u32 {
607 self.n_strx.get(endian)
608 }
609 fn n_type(&self) -> macho::SymbolFlags {
610 self.n_type
611 }
612 fn n_sect(&self) -> u8 {
613 self.n_sect
614 }
615 fn n_desc(&self, endian: Self::Endian) -> macho::SymbolDesc {
616 self.n_desc.get(endian)
617 }
618 fn n_value(&self, endian: Self::Endian) -> Self::Word {
619 self.n_value.get(endian)
620 }
621}