object/read/coff/
symbol.rs

1use alloc::fmt;
2use alloc::vec::Vec;
3use core::convert::TryInto;
4use core::fmt::Debug;
5use core::str;
6
7use super::{CoffCommon, CoffHeader, SectionTable};
8use crate::endian::{LittleEndian as LE, U32Bytes};
9use crate::pe;
10use crate::pod::{bytes_of, bytes_of_slice, Pod};
11use crate::read::util::StringTable;
12use crate::read::{
13    self, Bytes, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex,
14    SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection,
15};
16
17/// A table of symbol entries in a COFF or PE file.
18///
19/// Also includes the string table used for the symbol names.
20///
21/// Returned by [`CoffHeader::symbols`] and
22/// [`ImageNtHeaders::symbols`](crate::read::pe::ImageNtHeaders::symbols).
23#[derive(Debug)]
24pub struct SymbolTable<'data, R = &'data [u8], Coff = pe::ImageFileHeader>
25where
26    R: ReadRef<'data>,
27    Coff: CoffHeader,
28{
29    symbols: &'data [Coff::ImageSymbolBytes],
30    strings: StringTable<'data, R>,
31}
32
33impl<'data, R: ReadRef<'data>, Coff: CoffHeader> Default for SymbolTable<'data, R, Coff> {
34    fn default() -> Self {
35        Self {
36            symbols: &[],
37            strings: StringTable::default(),
38        }
39    }
40}
41
42impl<'data, R: ReadRef<'data>, Coff: CoffHeader> SymbolTable<'data, R, Coff> {
43    /// Read the symbol table.
44    pub fn parse(header: &Coff, data: R) -> Result<Self> {
45        // The symbol table may not be present.
46        let mut offset = header.pointer_to_symbol_table().into();
47        let (symbols, strings) = if offset != 0 {
48            let symbols = data
49                .read_slice(&mut offset, header.number_of_symbols() as usize)
50                .read_error("Invalid COFF symbol table offset or size")?;
51
52            // Note: don't update data when reading length; the length includes itself.
53            let length = data
54                .read_at::<U32Bytes<_>>(offset)
55                .read_error("Missing COFF string table")?
56                .get(LE);
57            let str_end = offset
58                .checked_add(length as u64)
59                .read_error("Invalid COFF string table length")?;
60            let strings = StringTable::new(data, offset, str_end);
61
62            (symbols, strings)
63        } else {
64            (&[][..], StringTable::default())
65        };
66
67        Ok(SymbolTable { symbols, strings })
68    }
69
70    /// Return the string table used for the symbol names.
71    #[inline]
72    pub fn strings(&self) -> StringTable<'data, R> {
73        self.strings
74    }
75
76    /// Return true if the symbol table is empty.
77    #[inline]
78    pub fn is_empty(&self) -> bool {
79        self.symbols.is_empty()
80    }
81
82    /// The number of symbol table entries.
83    ///
84    /// This includes auxiliary symbol table entries.
85    #[inline]
86    pub fn len(&self) -> usize {
87        self.symbols.len()
88    }
89
90    /// Iterate over the symbols.
91    #[inline]
92    pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, R, Coff> {
93        SymbolIterator {
94            symbols: self,
95            index: SymbolIndex(0),
96        }
97    }
98
99    /// Return the symbol table entry at the given index.
100    #[inline]
101    pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Coff::ImageSymbol> {
102        self.get::<Coff::ImageSymbol>(index, 0)
103    }
104
105    /// Return the auxiliary function symbol for the symbol table entry at the given index.
106    ///
107    /// Note that the index is of the symbol, not the first auxiliary record.
108    #[inline]
109    pub fn aux_function(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolFunction> {
110        self.get::<pe::ImageAuxSymbolFunction>(index, 1)
111    }
112
113    /// Return the auxiliary section symbol for the symbol table entry at the given index.
114    ///
115    /// Note that the index is of the symbol, not the first auxiliary record.
116    #[inline]
117    pub fn aux_section(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolSection> {
118        self.get::<pe::ImageAuxSymbolSection>(index, 1)
119    }
120
121    /// Return the auxiliary file name for the symbol table entry at the given index.
122    ///
123    /// Note that the index is of the symbol, not the first auxiliary record.
124    pub fn aux_file_name(&self, index: SymbolIndex, aux_count: u8) -> Result<&'data [u8]> {
125        let entries = index
126            .0
127            .checked_add(1)
128            .and_then(|x| Some(x..x.checked_add(aux_count.into())?))
129            .and_then(|x| self.symbols.get(x))
130            .read_error("Invalid COFF symbol index")?;
131        let bytes = bytes_of_slice(entries);
132        // The name is padded with nulls.
133        Ok(match memchr::memchr(b'\0', bytes) {
134            Some(end) => &bytes[..end],
135            None => bytes,
136        })
137    }
138
139    /// Return the symbol table entry or auxiliary record at the given index and offset.
140    pub fn get<T: Pod>(&self, index: SymbolIndex, offset: usize) -> Result<&'data T> {
141        let bytes = index
142            .0
143            .checked_add(offset)
144            .and_then(|x| self.symbols.get(x))
145            .read_error("Invalid COFF symbol index")?;
146        Bytes(bytes_of(bytes))
147            .read()
148            .read_error("Invalid COFF symbol data")
149    }
150
151    /// Construct a map from addresses to a user-defined map entry.
152    pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Coff::ImageSymbol) -> Option<Entry>>(
153        &self,
154        f: F,
155    ) -> SymbolMap<Entry> {
156        let mut symbols = Vec::with_capacity(self.symbols.len());
157        for (_, symbol) in self.iter() {
158            if !symbol.is_definition() {
159                continue;
160            }
161            if let Some(entry) = f(symbol) {
162                symbols.push(entry);
163            }
164        }
165        SymbolMap::new(symbols)
166    }
167}
168
169/// An iterator for symbol entries in a COFF or PE file.
170///
171/// Yields the index and symbol structure for each symbol.
172#[derive(Debug)]
173pub struct SymbolIterator<'data, 'table, R = &'data [u8], Coff = pe::ImageFileHeader>
174where
175    R: ReadRef<'data>,
176    Coff: CoffHeader,
177{
178    symbols: &'table SymbolTable<'data, R, Coff>,
179    index: SymbolIndex,
180}
181
182impl<'data, 'table, R: ReadRef<'data>, Coff: CoffHeader> Iterator
183    for SymbolIterator<'data, 'table, R, Coff>
184{
185    type Item = (SymbolIndex, &'data Coff::ImageSymbol);
186
187    fn next(&mut self) -> Option<Self::Item> {
188        let index = self.index;
189        let symbol = self.symbols.symbol(index).ok()?;
190        self.index.0 += 1 + symbol.number_of_aux_symbols() as usize;
191        Some((index, symbol))
192    }
193}
194
195/// A symbol table in a [`CoffBigFile`](super::CoffBigFile).
196pub type CoffBigSymbolTable<'data, 'file, R = &'data [u8]> =
197    CoffSymbolTable<'data, 'file, R, pe::AnonObjectHeaderBigobj>;
198
199/// A symbol table in a [`CoffFile`](super::CoffFile)
200/// or [`PeFile`](crate::read::pe::PeFile).
201#[derive(Debug, Clone, Copy)]
202pub struct CoffSymbolTable<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader>
203where
204    R: ReadRef<'data>,
205    Coff: CoffHeader,
206{
207    pub(crate) file: &'file CoffCommon<'data, R, Coff>,
208}
209
210impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed
211    for CoffSymbolTable<'data, 'file, R, Coff>
212{
213}
214
215impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbolTable<'data>
216    for CoffSymbolTable<'data, 'file, R, Coff>
217{
218    type Symbol = CoffSymbol<'data, 'file, R, Coff>;
219    type SymbolIterator = CoffSymbolIterator<'data, 'file, R, Coff>;
220
221    fn symbols(&self) -> Self::SymbolIterator {
222        CoffSymbolIterator::new(self.file)
223    }
224
225    fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
226        let symbol = self.file.symbols.symbol(index)?;
227        Ok(CoffSymbol {
228            file: self.file,
229            index,
230            symbol,
231        })
232    }
233}
234
235/// An iterator for the symbols in a [`CoffBigFile`](super::CoffBigFile).
236pub type CoffBigSymbolIterator<'data, 'file, R = &'data [u8]> =
237    CoffSymbolIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>;
238
239/// An iterator for the symbols in a [`CoffFile`](super::CoffFile)
240/// or [`PeFile`](crate::read::pe::PeFile).
241pub struct CoffSymbolIterator<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader>
242where
243    R: ReadRef<'data>,
244    Coff: CoffHeader,
245{
246    file: &'file CoffCommon<'data, R, Coff>,
247    index: SymbolIndex,
248}
249
250impl<'data, 'file, R, Coff> CoffSymbolIterator<'data, 'file, R, Coff>
251where
252    R: ReadRef<'data>,
253    Coff: CoffHeader,
254{
255    pub(crate) fn new(file: &'file CoffCommon<'data, R, Coff>) -> Self {
256        Self {
257            file,
258            index: SymbolIndex(0),
259        }
260    }
261
262    pub(crate) fn empty(file: &'file CoffCommon<'data, R, Coff>) -> Self {
263        Self {
264            file,
265            index: SymbolIndex(file.symbols.len()),
266        }
267    }
268}
269
270impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> fmt::Debug
271    for CoffSymbolIterator<'data, 'file, R, Coff>
272{
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        f.debug_struct("CoffSymbolIterator").finish()
275    }
276}
277
278impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator
279    for CoffSymbolIterator<'data, 'file, R, Coff>
280{
281    type Item = CoffSymbol<'data, 'file, R, Coff>;
282
283    fn next(&mut self) -> Option<Self::Item> {
284        let index = self.index;
285        let symbol = self.file.symbols.symbol(index).ok()?;
286        self.index.0 += 1 + symbol.number_of_aux_symbols() as usize;
287        Some(CoffSymbol {
288            file: self.file,
289            index,
290            symbol,
291        })
292    }
293}
294
295/// A symbol in a [`CoffBigFile`](super::CoffBigFile).
296///
297/// Most functionality is provided by the [`ObjectSymbol`] trait implementation.
298pub type CoffBigSymbol<'data, 'file, R = &'data [u8]> =
299    CoffSymbol<'data, 'file, R, pe::AnonObjectHeaderBigobj>;
300
301/// A symbol in a [`CoffFile`](super::CoffFile) or [`PeFile`](crate::read::pe::PeFile).
302///
303/// Most functionality is provided by the [`ObjectSymbol`] trait implementation.
304#[derive(Debug, Clone, Copy)]
305pub struct CoffSymbol<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader>
306where
307    R: ReadRef<'data>,
308    Coff: CoffHeader,
309{
310    pub(crate) file: &'file CoffCommon<'data, R, Coff>,
311    pub(crate) index: SymbolIndex,
312    pub(crate) symbol: &'data Coff::ImageSymbol,
313}
314
315impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSymbol<'data, 'file, R, Coff> {
316    #[inline]
317    /// Get the raw `ImageSymbol` struct.
318    #[deprecated(note = "Use `coff_symbol` instead")]
319    pub fn raw_symbol(&self) -> &'data Coff::ImageSymbol {
320        self.symbol
321    }
322
323    /// Get the raw `ImageSymbol` struct.
324    pub fn coff_symbol(&self) -> &'data Coff::ImageSymbol {
325        self.symbol
326    }
327}
328
329impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed
330    for CoffSymbol<'data, 'file, R, Coff>
331{
332}
333
334impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbol<'data>
335    for CoffSymbol<'data, 'file, R, Coff>
336{
337    #[inline]
338    fn index(&self) -> SymbolIndex {
339        self.index
340    }
341
342    fn name_bytes(&self) -> read::Result<&'data [u8]> {
343        if self.symbol.has_aux_file_name() {
344            self.file
345                .symbols
346                .aux_file_name(self.index, self.symbol.number_of_aux_symbols())
347        } else {
348            self.symbol.name(self.file.symbols.strings())
349        }
350    }
351
352    fn name(&self) -> read::Result<&'data str> {
353        let name = self.name_bytes()?;
354        str::from_utf8(name)
355            .ok()
356            .read_error("Non UTF-8 COFF symbol name")
357    }
358
359    fn address(&self) -> u64 {
360        self.symbol
361            .address(self.file.image_base, &self.file.sections)
362            .unwrap_or(None)
363            .unwrap_or(0)
364    }
365
366    fn size(&self) -> u64 {
367        match self.symbol.storage_class() {
368            pe::IMAGE_SYM_CLASS_STATIC => {
369                // Section symbols may duplicate the size from the section table.
370                if self.symbol.has_aux_section() {
371                    if let Ok(aux) = self.file.symbols.aux_section(self.index) {
372                        u64::from(aux.length.get(LE))
373                    } else {
374                        0
375                    }
376                } else {
377                    0
378                }
379            }
380            pe::IMAGE_SYM_CLASS_EXTERNAL => {
381                if self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED {
382                    // For undefined symbols, symbol.value is 0 and the size is 0.
383                    // For common data, symbol.value is the size.
384                    u64::from(self.symbol.value())
385                } else if self.symbol.has_aux_function() {
386                    // Function symbols may have a size.
387                    if let Ok(aux) = self.file.symbols.aux_function(self.index) {
388                        u64::from(aux.total_size.get(LE))
389                    } else {
390                        0
391                    }
392                } else {
393                    0
394                }
395            }
396            // Most symbols don't have sizes.
397            _ => 0,
398        }
399    }
400
401    fn kind(&self) -> SymbolKind {
402        let derived_kind = if self.symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION {
403            SymbolKind::Text
404        } else {
405            SymbolKind::Data
406        };
407        match self.symbol.storage_class() {
408            pe::IMAGE_SYM_CLASS_STATIC => {
409                if self.symbol.has_aux_section() {
410                    SymbolKind::Section
411                } else {
412                    derived_kind
413                }
414            }
415            pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => derived_kind,
416            pe::IMAGE_SYM_CLASS_SECTION => SymbolKind::Section,
417            pe::IMAGE_SYM_CLASS_FILE => SymbolKind::File,
418            pe::IMAGE_SYM_CLASS_LABEL => SymbolKind::Label,
419            _ => SymbolKind::Unknown,
420        }
421    }
422
423    fn section(&self) -> SymbolSection {
424        match self.symbol.section_number() {
425            pe::IMAGE_SYM_UNDEFINED => {
426                if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL {
427                    if self.symbol.value() == 0 {
428                        SymbolSection::Undefined
429                    } else {
430                        SymbolSection::Common
431                    }
432                } else if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_SECTION {
433                    SymbolSection::Undefined
434                } else {
435                    SymbolSection::Unknown
436                }
437            }
438            pe::IMAGE_SYM_ABSOLUTE => SymbolSection::Absolute,
439            pe::IMAGE_SYM_DEBUG => {
440                if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_FILE {
441                    SymbolSection::None
442                } else {
443                    SymbolSection::Unknown
444                }
445            }
446            index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)),
447            _ => SymbolSection::Unknown,
448        }
449    }
450
451    #[inline]
452    fn is_undefined(&self) -> bool {
453        self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL
454            && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED
455            && self.symbol.value() == 0
456    }
457
458    #[inline]
459    fn is_definition(&self) -> bool {
460        self.symbol.is_definition()
461    }
462
463    #[inline]
464    fn is_common(&self) -> bool {
465        self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL
466            && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED
467            && self.symbol.value() != 0
468    }
469
470    #[inline]
471    fn is_weak(&self) -> bool {
472        self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL
473    }
474
475    #[inline]
476    fn scope(&self) -> SymbolScope {
477        match self.symbol.storage_class() {
478            pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => {
479                // TODO: determine if symbol is exported
480                SymbolScope::Linkage
481            }
482            _ => SymbolScope::Compilation,
483        }
484    }
485
486    #[inline]
487    fn is_global(&self) -> bool {
488        match self.symbol.storage_class() {
489            pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true,
490            _ => false,
491        }
492    }
493
494    #[inline]
495    fn is_local(&self) -> bool {
496        !self.is_global()
497    }
498
499    fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
500        if self.symbol.has_aux_section() {
501            if let Ok(aux) = self.file.symbols.aux_section(self.index) {
502                let number = if Coff::is_type_bigobj() {
503                    u32::from(aux.number.get(LE)) | (u32::from(aux.high_number.get(LE)) << 16)
504                } else {
505                    u32::from(aux.number.get(LE))
506                };
507                return SymbolFlags::CoffSection {
508                    selection: aux.selection,
509                    associative_section: if number == 0 {
510                        None
511                    } else {
512                        Some(SectionIndex(number as usize))
513                    },
514                };
515            }
516        }
517        SymbolFlags::None
518    }
519}
520
521/// A trait for generic access to [`pe::ImageSymbol`] and [`pe::ImageSymbolEx`].
522#[allow(missing_docs)]
523pub trait ImageSymbol: Debug + Pod {
524    fn raw_name(&self) -> &[u8; 8];
525    fn value(&self) -> u32;
526    fn section_number(&self) -> i32;
527    fn typ(&self) -> u16;
528    fn storage_class(&self) -> u8;
529    fn number_of_aux_symbols(&self) -> u8;
530
531    /// Parse a COFF symbol name.
532    ///
533    /// `strings` must be the string table used for symbol names.
534    fn name<'data, R: ReadRef<'data>>(
535        &'data self,
536        strings: StringTable<'data, R>,
537    ) -> Result<&'data [u8]> {
538        let name = self.raw_name();
539        if name[0] == 0 {
540            // If the name starts with 0 then the last 4 bytes are a string table offset.
541            let offset = u32::from_le_bytes(name[4..8].try_into().unwrap());
542            strings
543                .get(offset)
544                .read_error("Invalid COFF symbol name offset")
545        } else {
546            // The name is inline and padded with nulls.
547            Ok(match memchr::memchr(b'\0', name) {
548                Some(end) => &name[..end],
549                None => &name[..],
550            })
551        }
552    }
553
554    /// Return the symbol address.
555    ///
556    /// This takes into account the image base and the section address,
557    /// and only returns an address for symbols that have an address.
558    fn address(&self, image_base: u64, sections: &SectionTable<'_>) -> Result<Option<u64>> {
559        // Only return an address for storage classes that we know use an address.
560        match self.storage_class() {
561            pe::IMAGE_SYM_CLASS_STATIC
562            | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL
563            | pe::IMAGE_SYM_CLASS_LABEL
564            | pe::IMAGE_SYM_CLASS_EXTERNAL => {}
565            _ => return Ok(None),
566        }
567        let Some(section_index) = self.section() else {
568            return Ok(None);
569        };
570        let section = sections.section(section_index)?;
571        let virtual_address = u64::from(section.virtual_address.get(LE));
572        let value = u64::from(self.value());
573        Ok(Some(image_base + virtual_address + value))
574    }
575
576    /// Return the section index for the symbol.
577    fn section(&self) -> Option<SectionIndex> {
578        let section_number = self.section_number();
579        if section_number > 0 {
580            Some(SectionIndex(section_number as usize))
581        } else {
582            None
583        }
584    }
585
586    /// Return true if the symbol is a definition of a function or data object.
587    fn is_definition(&self) -> bool {
588        if self.section_number() <= 0 {
589            return false;
590        }
591        match self.storage_class() {
592            pe::IMAGE_SYM_CLASS_STATIC => !self.has_aux_section(),
593            pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true,
594            _ => false,
595        }
596    }
597
598    /// Return true if the symbol has an auxiliary file name.
599    fn has_aux_file_name(&self) -> bool {
600        self.number_of_aux_symbols() > 0 && self.storage_class() == pe::IMAGE_SYM_CLASS_FILE
601    }
602
603    /// Return true if the symbol has an auxiliary function symbol.
604    fn has_aux_function(&self) -> bool {
605        self.number_of_aux_symbols() > 0 && self.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION
606    }
607
608    /// Return true if the symbol has an auxiliary section symbol.
609    fn has_aux_section(&self) -> bool {
610        self.number_of_aux_symbols() > 0
611            && self.storage_class() == pe::IMAGE_SYM_CLASS_STATIC
612            && self.typ() == 0
613    }
614
615    fn base_type(&self) -> u16 {
616        self.typ() & pe::N_BTMASK
617    }
618
619    fn derived_type(&self) -> u16 {
620        (self.typ() & pe::N_TMASK) >> pe::N_BTSHFT
621    }
622}
623
624impl ImageSymbol for pe::ImageSymbol {
625    fn raw_name(&self) -> &[u8; 8] {
626        &self.name
627    }
628    fn value(&self) -> u32 {
629        self.value.get(LE)
630    }
631    fn section_number(&self) -> i32 {
632        let section_number = self.section_number.get(LE);
633        if section_number >= pe::IMAGE_SYM_SECTION_MAX {
634            (section_number as i16) as i32
635        } else {
636            section_number as i32
637        }
638    }
639    fn typ(&self) -> u16 {
640        self.typ.get(LE)
641    }
642    fn storage_class(&self) -> u8 {
643        self.storage_class
644    }
645    fn number_of_aux_symbols(&self) -> u8 {
646        self.number_of_aux_symbols
647    }
648}
649
650impl ImageSymbol for pe::ImageSymbolEx {
651    fn raw_name(&self) -> &[u8; 8] {
652        &self.name
653    }
654    fn value(&self) -> u32 {
655        self.value.get(LE)
656    }
657    fn section_number(&self) -> i32 {
658        self.section_number.get(LE)
659    }
660    fn typ(&self) -> u16 {
661        self.typ.get(LE)
662    }
663    fn storage_class(&self) -> u8 {
664        self.storage_class
665    }
666    fn number_of_aux_symbols(&self) -> u8 {
667        self.number_of_aux_symbols
668    }
669}