use crate::{Result, error::Error};
#[derive(Debug, Clone, serde::Serialize)]
pub struct PdbStream<'a> {
pub pdb_id: [u8; 20],
pub entry_point: u32,
pub referenced_type_system_tables: u64,
pub type_system_table_rows: Vec<u32>,
#[serde(skip_serializing)]
data: &'a [u8],
}
impl<'a> PdbStream<'a> {
pub fn parse(data: &'a [u8]) -> Result<Self> {
const FIXED_HEADER_LEN: usize = 20 + 4 + 8;
if data.len() < FIXED_HEADER_LEN {
return Err(Error::NotEnoughData(data.len(), FIXED_HEADER_LEN));
}
let mut pdb_id = [0u8; 20];
pdb_id.copy_from_slice(&data[0..20]);
let entry_point = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
let referenced_type_system_tables = u64::from_le_bytes([
data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31],
]);
let n_tables = referenced_type_system_tables.count_ones() as usize;
let rows_end = FIXED_HEADER_LEN
.checked_add(
n_tables
.checked_mul(4)
.ok_or(Error::NotEnoughData(data.len(), FIXED_HEADER_LEN))?,
)
.ok_or(Error::NotEnoughData(data.len(), FIXED_HEADER_LEN))?;
if rows_end > data.len() {
return Err(Error::NotEnoughData(data.len(), rows_end));
}
let mut rows = Vec::with_capacity(n_tables);
for i in 0..n_tables {
let off = FIXED_HEADER_LEN + i * 4;
rows.push(u32::from_le_bytes([
data[off],
data[off + 1],
data[off + 2],
data[off + 3],
]));
}
Ok(Self {
pdb_id,
entry_point,
referenced_type_system_tables,
type_system_table_rows: rows,
data,
})
}
#[must_use]
pub fn guid(&self) -> uuid::Uuid {
let mut g = [0u8; 16];
g.copy_from_slice(&self.pdb_id[0..16]);
uuid::Uuid::from_bytes_le(g)
}
#[must_use]
pub fn stamp(&self) -> u32 {
u32::from_le_bytes([
self.pdb_id[16],
self.pdb_id[17],
self.pdb_id[18],
self.pdb_id[19],
])
}
#[must_use]
pub fn raw(&self) -> &'a [u8] {
self.data
}
}
impl<'a> crate::DnPe<'a> {
pub fn new_pdb_stream(
&self,
_metadata_rva: &u32,
_stream_offset: &u32,
_stream_size: &usize,
_stream_name: &str,
stream_data: &'a [u8],
) -> Result<super::Stream<'a>> {
Ok(super::Stream::PdbStream(PdbStream::parse(stream_data)?))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_minimal_header() {
let mut data = vec![0u8; 32 + 8]; for b in &mut data[0..20] {
*b = 0xab;
}
data[20..24].copy_from_slice(&0x06000001u32.to_le_bytes());
let bitmap: u64 = (1 << 2) | (1 << 5);
data[24..32].copy_from_slice(&bitmap.to_le_bytes());
data[32..36].copy_from_slice(&42u32.to_le_bytes());
data[36..40].copy_from_slice(&7u32.to_le_bytes());
let s = PdbStream::parse(&data).unwrap();
assert_eq!(s.pdb_id, [0xab; 20]);
assert_eq!(s.entry_point, 0x06000001);
assert_eq!(s.referenced_type_system_tables, bitmap);
assert_eq!(s.type_system_table_rows, vec![42, 7]);
assert_eq!(s.stamp(), u32::from_le_bytes([0xab; 4]));
}
#[test]
fn rejects_short_input() {
let too_short = vec![0u8; 31];
assert!(PdbStream::parse(&too_short).is_err());
}
#[test]
fn rejects_truncated_row_counts() {
let mut data = vec![0u8; 32 + 2]; let bitmap: u64 = 1; data[24..32].copy_from_slice(&bitmap.to_le_bytes());
assert!(PdbStream::parse(&data).is_err());
}
}