use std::collections::HashMap;
#[must_use]
pub fn parse_pe(pe_bytes: &[u8], image_base: u64) -> HashMap<u64, String> {
let mut out = HashMap::new();
let pe = match goblin::Object::parse(pe_bytes) {
Ok(goblin::Object::PE(p)) => p,
_ => return out,
};
let section = |name: &str| -> &[u8] {
for s in &pe.sections {
let sec_name = std::str::from_utf8(&s.name).unwrap_or("");
if sec_name.trim_end_matches('\0').starts_with(name) {
let start = s.pointer_to_raw_data as usize;
let len = s.size_of_raw_data as usize;
if let Some(slice) = pe_bytes.get(start..start.saturating_add(len)) {
return slice;
}
}
}
&[]
};
let debug_info = section(".debug_info");
let debug_abbrev = section(".debug_abbrev");
let debug_str = section(".debug_str");
if debug_info.is_empty() || debug_abbrev.is_empty() {
return out;
}
let endian = gimli::LittleEndian;
let dwarf = gimli::Dwarf {
debug_info: gimli::DebugInfo::new(debug_info, endian),
debug_abbrev: gimli::DebugAbbrev::new(debug_abbrev, endian),
debug_str: gimli::DebugStr::new(debug_str, endian),
..Default::default()
};
let mut units = dwarf.units();
while let Ok(Some(header)) = units.next() {
let unit = match dwarf.unit(header) {
Ok(u) => u,
Err(_) => continue,
};
let unit_ref = unit.unit_ref(&dwarf);
let mut entries = unit_ref.entries();
while let Ok(Some((_delta, entry))) = entries.next_dfs() {
if entry.tag() != gimli::DW_TAG_subprogram {
continue;
}
let low_pc = match entry.attr_value(gimli::DW_AT_low_pc) {
Ok(Some(gimli::AttributeValue::Addr(a))) => a,
_ => continue,
};
if low_pc == 0 {
continue;
}
let name_attr = entry
.attr_value(gimli::DW_AT_linkage_name)
.ok()
.flatten()
.or_else(|| {
entry
.attr_value(gimli::DW_AT_MIPS_linkage_name)
.ok()
.flatten()
})
.or_else(|| entry.attr_value(gimli::DW_AT_name).ok().flatten());
let Some(av) = name_attr else { continue };
let Ok(slice) = unit_ref.attr_string(av) else {
continue;
};
let raw = match std::str::from_utf8(slice.slice()) {
Ok(s) => s,
Err(_) => continue,
};
if raw.is_empty() {
continue;
}
let demangled = crate::demangle::maybe_demangle(raw);
let va = if low_pc >= image_base {
low_pc
} else {
image_base.saturating_add(low_pc)
};
out.insert(va, demangled);
}
}
out
}