1 use alloc::fmt;
2 use core::convert::TryInto;
3 use core::str;
4 
5 use crate::endian::{LittleEndian as LE, U32Bytes};
6 use crate::pe;
7 use crate::pod::{Bytes, Pod};
8 use crate::read::util::StringTable;
9 use crate::read::{
10     ReadError, Result, SectionIndex, Symbol, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope,
11     SymbolSection,
12 };
13 
14 /// A table of symbol entries in a COFF or PE file.
15 ///
16 /// Also includes the string table used for the symbol names.
17 #[derive(Debug)]
18 pub struct SymbolTable<'data> {
19     symbols: &'data [pe::ImageSymbolBytes],
20     strings: StringTable<'data>,
21 }
22 
23 impl<'data> SymbolTable<'data> {
24     /// Read the symbol table.
parse(header: &pe::ImageFileHeader, mut data: Bytes<'data>) -> Result<Self>25     pub fn parse(header: &pe::ImageFileHeader, mut data: Bytes<'data>) -> Result<Self> {
26         // The symbol table may not be present.
27         let symbol_offset = header.pointer_to_symbol_table.get(LE) as usize;
28         let (symbols, strings) = if symbol_offset != 0 {
29             data.skip(symbol_offset)
30                 .read_error("Invalid COFF symbol table offset")?;
31             let symbols = data
32                 .read_slice(header.number_of_symbols.get(LE) as usize)
33                 .read_error("Invalid COFF symbol table size")?;
34 
35             // Note: don't update data when reading length; the length includes itself.
36             let length = data
37                 .read_at::<U32Bytes<_>>(0)
38                 .read_error("Missing COFF string table")?
39                 .get(LE);
40             let strings = data
41                 .read_bytes(length as usize)
42                 .read_error("Invalid COFF string table length")?;
43 
44             (symbols, strings)
45         } else {
46             (&[][..], Bytes(&[]))
47         };
48 
49         Ok(SymbolTable {
50             symbols,
51             strings: StringTable::new(strings),
52         })
53     }
54 
55     /// Return the string table used for the symbol names.
56     #[inline]
strings(&self) -> StringTable<'data>57     pub fn strings(&self) -> StringTable<'data> {
58         self.strings
59     }
60 
61     /// Return true if the symbol table is empty.
62     #[inline]
is_empty(&self) -> bool63     pub fn is_empty(&self) -> bool {
64         self.symbols.is_empty()
65     }
66 
67     /// The number of symbols.
68     #[inline]
len(&self) -> usize69     pub fn len(&self) -> usize {
70         self.symbols.len()
71     }
72 
73     /// Return the symbol table entry at the given index.
74     #[inline]
symbol(&self, index: usize) -> Option<&'data pe::ImageSymbol>75     pub fn symbol(&self, index: usize) -> Option<&'data pe::ImageSymbol> {
76         self.get::<pe::ImageSymbol>(index)
77     }
78 
79     /// Return the symbol table entry or auxilliary record at the given index.
get<T: Pod>(&self, index: usize) -> Option<&'data T>80     pub fn get<T: Pod>(&self, index: usize) -> Option<&'data T> {
81         let bytes = self.symbols.get(index)?;
82         Bytes(&bytes.0[..]).read().ok()
83     }
84 }
85 
86 impl pe::ImageSymbol {
87     /// Parse a COFF symbol name.
88     ///
89     /// `strings` must be the string table used for symbols names.
name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]>90     pub fn name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]> {
91         if self.name[0] == 0 {
92             // If the name starts with 0 then the last 4 bytes are a string table offset.
93             let offset = u32::from_le_bytes(self.name[4..8].try_into().unwrap());
94             strings
95                 .get(offset)
96                 .read_error("Invalid COFF symbol name offset")
97         } else {
98             // The name is inline and padded with nulls.
99             Ok(match self.name.iter().position(|&x| x == 0) {
100                 Some(end) => &self.name[..end],
101                 None => &self.name[..],
102             })
103         }
104     }
105 }
106 
107 /// An iterator over the symbols of a `CoffFile`.
108 pub struct CoffSymbolIterator<'data, 'file>
109 where
110     'data: 'file,
111 {
112     pub(crate) symbols: &'file SymbolTable<'data>,
113     pub(crate) index: usize,
114 }
115 
116 impl<'data, 'file> fmt::Debug for CoffSymbolIterator<'data, 'file> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         f.debug_struct("CoffSymbolIterator").finish()
119     }
120 }
121 
122 impl<'data, 'file> Iterator for CoffSymbolIterator<'data, 'file> {
123     type Item = (SymbolIndex, Symbol<'data>);
124 
next(&mut self) -> Option<Self::Item>125     fn next(&mut self) -> Option<Self::Item> {
126         let index = self.index;
127         let symbol = self.symbols.get::<pe::ImageSymbol>(index)?;
128         self.index += 1 + symbol.number_of_aux_symbols as usize;
129         Some((
130             SymbolIndex(index),
131             parse_symbol(self.symbols, index, symbol),
132         ))
133     }
134 }
135 
parse_symbol<'data>( symbols: &SymbolTable<'data>, index: usize, symbol: &'data pe::ImageSymbol, ) -> Symbol<'data>136 pub(crate) fn parse_symbol<'data>(
137     symbols: &SymbolTable<'data>,
138     index: usize,
139     symbol: &'data pe::ImageSymbol,
140 ) -> Symbol<'data> {
141     let value = symbol.value.get(LE);
142     let section_number = symbol.section_number.get(LE);
143 
144     let name = if symbol.storage_class == pe::IMAGE_SYM_CLASS_FILE {
145         // The file name is in the following auxiliary symbol.
146         if symbol.number_of_aux_symbols > 0 {
147             symbols.symbols.get(index + 1).map(|s| {
148                 // The name is padded with nulls.
149                 match s.0.iter().position(|&x| x == 0) {
150                     Some(end) => &s.0[..end],
151                     None => &s.0[..],
152                 }
153             })
154         } else {
155             None
156         }
157     } else {
158         symbol.name(symbols.strings()).ok()
159     };
160     let name = name.and_then(|s| str::from_utf8(s).ok());
161 
162     let derived_kind = if symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION {
163         SymbolKind::Text
164     } else {
165         SymbolKind::Data
166     };
167     let mut flags = SymbolFlags::None;
168     // FIXME: symbol.value is a section offset for non-absolute symbols, not an address
169     let (kind, address, size) = match symbol.storage_class {
170         pe::IMAGE_SYM_CLASS_STATIC => {
171             if value == 0 && symbol.number_of_aux_symbols > 0 {
172                 let mut size = 0;
173                 if let Some(aux) = symbols.get::<pe::ImageAuxSymbolSection>(index + 1) {
174                     size = u64::from(aux.length.get(LE));
175                     // TODO: use high_number for bigobj
176                     let number = aux.number.get(LE) as usize;
177                     flags = SymbolFlags::CoffSection {
178                         selection: aux.selection,
179                         associative_section: SectionIndex(number),
180                     };
181                 }
182                 (SymbolKind::Section, 0, size)
183             } else {
184                 (derived_kind, u64::from(value), 0)
185             }
186         }
187         pe::IMAGE_SYM_CLASS_EXTERNAL => {
188             if section_number == pe::IMAGE_SYM_UNDEFINED {
189                 // Common data: symbol.value is the size.
190                 (derived_kind, 0, u64::from(value))
191             } else if symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION
192                 && symbol.number_of_aux_symbols > 0
193             {
194                 let mut size = 0;
195                 if let Some(aux) = symbols.get::<pe::ImageAuxSymbolFunction>(index + 1) {
196                     size = u64::from(aux.total_size.get(LE));
197                 }
198                 (derived_kind, u64::from(value), size)
199             } else {
200                 (derived_kind, u64::from(value), 0)
201             }
202         }
203         pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => (derived_kind, u64::from(value), 0),
204         pe::IMAGE_SYM_CLASS_SECTION => (SymbolKind::Section, 0, 0),
205         pe::IMAGE_SYM_CLASS_FILE => (SymbolKind::File, 0, 0),
206         pe::IMAGE_SYM_CLASS_LABEL => (SymbolKind::Label, u64::from(value), 0),
207         _ => {
208             // No address because symbol.value could mean anything.
209             (SymbolKind::Unknown, 0, 0)
210         }
211     };
212     let section = match section_number {
213         pe::IMAGE_SYM_UNDEFINED => {
214             if symbol.storage_class == pe::IMAGE_SYM_CLASS_EXTERNAL && value != 0 {
215                 SymbolSection::Common
216             } else {
217                 SymbolSection::Undefined
218             }
219         }
220         pe::IMAGE_SYM_ABSOLUTE => SymbolSection::Absolute,
221         pe::IMAGE_SYM_DEBUG => {
222             if symbol.storage_class == pe::IMAGE_SYM_CLASS_FILE {
223                 SymbolSection::None
224             } else {
225                 SymbolSection::Unknown
226             }
227         }
228         index if index > 0 => SymbolSection::Section(SectionIndex(index as usize - 1)),
229         _ => SymbolSection::Unknown,
230     };
231     let weak = symbol.storage_class == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
232     let scope = match symbol.storage_class {
233         _ if section == SymbolSection::Undefined => SymbolScope::Unknown,
234         pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => {
235             // TODO: determine if symbol is exported
236             SymbolScope::Linkage
237         }
238         _ => SymbolScope::Compilation,
239     };
240     Symbol {
241         name,
242         address,
243         size,
244         kind,
245         section,
246         weak,
247         scope,
248         flags,
249     }
250 }
251