use crate::{dir::*, error::*, impl_from_bytes, mem::*};
use alloc::string::ToString;
use core::mem;
pub enum ExportValue<'a> {
Rva(u32),
Forward(&'a str),
}
pub struct Export<'a> {
pub value: ExportValue<'a>,
pub ordinal: u16,
pub name: Option<&'a str>,
}
pub struct ExportTable<'a> {
data: ByteReader<'a>,
dir: &'a DataDirectory,
export_table: Option<&'a ExportDirectoryTable>,
index: (u16, usize),
}
impl<'a> ExportTable<'a> {
pub fn export_table(&mut self) -> Result<&'a ExportDirectoryTable> {
if self.export_table.is_none() {
let etable = self.data.read::<ExportDirectoryTable>()?.validate()?;
self.data.skip(SkipPos::Rel(etable.function_rva as _));
Ok(self.export_table.insert(etable))
} else {
Ok(self.export_table.unwrap())
}
}
pub fn time_date_stamp(&mut self) -> Result<u32> {
Ok(self.export_table()?.time_date_stamp)
}
pub fn func_count(&mut self) -> Result<u32> {
Ok(self.export_table()?.num_of_funcs)
}
pub fn name_count(&mut self) -> Result<u32> {
Ok(self.export_table()?.num_of_names)
}
}
impl<'a> DataDirectoryTable<'a> for ExportTable<'a> {
fn new(bytes: &'a [u8], dir: &'a DataDirectory) -> Self {
Self {
data: ByteReader::new_with_rel(bytes, dir.rva as usize),
dir,
export_table: None,
index: (0, 0),
}
}
fn typ() -> DataDirectoryType {
DataDirectoryType::ExportTable
}
}
impl<'a> Iterator for ExportTable<'a> {
type Item = Result<Export<'a>>;
fn next(&mut self) -> Option<Self::Item> {
let etable = match self.export_table() {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
if etable.num_of_funcs <= self.index.0 as u32 {
return None;
}
match (|| {
let rva = self.data.read::<u32>()?;
let ordinal = self.data.read_at::<u16>(
etable.ordinals_rva as usize + mem::size_of::<u16>() * self.index.1,
)?;
let name = if self.index.0 == *ordinal {
let name_rva = self.data.read_at::<u32>(
etable.names_rva as usize + mem::size_of::<u32>() * self.index.1,
)?;
Some(str_from_bytes(self.data.bytes_at(*name_rva as usize)?)?)
} else {
None
};
self.index.0 += 1;
if name.is_some() {
self.index.1 += 1;
}
let value = if self.dir.contains_rva(*rva) {
ExportValue::Forward(str_from_bytes(
self.data.bytes_at(*rva as usize)?,
)?)
} else {
ExportValue::Rva(*rva)
};
Ok(Export {
value,
ordinal: etable.ordinal_base as u16 + self.index.0 - 1,
name,
})
})() {
Ok(v) => Some(Ok(v)),
Err(e) => Some(Err(e)),
}
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct ExportDirectoryTable {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub name_rva: u32,
pub ordinal_base: u32,
pub num_of_funcs: u32,
pub num_of_names: u32,
pub function_rva: u32,
pub names_rva: u32,
pub ordinals_rva: u32,
}
impl ExportDirectoryTable {
pub fn validate(&self) -> Result<&Self> {
if self.characteristics != 0 {
return Error::make_malformed::<ExportDirectoryTable, _>(
"has non zero reserved field 'characteristics'".to_string(),
);
}
if self.num_of_funcs < self.num_of_names {
return Error::make_malformed::<ExportDirectoryTable, _>(
"has invalid number of functions or names".to_string(),
);
}
if (self.names_rva == 0 && self.ordinals_rva != 0)
|| (self.names_rva != 0 && !self.ordinals_rva == 0)
{
return Error::make_malformed::<ExportDirectoryTable, _>(
"has invalid rva to name or ordinal table".to_string(),
);
}
Ok(self)
}
}
impl_from_bytes!(ExportDirectoryTable);