#![allow(clippy::unused_unit)]
use crate::{error, strtab};
use alloc::vec::Vec;
use core::fmt::{self, Debug};
use scroll::{ctx, IOread, IOwrite, Pread, Pwrite, SizeWith};
pub const COFF_SYMBOL_SIZE: usize = 18;
pub const IMAGE_SYM_UNDEFINED: i16 = 0;
pub const IMAGE_SYM_ABSOLUTE: i16 = -1;
pub const IMAGE_SYM_DEBUG: i16 = -2;
pub const IMAGE_SYM_TYPE_NULL: u16 = 0;
pub const IMAGE_SYM_TYPE_VOID: u16 = 1;
pub const IMAGE_SYM_TYPE_CHAR: u16 = 2;
pub const IMAGE_SYM_TYPE_SHORT: u16 = 3;
pub const IMAGE_SYM_TYPE_INT: u16 = 4;
pub const IMAGE_SYM_TYPE_LONG: u16 = 5;
pub const IMAGE_SYM_TYPE_FLOAT: u16 = 6;
pub const IMAGE_SYM_TYPE_DOUBLE: u16 = 7;
pub const IMAGE_SYM_TYPE_STRUCT: u16 = 8;
pub const IMAGE_SYM_TYPE_UNION: u16 = 9;
pub const IMAGE_SYM_TYPE_ENUM: u16 = 10;
pub const IMAGE_SYM_TYPE_MOE: u16 = 11;
pub const IMAGE_SYM_TYPE_BYTE: u16 = 12;
pub const IMAGE_SYM_TYPE_WORD: u16 = 13;
pub const IMAGE_SYM_TYPE_UINT: u16 = 14;
pub const IMAGE_SYM_TYPE_DWORD: u16 = 15;
pub const IMAGE_SYM_DTYPE_NULL: u16 = 0;
pub const IMAGE_SYM_DTYPE_POINTER: u16 = 1;
pub const IMAGE_SYM_DTYPE_FUNCTION: u16 = 2;
pub const IMAGE_SYM_DTYPE_ARRAY: u16 = 3;
pub const IMAGE_SYM_TYPE_MASK: u16 = 0xf;
pub const IMAGE_SYM_DTYPE_SHIFT: usize = 4;
pub const IMAGE_SYM_CLASS_END_OF_FUNCTION: u8 = 0xff;
pub const IMAGE_SYM_CLASS_NULL: u8 = 0;
pub const IMAGE_SYM_CLASS_AUTOMATIC: u8 = 1;
pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 2;
pub const IMAGE_SYM_CLASS_STATIC: u8 = 3;
pub const IMAGE_SYM_CLASS_REGISTER: u8 = 4;
pub const IMAGE_SYM_CLASS_EXTERNAL_DEF: u8 = 5;
pub const IMAGE_SYM_CLASS_LABEL: u8 = 6;
pub const IMAGE_SYM_CLASS_UNDEFINED_LABEL: u8 = 7;
pub const IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: u8 = 8;
pub const IMAGE_SYM_CLASS_ARGUMENT: u8 = 9;
pub const IMAGE_SYM_CLASS_STRUCT_TAG: u8 = 10;
pub const IMAGE_SYM_CLASS_MEMBER_OF_UNION: u8 = 11;
pub const IMAGE_SYM_CLASS_UNION_TAG: u8 = 12;
pub const IMAGE_SYM_CLASS_TYPE_DEFINITION: u8 = 13;
pub const IMAGE_SYM_CLASS_UNDEFINED_STATIC: u8 = 14;
pub const IMAGE_SYM_CLASS_ENUM_TAG: u8 = 15;
pub const IMAGE_SYM_CLASS_MEMBER_OF_ENUM: u8 = 16;
pub const IMAGE_SYM_CLASS_REGISTER_PARAM: u8 = 17;
pub const IMAGE_SYM_CLASS_BIT_FIELD: u8 = 18;
pub const IMAGE_SYM_CLASS_BLOCK: u8 = 100;
pub const IMAGE_SYM_CLASS_FUNCTION: u8 = 101;
pub const IMAGE_SYM_CLASS_END_OF_STRUCT: u8 = 102;
pub const IMAGE_SYM_CLASS_FILE: u8 = 103;
pub const IMAGE_SYM_CLASS_SECTION: u8 = 104;
pub const IMAGE_SYM_CLASS_WEAK_EXTERNAL: u8 = 105;
pub const IMAGE_SYM_CLASS_CLR_TOKEN: u8 = 107;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct Symbol {
pub name: [u8; 8],
pub value: u32,
pub section_number: i16,
pub typ: u16,
pub storage_class: u8,
pub number_of_aux_symbols: u8,
}
impl Symbol {
pub fn parse<'a>(bytes: &'a [u8], offset: usize) -> error::Result<(Option<&'a str>, Symbol)> {
let symbol = bytes.pread::<Symbol>(offset)?;
let name = if symbol.name[0] != 0 {
bytes
.pread_with(offset, ctx::StrCtx::DelimiterUntil(0, 8))
.ok()
} else {
None
};
Ok((name, symbol))
}
pub fn name<'a>(&'a self, strtab: &'a strtab::Strtab) -> error::Result<&'a str> {
if let Some(offset) = self.name_offset() {
strtab.get_at(offset as usize).ok_or_else(|| {
error::Error::Malformed(format!("Invalid Symbol name offset {:#x}", offset))
})
} else {
Ok(self.name.pread(0)?)
}
}
pub fn name_offset(&self) -> Option<u32> {
let length_field_size = core::mem::size_of::<u32>() as u32;
if self.name[0] == 0 {
self.name
.pread_with(4, scroll::LE)
.ok()
.map(|offset: u32| offset - length_field_size)
} else {
None
}
}
pub fn set_name_offset(&mut self, offset: u32) {
self.name[..4].copy_from_slice(&[0; 4]);
self.name.pwrite_with(offset, 4, scroll::LE).unwrap();
}
pub fn base_type(&self) -> u16 {
self.typ & IMAGE_SYM_TYPE_MASK
}
pub fn derived_type(&self) -> u16 {
self.typ >> IMAGE_SYM_DTYPE_SHIFT
}
pub fn is_function_definition(&self) -> bool {
self.storage_class == IMAGE_SYM_CLASS_EXTERNAL
&& self.derived_type() == IMAGE_SYM_DTYPE_FUNCTION
&& self.section_number > 0
}
pub fn is_weak_external(&self) -> bool {
self.storage_class == IMAGE_SYM_CLASS_WEAK_EXTERNAL
}
pub fn is_file(&self) -> bool {
self.storage_class == IMAGE_SYM_CLASS_FILE
}
pub fn is_section_definition(&self) -> bool {
self.storage_class == IMAGE_SYM_CLASS_STATIC && self.number_of_aux_symbols > 0
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct AuxFunctionDefinition {
pub tag_index: u32,
pub total_size: u32,
pub pointer_to_line_number: u32,
pub pointer_to_next_function: u32,
pub unused: [u8; 2],
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct AuxBeginAndEndFunction {
pub unused1: [u8; 4],
pub line_number: u16,
pub unused2: [u8; 6],
pub pointer_to_next_function: u32,
pub unused3: [u8; 2],
}
pub const IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY: u32 = 1;
pub const IMAGE_WEAK_EXTERN_SEARCH_LIBRARY: u32 = 2;
pub const IMAGE_WEAK_EXTERN_SEARCH_ALIAS: u32 = 3;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct AuxWeakExternal {
pub tag_index: u32,
pub characteristics: u32,
pub unused: [u8; 10],
}
pub const IMAGE_COMDAT_SELECT_NODUPLICATES: u8 = 1;
pub const IMAGE_COMDAT_SELECT_ANY: u8 = 2;
pub const IMAGE_COMDAT_SELECT_SAME_SIZE: u8 = 3;
pub const IMAGE_COMDAT_SELECT_EXACT_MATCH: u8 = 4;
pub const IMAGE_COMDAT_SELECT_ASSOCIATIVE: u8 = 5;
pub const IMAGE_COMDAT_SELECT_LARGEST: u8 = 6;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct AuxSectionDefinition {
pub length: u32,
pub number_of_relocations: u16,
pub number_of_line_numbers: u16,
pub checksum: u32,
pub number: u16,
pub selection: u8,
pub unused: [u8; 3],
}
pub struct SymbolTable<'a> {
symbols: &'a [u8],
}
impl<'a> SymbolTable<'a> {
pub fn parse(bytes: &'a [u8], offset: usize, number: usize) -> error::Result<SymbolTable<'a>> {
let symbols = bytes.pread_with(offset, Self::size(number))?;
Ok(SymbolTable { symbols })
}
pub fn size(number: usize) -> usize {
number * COFF_SYMBOL_SIZE
}
pub fn get(&self, index: usize) -> Option<(Option<&'a str>, Symbol)> {
let offset = index * COFF_SYMBOL_SIZE;
Symbol::parse(self.symbols, offset).ok()
}
pub fn aux_function_definition(&self, index: usize) -> Option<AuxFunctionDefinition> {
let offset = index * COFF_SYMBOL_SIZE;
self.symbols.pread(offset).ok()
}
pub fn aux_begin_and_end_function(&self, index: usize) -> Option<AuxBeginAndEndFunction> {
let offset = index * COFF_SYMBOL_SIZE;
self.symbols.pread(offset).ok()
}
pub fn aux_weak_external(&self, index: usize) -> Option<AuxWeakExternal> {
let offset = index * COFF_SYMBOL_SIZE;
self.symbols.pread(offset).ok()
}
pub fn aux_file(&self, index: usize, number: usize) -> Option<&'a str> {
let offset = index * COFF_SYMBOL_SIZE;
let length = number * COFF_SYMBOL_SIZE;
self.symbols
.pread_with(offset, ctx::StrCtx::DelimiterUntil(0, length))
.ok()
}
pub fn aux_section_definition(&self, index: usize) -> Option<AuxSectionDefinition> {
let offset = index * COFF_SYMBOL_SIZE;
self.symbols.pread(offset).ok()
}
pub fn iter(&self) -> SymbolIterator<'a> {
SymbolIterator {
index: 0,
symbols: self.symbols,
}
}
}
impl<'a> Debug for SymbolTable<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("SymbolTable")
.field("symbols", &self.iter().collect::<Vec<_>>())
.finish()
}
}
#[derive(Default)]
pub struct SymbolIterator<'a> {
index: usize,
symbols: &'a [u8],
}
impl<'a> Iterator for SymbolIterator<'a> {
type Item = (usize, Option<&'a str>, Symbol);
fn next(&mut self) -> Option<Self::Item> {
let offset = self.index * COFF_SYMBOL_SIZE;
if offset >= self.symbols.len() {
None
} else {
let index = self.index;
let (name, symbol) = Symbol::parse(self.symbols, offset).ok()?;
self.index += 1 + symbol.number_of_aux_symbols as usize;
Some((index, name, symbol))
}
}
}