use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct XMetadata {
pub pdb_guid: Option<String>,
pub pdb_age: Option<u32>,
pub pdb_filename: Option<String>,
pub debug_entry_kinds: Vec<String>,
}
#[must_use]
pub fn parse_pe(pe_bytes: &[u8]) -> Option<XMetadata> {
let pe = match goblin::Object::parse(pe_bytes) {
Ok(goblin::Object::PE(p)) => p,
_ => return None,
};
let dbg = pe.debug_data.as_ref()?;
let mut out = XMetadata::default();
if dbg.codeview_pdb70_debug_info.is_some() {
out.debug_entry_kinds.push("codeview_pdb70".to_string());
}
if dbg.codeview_pdb20_debug_info.is_some() {
out.debug_entry_kinds.push("codeview_pdb20".to_string());
}
if dbg.vcfeature_info.is_some() {
out.debug_entry_kinds.push("vcfeature".to_string());
}
if dbg.ex_dll_characteristics_info.is_some() {
out.debug_entry_kinds
.push("ex_dll_characteristics".to_string());
}
if dbg.repro_info.is_some() {
out.debug_entry_kinds.push("repro".to_string());
}
if dbg.pogo_info.is_some() {
out.debug_entry_kinds.push("pogo".to_string());
}
if let Some(cv) = dbg.codeview_pdb70_debug_info.as_ref() {
out.pdb_guid = Some(hex_lower(&cv.signature));
out.pdb_age = Some(cv.age);
let raw = cv.filename;
let end = raw.iter().position(|b| *b == 0).unwrap_or(raw.len());
if end > 0
&& let Ok(s) = std::str::from_utf8(&raw[..end])
{
out.pdb_filename = Some(s.to_string());
}
} else if let Some(cv) = dbg.codeview_pdb20_debug_info.as_ref() {
out.pdb_age = Some(cv.age);
let raw = cv.filename;
let end = raw.iter().position(|b| *b == 0).unwrap_or(raw.len());
if end > 0
&& let Ok(s) = std::str::from_utf8(&raw[..end])
{
out.pdb_filename = Some(s.to_string());
}
out.pdb_guid = Some(format!("{:08x}", cv.signature));
}
if out == XMetadata::default() {
None
} else {
Some(out)
}
}
fn hex_lower(sig: &[u8; 16]) -> String {
let mut s = String::with_capacity(32);
for b in sig.iter() {
s.push_str(&format!("{b:02x}"));
}
s
}