Source code

Revision control

Copy as Markdown

Other Tools

use crate::error;
use scroll::{
ctx::{self},
Pread, Pwrite, SizeWith,
};
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct DataDirectory {
pub virtual_address: u32,
pub size: u32,
}
pub const SIZEOF_DATA_DIRECTORY: usize = 8;
const NUM_DATA_DIRECTORIES: usize = 16;
impl DataDirectory {
pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
Ok(bytes.gread_with(offset, scroll::LE)?)
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DataDirectoryType {
ExportTable,
ImportTable,
ResourceTable,
ExceptionTable,
CertificateTable,
BaseRelocationTable,
DebugTable,
Architecture,
GlobalPtr,
TlsTable,
LoadConfigTable,
BoundImportTable,
ImportAddressTable,
DelayImportDescriptor,
ClrRuntimeHeader,
}
impl TryFrom<usize> for DataDirectoryType {
type Error = error::Error;
fn try_from(value: usize) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::ExportTable,
1 => Self::ImportTable,
2 => Self::ResourceTable,
3 => Self::ExceptionTable,
4 => Self::CertificateTable,
5 => Self::BaseRelocationTable,
6 => Self::DebugTable,
7 => Self::Architecture,
8 => Self::GlobalPtr,
9 => Self::TlsTable,
10 => Self::LoadConfigTable,
11 => Self::BoundImportTable,
12 => Self::ImportAddressTable,
13 => Self::DelayImportDescriptor,
14 => Self::ClrRuntimeHeader,
_ => {
return Err(error::Error::Malformed(
"Wrong data directory index number".into(),
))
}
})
}
}
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct DataDirectories {
pub data_directories: [Option<(usize, DataDirectory)>; NUM_DATA_DIRECTORIES],
}
impl ctx::TryIntoCtx<scroll::Endian> for DataDirectories {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
for opt_dd in self.data_directories {
if let Some((dd_offset, dd)) = opt_dd {
bytes.pwrite_with(dd, dd_offset, ctx)?;
*offset += dd_offset;
} else {
bytes.gwrite(&[0; SIZEOF_DATA_DIRECTORY][..], offset)?;
}
}
Ok(NUM_DATA_DIRECTORIES * SIZEOF_DATA_DIRECTORY)
}
}
macro_rules! build_dd_getter {
($dd_name:tt, $index:tt) => {
pub fn $dd_name(&self) -> Option<&DataDirectory> {
let idx = $index;
self.data_directories[idx].as_ref().map(|(_, dd)| dd)
}
};
}
impl DataDirectories {
pub fn parse(bytes: &[u8], count: usize, offset: &mut usize) -> error::Result<Self> {
let mut data_directories = [None; NUM_DATA_DIRECTORIES];
if count > NUM_DATA_DIRECTORIES {
return Err(error::Error::Malformed(format!(
"data directory count ({}) is greater than maximum number of data directories ({})",
count, NUM_DATA_DIRECTORIES
)));
}
for dir in data_directories.iter_mut().take(count) {
let dd = DataDirectory::parse(bytes, offset)?;
let dd = if dd.virtual_address == 0 && dd.size == 0 {
None
} else {
Some((*offset, dd))
};
*dir = dd;
}
Ok(DataDirectories { data_directories })
}
build_dd_getter!(get_export_table, 0);
build_dd_getter!(get_import_table, 1);
build_dd_getter!(get_resource_table, 2);
build_dd_getter!(get_exception_table, 3);
build_dd_getter!(get_certificate_table, 4);
build_dd_getter!(get_base_relocation_table, 5);
build_dd_getter!(get_debug_table, 6);
build_dd_getter!(get_architecture, 7);
build_dd_getter!(get_global_ptr, 8);
build_dd_getter!(get_tls_table, 9);
build_dd_getter!(get_load_config_table, 10);
build_dd_getter!(get_bound_import_table, 11);
build_dd_getter!(get_import_address_table, 12);
build_dd_getter!(get_delay_import_descriptor, 13);
build_dd_getter!(get_clr_runtime_header, 14);
pub fn dirs(&self) -> impl Iterator<Item = (DataDirectoryType, DataDirectory)> {
self.data_directories
.into_iter()
.enumerate()
// (Index, Option<DD>) -> Option<(Index, DD)> -> (DDT, DD)
.filter_map(|(i, o)|
// We should not have invalid indexes.
// Indeed: `data_directories: &[_; N]` where N is the number
// of data directories.
// The `TryFrom` trait for integers to DataDirectoryType
// takes into account the N possible data directories.
// Therefore, the unwrap can never fail as long as Rust guarantees
// on types are honored.
o.map(|(_, v)| (i.try_into().unwrap(), v)))
}
}