]>
Commit | Line | Data |
---|---|---|
17df50a5 XL |
1 | use alloc::fmt; |
2 | use alloc::vec::Vec; | |
3 | use core::convert::TryInto; | |
4 | use core::str; | |
5 | ||
6 | use super::{CoffCommon, SectionTable}; | |
7 | use crate::endian::{LittleEndian as LE, U32Bytes}; | |
8 | use crate::pe; | |
136023e0 | 9 | use crate::pod::{bytes_of_slice, Pod}; |
17df50a5 XL |
10 | use crate::read::util::StringTable; |
11 | use crate::read::{ | |
136023e0 XL |
12 | self, Bytes, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, |
13 | SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, | |
17df50a5 XL |
14 | }; |
15 | ||
16 | /// A table of symbol entries in a COFF or PE file. | |
17 | /// | |
18 | /// Also includes the string table used for the symbol names. | |
19 | #[derive(Debug)] | |
136023e0 XL |
20 | pub struct SymbolTable<'data, R = &'data [u8]> |
21 | where | |
22 | R: ReadRef<'data>, | |
23 | { | |
17df50a5 | 24 | symbols: &'data [pe::ImageSymbolBytes], |
136023e0 | 25 | strings: StringTable<'data, R>, |
17df50a5 XL |
26 | } |
27 | ||
136023e0 | 28 | impl<'data, R: ReadRef<'data>> SymbolTable<'data, R> { |
17df50a5 | 29 | /// Read the symbol table. |
136023e0 | 30 | pub fn parse(header: &pe::ImageFileHeader, data: R) -> Result<Self> { |
17df50a5 XL |
31 | // The symbol table may not be present. |
32 | let mut offset = header.pointer_to_symbol_table.get(LE).into(); | |
33 | let (symbols, strings) = if offset != 0 { | |
34 | let symbols = data | |
35 | .read_slice(&mut offset, header.number_of_symbols.get(LE) as usize) | |
36 | .read_error("Invalid COFF symbol table offset or size")?; | |
37 | ||
38 | // Note: don't update data when reading length; the length includes itself. | |
39 | let length = data | |
40 | .read_at::<U32Bytes<_>>(offset) | |
41 | .read_error("Missing COFF string table")? | |
42 | .get(LE); | |
136023e0 XL |
43 | let str_end = offset |
44 | .checked_add(length as u64) | |
17df50a5 | 45 | .read_error("Invalid COFF string table length")?; |
136023e0 | 46 | let strings = StringTable::new(data, offset, str_end); |
17df50a5 XL |
47 | |
48 | (symbols, strings) | |
49 | } else { | |
136023e0 | 50 | (&[][..], StringTable::default()) |
17df50a5 XL |
51 | }; |
52 | ||
136023e0 | 53 | Ok(SymbolTable { symbols, strings }) |
17df50a5 XL |
54 | } |
55 | ||
56 | /// Return the string table used for the symbol names. | |
57 | #[inline] | |
136023e0 | 58 | pub fn strings(&self) -> StringTable<'data, R> { |
17df50a5 XL |
59 | self.strings |
60 | } | |
61 | ||
62 | /// Return true if the symbol table is empty. | |
63 | #[inline] | |
64 | pub fn is_empty(&self) -> bool { | |
65 | self.symbols.is_empty() | |
66 | } | |
67 | ||
68 | /// The number of symbol table entries. | |
69 | /// | |
70 | /// This includes auxiliary symbol table entries. | |
71 | #[inline] | |
72 | pub fn len(&self) -> usize { | |
73 | self.symbols.len() | |
74 | } | |
75 | ||
76 | /// Iterate over the symbols. | |
77 | #[inline] | |
136023e0 | 78 | pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, R> { |
17df50a5 XL |
79 | SymbolIterator { |
80 | symbols: self, | |
81 | index: 0, | |
82 | } | |
83 | } | |
84 | ||
85 | /// Return the symbol table entry at the given index. | |
86 | #[inline] | |
87 | pub fn symbol(&self, index: usize) -> Result<&'data pe::ImageSymbol> { | |
88 | self.get::<pe::ImageSymbol>(index, 0) | |
89 | } | |
90 | ||
91 | /// Return the auxiliary function symbol for the symbol table entry at the given index. | |
92 | /// | |
93 | /// Note that the index is of the symbol, not the first auxiliary record. | |
94 | #[inline] | |
95 | pub fn aux_function(&self, index: usize) -> Result<&'data pe::ImageAuxSymbolFunction> { | |
96 | self.get::<pe::ImageAuxSymbolFunction>(index, 1) | |
97 | } | |
98 | ||
99 | /// Return the auxiliary section symbol for the symbol table entry at the given index. | |
100 | /// | |
101 | /// Note that the index is of the symbol, not the first auxiliary record. | |
102 | #[inline] | |
103 | pub fn aux_section(&self, index: usize) -> Result<&'data pe::ImageAuxSymbolSection> { | |
104 | self.get::<pe::ImageAuxSymbolSection>(index, 1) | |
105 | } | |
106 | ||
107 | /// Return the auxiliary file name for the symbol table entry at the given index. | |
108 | /// | |
109 | /// Note that the index is of the symbol, not the first auxiliary record. | |
110 | pub fn aux_file_name(&self, index: usize, aux_count: u8) -> Result<&'data [u8]> { | |
111 | let entries = index | |
112 | .checked_add(1) | |
113 | .and_then(|x| Some(x..x.checked_add(aux_count.into())?)) | |
114 | .and_then(|x| self.symbols.get(x)) | |
115 | .read_error("Invalid COFF symbol index")?; | |
116 | let bytes = bytes_of_slice(entries); | |
117 | // The name is padded with nulls. | |
118 | Ok(match memchr::memchr(b'\0', bytes) { | |
119 | Some(end) => &bytes[..end], | |
120 | None => bytes, | |
121 | }) | |
122 | } | |
123 | ||
124 | /// Return the symbol table entry or auxiliary record at the given index and offset. | |
125 | pub fn get<T: Pod>(&self, index: usize, offset: usize) -> Result<&'data T> { | |
126 | let bytes = index | |
127 | .checked_add(offset) | |
128 | .and_then(|x| self.symbols.get(x)) | |
129 | .read_error("Invalid COFF symbol index")?; | |
130 | Bytes(&bytes.0[..]) | |
131 | .read() | |
132 | .read_error("Invalid COFF symbol data") | |
133 | } | |
134 | ||
135 | /// Construct a map from addresses to a user-defined map entry. | |
136 | pub fn map<Entry: SymbolMapEntry, F: Fn(&'data pe::ImageSymbol) -> Option<Entry>>( | |
137 | &self, | |
138 | f: F, | |
139 | ) -> SymbolMap<Entry> { | |
140 | let mut symbols = Vec::with_capacity(self.symbols.len()); | |
141 | for (_, symbol) in self.iter() { | |
142 | if !symbol.is_definition() { | |
143 | continue; | |
144 | } | |
145 | if let Some(entry) = f(symbol) { | |
146 | symbols.push(entry); | |
147 | } | |
148 | } | |
149 | SymbolMap::new(symbols) | |
150 | } | |
151 | } | |
152 | ||
153 | /// An iterator for symbol entries in a COFF or PE file. | |
154 | /// | |
155 | /// Yields the index and symbol structure for each symbol. | |
156 | #[derive(Debug)] | |
136023e0 XL |
157 | pub struct SymbolIterator<'data, 'table, R = &'data [u8]> |
158 | where | |
159 | R: ReadRef<'data>, | |
160 | { | |
161 | symbols: &'table SymbolTable<'data, R>, | |
17df50a5 XL |
162 | index: usize, |
163 | } | |
164 | ||
136023e0 | 165 | impl<'data, 'table, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'table, R> { |
17df50a5 XL |
166 | type Item = (usize, &'data pe::ImageSymbol); |
167 | ||
168 | fn next(&mut self) -> Option<Self::Item> { | |
169 | let index = self.index; | |
170 | let symbol = self.symbols.symbol(index).ok()?; | |
171 | self.index += 1 + symbol.number_of_aux_symbols as usize; | |
172 | Some((index, symbol)) | |
173 | } | |
174 | } | |
175 | ||
176 | impl pe::ImageSymbol { | |
177 | /// Parse a COFF symbol name. | |
178 | /// | |
179 | /// `strings` must be the string table used for symbol names. | |
136023e0 XL |
180 | pub fn name<'data, R: ReadRef<'data>>( |
181 | &'data self, | |
182 | strings: StringTable<'data, R>, | |
183 | ) -> Result<&'data [u8]> { | |
17df50a5 XL |
184 | if self.name[0] == 0 { |
185 | // If the name starts with 0 then the last 4 bytes are a string table offset. | |
186 | let offset = u32::from_le_bytes(self.name[4..8].try_into().unwrap()); | |
187 | strings | |
188 | .get(offset) | |
189 | .read_error("Invalid COFF symbol name offset") | |
190 | } else { | |
191 | // The name is inline and padded with nulls. | |
192 | Ok(match memchr::memchr(b'\0', &self.name) { | |
193 | Some(end) => &self.name[..end], | |
194 | None => &self.name[..], | |
195 | }) | |
196 | } | |
197 | } | |
198 | ||
199 | /// Return the symbol address. | |
200 | /// | |
201 | /// This takes into account the image base and the section address. | |
202 | pub fn address(&self, image_base: u64, sections: &SectionTable) -> Result<u64> { | |
203 | let section_number = self.section_number.get(LE) as usize; | |
204 | let section = sections.section(section_number)?; | |
205 | let virtual_address = u64::from(section.virtual_address.get(LE)); | |
206 | let value = u64::from(self.value.get(LE)); | |
207 | Ok(image_base + virtual_address + value) | |
208 | } | |
209 | ||
210 | /// Return true if the symbol is a definition of a function or data object. | |
211 | pub fn is_definition(&self) -> bool { | |
212 | let section_number = self.section_number.get(LE); | |
213 | if section_number == pe::IMAGE_SYM_UNDEFINED { | |
214 | return false; | |
215 | } | |
216 | match self.storage_class { | |
217 | pe::IMAGE_SYM_CLASS_STATIC => { | |
218 | // Exclude section symbols. | |
219 | !(self.value.get(LE) == 0 && self.number_of_aux_symbols > 0) | |
220 | } | |
221 | pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, | |
222 | _ => false, | |
223 | } | |
224 | } | |
225 | ||
226 | /// Return true if the symbol has an auxiliary file name. | |
227 | pub fn has_aux_file_name(&self) -> bool { | |
228 | self.number_of_aux_symbols > 0 && self.storage_class == pe::IMAGE_SYM_CLASS_FILE | |
229 | } | |
230 | ||
231 | /// Return true if the symbol has an auxiliary function symbol. | |
232 | pub fn has_aux_function(&self) -> bool { | |
233 | self.number_of_aux_symbols > 0 && self.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION | |
234 | } | |
235 | ||
236 | /// Return true if the symbol has an auxiliary section symbol. | |
237 | pub fn has_aux_section(&self) -> bool { | |
238 | self.number_of_aux_symbols > 0 | |
239 | && self.storage_class == pe::IMAGE_SYM_CLASS_STATIC | |
240 | && self.value.get(LE) == 0 | |
241 | } | |
242 | } | |
243 | ||
244 | /// A symbol table of a `CoffFile`. | |
245 | #[derive(Debug, Clone, Copy)] | |
136023e0 XL |
246 | pub struct CoffSymbolTable<'data, 'file, R = &'data [u8]> |
247 | where | |
248 | R: ReadRef<'data>, | |
249 | { | |
250 | pub(crate) file: &'file CoffCommon<'data, R>, | |
17df50a5 XL |
251 | } |
252 | ||
136023e0 | 253 | impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSymbolTable<'data, 'file, R> {} |
17df50a5 | 254 | |
136023e0 XL |
255 | impl<'data, 'file, R: ReadRef<'data>> ObjectSymbolTable<'data> |
256 | for CoffSymbolTable<'data, 'file, R> | |
257 | { | |
258 | type Symbol = CoffSymbol<'data, 'file, R>; | |
259 | type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; | |
17df50a5 XL |
260 | |
261 | fn symbols(&self) -> Self::SymbolIterator { | |
262 | CoffSymbolIterator { | |
263 | file: self.file, | |
264 | index: 0, | |
265 | } | |
266 | } | |
267 | ||
268 | fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { | |
269 | let symbol = self.file.symbols.symbol(index.0)?; | |
270 | Ok(CoffSymbol { | |
271 | file: self.file, | |
272 | index, | |
273 | symbol, | |
274 | }) | |
275 | } | |
276 | } | |
277 | ||
278 | /// An iterator over the symbols of a `CoffFile`. | |
136023e0 XL |
279 | pub struct CoffSymbolIterator<'data, 'file, R = &'data [u8]> |
280 | where | |
281 | R: ReadRef<'data>, | |
282 | { | |
283 | pub(crate) file: &'file CoffCommon<'data, R>, | |
17df50a5 XL |
284 | pub(crate) index: usize, |
285 | } | |
286 | ||
136023e0 | 287 | impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for CoffSymbolIterator<'data, 'file, R> { |
17df50a5 XL |
288 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
289 | f.debug_struct("CoffSymbolIterator").finish() | |
290 | } | |
291 | } | |
292 | ||
136023e0 XL |
293 | impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffSymbolIterator<'data, 'file, R> { |
294 | type Item = CoffSymbol<'data, 'file, R>; | |
17df50a5 XL |
295 | |
296 | fn next(&mut self) -> Option<Self::Item> { | |
297 | let index = self.index; | |
298 | let symbol = self.file.symbols.symbol(index).ok()?; | |
299 | self.index += 1 + symbol.number_of_aux_symbols as usize; | |
300 | Some(CoffSymbol { | |
301 | file: self.file, | |
302 | index: SymbolIndex(index), | |
303 | symbol, | |
304 | }) | |
305 | } | |
306 | } | |
307 | ||
308 | /// A symbol of a `CoffFile`. | |
309 | #[derive(Debug, Clone, Copy)] | |
136023e0 XL |
310 | pub struct CoffSymbol<'data, 'file, R = &'data [u8]> |
311 | where | |
312 | R: ReadRef<'data>, | |
313 | { | |
314 | pub(crate) file: &'file CoffCommon<'data, R>, | |
17df50a5 XL |
315 | pub(crate) index: SymbolIndex, |
316 | pub(crate) symbol: &'data pe::ImageSymbol, | |
317 | } | |
318 | ||
136023e0 | 319 | impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSymbol<'data, 'file, R> {} |
17df50a5 | 320 | |
136023e0 | 321 | impl<'data, 'file, R: ReadRef<'data>> ObjectSymbol<'data> for CoffSymbol<'data, 'file, R> { |
17df50a5 XL |
322 | #[inline] |
323 | fn index(&self) -> SymbolIndex { | |
324 | self.index | |
325 | } | |
326 | ||
327 | fn name(&self) -> read::Result<&'data str> { | |
328 | let name = if self.symbol.has_aux_file_name() { | |
329 | self.file | |
330 | .symbols | |
331 | .aux_file_name(self.index.0, self.symbol.number_of_aux_symbols)? | |
332 | } else { | |
333 | self.symbol.name(self.file.symbols.strings())? | |
334 | }; | |
335 | str::from_utf8(name) | |
336 | .ok() | |
337 | .read_error("Non UTF-8 COFF symbol name") | |
338 | } | |
339 | ||
340 | fn address(&self) -> u64 { | |
341 | // Only return an address for storage classes that we know use an address. | |
342 | match self.symbol.storage_class { | |
343 | pe::IMAGE_SYM_CLASS_STATIC | |
344 | | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL | |
345 | | pe::IMAGE_SYM_CLASS_LABEL => {} | |
346 | pe::IMAGE_SYM_CLASS_EXTERNAL => { | |
347 | if self.symbol.section_number.get(LE) == pe::IMAGE_SYM_UNDEFINED { | |
348 | // Undefined or common data, neither of which have an address. | |
349 | return 0; | |
350 | } | |
351 | } | |
352 | _ => return 0, | |
353 | } | |
354 | self.symbol | |
355 | .address(self.file.image_base, &self.file.sections) | |
356 | .unwrap_or(0) | |
357 | } | |
358 | ||
359 | fn size(&self) -> u64 { | |
360 | match self.symbol.storage_class { | |
361 | pe::IMAGE_SYM_CLASS_STATIC => { | |
362 | // Section symbols may duplicate the size from the section table. | |
363 | if self.symbol.has_aux_section() { | |
364 | if let Ok(aux) = self.file.symbols.aux_section(self.index.0) { | |
365 | u64::from(aux.length.get(LE)) | |
366 | } else { | |
367 | 0 | |
368 | } | |
369 | } else { | |
370 | 0 | |
371 | } | |
372 | } | |
373 | pe::IMAGE_SYM_CLASS_EXTERNAL => { | |
374 | if self.symbol.section_number.get(LE) == pe::IMAGE_SYM_UNDEFINED { | |
375 | // For undefined symbols, symbol.value is 0 and the size is 0. | |
376 | // For common data, symbol.value is the size. | |
377 | u64::from(self.symbol.value.get(LE)) | |
378 | } else if self.symbol.has_aux_function() { | |
379 | // Function symbols may have a size. | |
380 | if let Ok(aux) = self.file.symbols.aux_function(self.index.0) { | |
381 | u64::from(aux.total_size.get(LE)) | |
382 | } else { | |
383 | 0 | |
384 | } | |
385 | } else { | |
386 | 0 | |
387 | } | |
388 | } | |
389 | // Most symbols don't have sizes. | |
390 | _ => 0, | |
391 | } | |
392 | } | |
393 | ||
394 | fn kind(&self) -> SymbolKind { | |
395 | let derived_kind = if self.symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION { | |
396 | SymbolKind::Text | |
397 | } else { | |
398 | SymbolKind::Data | |
399 | }; | |
400 | match self.symbol.storage_class { | |
401 | pe::IMAGE_SYM_CLASS_STATIC => { | |
402 | if self.symbol.value.get(LE) == 0 && self.symbol.number_of_aux_symbols > 0 { | |
403 | SymbolKind::Section | |
404 | } else { | |
405 | derived_kind | |
406 | } | |
407 | } | |
408 | pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => derived_kind, | |
409 | pe::IMAGE_SYM_CLASS_SECTION => SymbolKind::Section, | |
410 | pe::IMAGE_SYM_CLASS_FILE => SymbolKind::File, | |
411 | pe::IMAGE_SYM_CLASS_LABEL => SymbolKind::Label, | |
412 | _ => SymbolKind::Unknown, | |
413 | } | |
414 | } | |
415 | ||
416 | fn section(&self) -> SymbolSection { | |
417 | match self.symbol.section_number.get(LE) { | |
418 | pe::IMAGE_SYM_UNDEFINED => { | |
419 | if self.symbol.storage_class == pe::IMAGE_SYM_CLASS_EXTERNAL | |
420 | && self.symbol.value.get(LE) == 0 | |
421 | { | |
422 | SymbolSection::Undefined | |
423 | } else { | |
424 | SymbolSection::Common | |
425 | } | |
426 | } | |
427 | pe::IMAGE_SYM_ABSOLUTE => SymbolSection::Absolute, | |
428 | pe::IMAGE_SYM_DEBUG => { | |
429 | if self.symbol.storage_class == pe::IMAGE_SYM_CLASS_FILE { | |
430 | SymbolSection::None | |
431 | } else { | |
432 | SymbolSection::Unknown | |
433 | } | |
434 | } | |
435 | index if index > 0 => SymbolSection::Section(SectionIndex(index.into())), | |
436 | _ => SymbolSection::Unknown, | |
437 | } | |
438 | } | |
439 | ||
440 | #[inline] | |
441 | fn is_undefined(&self) -> bool { | |
442 | self.symbol.storage_class == pe::IMAGE_SYM_CLASS_EXTERNAL | |
443 | && self.symbol.section_number.get(LE) == pe::IMAGE_SYM_UNDEFINED | |
444 | && self.symbol.value.get(LE) == 0 | |
445 | } | |
446 | ||
447 | #[inline] | |
448 | fn is_definition(&self) -> bool { | |
449 | self.symbol.is_definition() | |
450 | } | |
451 | ||
452 | #[inline] | |
453 | fn is_common(&self) -> bool { | |
454 | self.symbol.storage_class == pe::IMAGE_SYM_CLASS_EXTERNAL | |
455 | && self.symbol.section_number.get(LE) == pe::IMAGE_SYM_UNDEFINED | |
456 | && self.symbol.value.get(LE) != 0 | |
457 | } | |
458 | ||
459 | #[inline] | |
460 | fn is_weak(&self) -> bool { | |
461 | self.symbol.storage_class == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL | |
462 | } | |
463 | ||
464 | #[inline] | |
465 | fn scope(&self) -> SymbolScope { | |
466 | match self.symbol.storage_class { | |
467 | pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => { | |
468 | // TODO: determine if symbol is exported | |
469 | SymbolScope::Linkage | |
470 | } | |
471 | _ => SymbolScope::Compilation, | |
472 | } | |
473 | } | |
474 | ||
475 | #[inline] | |
476 | fn is_global(&self) -> bool { | |
477 | match self.symbol.storage_class { | |
478 | pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, | |
479 | _ => false, | |
480 | } | |
481 | } | |
482 | ||
483 | #[inline] | |
484 | fn is_local(&self) -> bool { | |
485 | !self.is_global() | |
486 | } | |
487 | ||
488 | fn flags(&self) -> SymbolFlags<SectionIndex> { | |
489 | if self.symbol.has_aux_section() { | |
490 | if let Ok(aux) = self.file.symbols.aux_section(self.index.0) { | |
491 | // TODO: use high_number for bigobj | |
492 | let number = aux.number.get(LE) as usize; | |
493 | return SymbolFlags::CoffSection { | |
494 | selection: aux.selection, | |
495 | associative_section: if number == 0 { | |
496 | None | |
497 | } else { | |
498 | Some(SectionIndex(number)) | |
499 | }, | |
500 | }; | |
501 | } | |
502 | } | |
503 | SymbolFlags::None | |
504 | } | |
505 | } |