use core::marker::PhantomData;
use crate::util::file_offset_to_vm_in;
const SIZEOF_DATA_IN_CODE_ENTRY: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DataInCodeKind {
Data,
JumpTable8,
JumpTable16,
JumpTable32,
AbsJumpTable32,
Other(u16),
}
impl DataInCodeKind {
#[must_use]
pub const fn from_raw(raw: u16) -> Self {
match raw {
1 => Self::Data,
2 => Self::JumpTable8,
3 => Self::JumpTable16,
4 => Self::JumpTable32,
5 => Self::AbsJumpTable32,
other => Self::Other(other),
}
}
#[must_use]
pub const fn entry_bytes(self) -> Option<u8> {
match self {
Self::JumpTable8 => Some(1),
Self::JumpTable16 => Some(2),
Self::JumpTable32 | Self::AbsJumpTable32 => Some(4),
Self::Data | Self::Other(_) => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DataInCode {
pub offset: u32,
pub address: Option<u64>,
pub length: u16,
pub kind: DataInCodeKind,
}
pub struct DataInCodeIter<'a, 'p> {
stream: &'a [u8],
cursor: usize,
little_endian: bool,
segments: Vec<(u64, u64, u64, u64)>,
_parent: PhantomData<&'p ()>,
}
impl<'a, 'p> DataInCodeIter<'a, 'p> {
pub(crate) fn new(
stream: &'a [u8],
little_endian: bool,
segments: Vec<(u64, u64, u64, u64)>,
) -> Self {
Self {
stream,
cursor: 0,
little_endian,
segments,
_parent: PhantomData,
}
}
pub(crate) fn empty() -> Self {
Self::new(&[], true, Vec::new())
}
}
impl<'a, 'p> Iterator for DataInCodeIter<'a, 'p> {
type Item = DataInCode;
fn next(&mut self) -> Option<DataInCode> {
let end = self.cursor.checked_add(SIZEOF_DATA_IN_CODE_ENTRY)?;
let record = self.stream.get(self.cursor..end)?;
self.cursor = end;
let (offset, length, kind) = match *record {
[o0, o1, o2, o3, l0, l1, k0, k1] if self.little_endian => (
u32::from_le_bytes([o0, o1, o2, o3]),
u16::from_le_bytes([l0, l1]),
u16::from_le_bytes([k0, k1]),
),
[o0, o1, o2, o3, l0, l1, k0, k1] => (
u32::from_be_bytes([o0, o1, o2, o3]),
u16::from_be_bytes([l0, l1]),
u16::from_be_bytes([k0, k1]),
),
_ => return None,
};
Some(DataInCode {
offset,
address: file_offset_to_vm_in(self.segments.iter().copied(), u64::from(offset)),
length,
kind: DataInCodeKind::from_raw(kind),
})
}
}
#[cfg(test)]
mod tests {
use super::{DataInCode, DataInCodeIter, DataInCodeKind};
#[test]
fn kinds_read_with_their_entry_width() {
let expected = [
(1, DataInCodeKind::Data, None),
(2, DataInCodeKind::JumpTable8, Some(1)),
(3, DataInCodeKind::JumpTable16, Some(2)),
(4, DataInCodeKind::JumpTable32, Some(4)),
(5, DataInCodeKind::AbsJumpTable32, Some(4)),
(0x77, DataInCodeKind::Other(0x77), None),
];
for (raw, kind, width) in expected {
assert_eq!(DataInCodeKind::from_raw(raw), kind);
assert_eq!(kind.entry_bytes(), width);
}
}
#[test]
fn entries_decode_and_resolve() {
let segments = vec![(0x1_0000_0000, 0x4000, 0, 0x4000)];
let mut little = Vec::new();
little.extend_from_slice(&0x3f10_u32.to_le_bytes());
little.extend_from_slice(&0x20_u16.to_le_bytes());
little.extend_from_slice(&4_u16.to_le_bytes());
little.extend_from_slice(&0x9000_u32.to_le_bytes());
little.extend_from_slice(&8_u16.to_le_bytes());
little.extend_from_slice(&1_u16.to_le_bytes());
little.extend_from_slice(&[0xAA; 5]);
let entries: Vec<DataInCode> =
DataInCodeIter::new(&little, true, segments.clone()).collect();
assert_eq!(
entries,
[
DataInCode {
offset: 0x3f10,
address: Some(0x1_0000_3f10),
length: 0x20,
kind: DataInCodeKind::JumpTable32,
},
DataInCode {
offset: 0x9000,
address: None,
length: 8,
kind: DataInCodeKind::Data,
},
]
);
let mut big = Vec::new();
big.extend_from_slice(&0x3f10_u32.to_be_bytes());
big.extend_from_slice(&0x20_u16.to_be_bytes());
big.extend_from_slice(&2_u16.to_be_bytes());
let entries: Vec<DataInCode> = DataInCodeIter::new(&big, false, segments).collect();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].address, Some(0x1_0000_3f10));
assert_eq!(entries[0].kind, DataInCodeKind::JumpTable8);
}
}