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