Skip to content

Commit

Permalink
rework api a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
0vercl0k committed Jun 13, 2024
1 parent cf0fc2b commit 4ad6548
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 74 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/symbolizer-rs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ jobs:
run: cargo test --release --workspace

- name: cargo check
run: cargo check --workspace
run: cargo check --workspace --examples --tests

- name: cargo build
run: cargo build --release --workspace
run: cargo build --release --workspace --examples --tests

- name: Upload artifacts
uses: actions/upload-artifact@v4
Expand Down
16 changes: 8 additions & 8 deletions crates/symbolizer/examples/symbolize-dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ fn user(dmp: UserDumpParser, addr: u64) -> Result<()> {

let mut wrapper = UserDumpAddrSpace(dmp);
let mut symb = Builder::default()
.modules(&modules)
.modules(modules)
.msft_symsrv()
.symcache(&sympath().expect("define a _NT_SYMBOL_PATH"))
.build(&mut wrapper)?;
.symcache(sympath().expect("define a _NT_SYMBOL_PATH"))
.build()?;

let mut s = Vec::new();
symb.full(addr, &mut s)?;
symb.full(&mut wrapper, addr, &mut s)?;
println!("{addr:#x}: {}", String::from_utf8(s)?);

Ok(())
Expand Down Expand Up @@ -129,13 +129,13 @@ fn kernel(dmp: KernelDumpParser, addr: u64) -> Result<()> {

let mut wrapper = KernelDumpAdrSpace(&dmp);
let mut symb = Builder::default()
.modules(&modules)
.modules(modules)
.msft_symsrv()
.symcache(&sympath().expect("define a _NT_SYMBOL_PATH"))
.build(&mut wrapper)?;
.symcache(sympath().expect("define a _NT_SYMBOL_PATH"))
.build()?;

let mut s = Vec::new();
symb.full(addr, &mut s)?;
symb.full(&mut wrapper, addr, &mut s)?;
println!("{addr:#x}: {}", String::from_utf8(s)?);

Ok(())
Expand Down
37 changes: 16 additions & 21 deletions crates/symbolizer/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Axel '0vercl0k' Souchet - June 7 2024
use std::path::{Path, PathBuf};

use anyhow::anyhow;

use crate::symbolizer::{Config, PdbLookupMode};
use crate::{AddrSpace, Module, Result, Symbolizer};
use crate::{Module, Result, Symbolizer};

#[derive(Default)]
pub struct NoSymcache;
Expand All @@ -19,20 +21,10 @@ pub struct Builder<SC> {

impl<SC> Builder<SC> {
pub fn msft_symsrv(self) -> Builder<SC> {
let Self {
symcache, modules, ..
} = self;

Builder {
symcache,
modules,
mode: PdbLookupMode::Online {
symsrvs: vec!["https://msdl.microsoft.com/download/symbols/".into()],
},
}
self.online(vec!["https://msdl.microsoft.com/download/symbols/"])
}

pub fn online(self, symsrvs: impl Iterator<Item = impl Into<String>>) -> Builder<SC> {
pub fn online(self, symsrvs: impl IntoIterator<Item = impl Into<String>>) -> Builder<SC> {
let Self {
symcache, modules, ..
} = self;
Expand All @@ -41,14 +33,14 @@ impl<SC> Builder<SC> {
symcache,
modules,
mode: PdbLookupMode::Online {
symsrvs: symsrvs.map(Into::into).collect(),
symsrvs: symsrvs.into_iter().map(Into::into).collect(),
},
}
}
}

impl Builder<NoSymcache> {
pub fn symcache(self, cache: &impl AsRef<Path>) -> Builder<Symcache> {
pub fn symcache(self, cache: impl AsRef<Path>) -> Builder<Symcache> {
let Self { modules, mode, .. } = self;

Builder {
Expand All @@ -60,29 +52,32 @@ impl Builder<NoSymcache> {
}

impl<SC> Builder<SC> {
pub fn modules<'a>(mut self, modules: impl IntoIterator<Item = &'a Module>) -> Self {
self.modules = modules.into_iter().cloned().collect();
pub fn modules(mut self, modules: impl IntoIterator<Item = Module>) -> Self {
self.modules = modules.into_iter().collect();

self
}
}

impl Builder<Symcache> {
pub fn build<AS>(self, addr_space: &mut AS) -> Result<Symbolizer<AS>>
where
AS: AddrSpace,
pub fn build(self) -> Result<Symbolizer>
{
let Self {
symcache,
modules,
mode,
} = self;

if !symcache.0.exists() {
return Err(anyhow!("symcache {:?} does not exist", symcache.0).into());
}

let config = Config {
symcache: symcache.0,
modules,
mode,
};

Symbolizer::new(addr_space, config)
Symbolizer::new(config)
}
}
27 changes: 9 additions & 18 deletions crates/symbolizer/src/symbolizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,7 @@ pub struct Config {
/// The [`Symbolizer`] is the main object that glues all the logic.
///
/// It downloads, parses PDB information, and symbolizes.
pub struct Symbolizer<'a, AS>
where
AS: AddrSpace,
pub struct Symbolizer
{
/// Keep track of some statistics such as the number of lines symbolized,
/// PDB downloaded, etc.
Expand All @@ -239,10 +237,6 @@ where
/// This is the list of kernel / user modules read from the kernel crash
/// dump.
modules: Modules,
/// The kernel dump parser. We need this to be able to read PDB identifiers
/// out of the PE headers, as well as reading the export tables of those
/// modules.
addr_space: RefCell<&'a mut AS>,
/// List of symbol servers to try to download PDBs from when needed.
symsrvs: Vec<String>,
/// Caches addresses to symbols. This allows us to not have to symbolize an
Expand All @@ -254,16 +248,14 @@ where
offline: bool,
}

impl<'a, AS> Symbolizer<'a, AS>
where
AS: AddrSpace,
impl Symbolizer
{
pub fn builder() -> Builder<NoSymcache> {
Builder::default()
}

/// Create a [`Symbolizer`].
pub fn new(addr_space: &'a mut AS, config: Config) -> Result<Self> {
pub fn new(config: Config) -> Result<Self> {
let (offline, symsrvs) = match config.mode {
PdbLookupMode::Offline =>
// If the user wants offline, then let's do that..
Expand Down Expand Up @@ -291,7 +283,6 @@ where
stats: Default::default(),
symcache: config.symcache,
modules: Modules::new(config.modules),
addr_space: RefCell::new(addr_space),
symsrvs,
addr_cache: Default::default(),
pdb_caches: Default::default(),
Expand Down Expand Up @@ -322,7 +313,7 @@ where
/// or remotely) and extract every bit of relevant information for us.
/// Finally, the result will be kept around to symbolize addresses in that
/// module faster in the future.
fn try_symbolize_addr_from_pdbs(&self, addr: u64) -> Result<Option<Rc<String>>> {
fn try_symbolize_addr_from_pdbs(&self, addr_space: &mut impl AddrSpace, addr: u64) -> Result<Option<Rc<String>>> {
trace!("symbolizing address {addr:#x}..");
let Some(module) = self.modules.find(addr) else {
trace!("address {addr:#x} doesn't belong to any module");
Expand All @@ -341,7 +332,7 @@ where

// Let's start by parsing the PE to get its exports, and PDB information if
// there's any.
let pe = Pe::new(*self.addr_space.borrow_mut(), module.at.start)?;
let pe = Pe::new(addr_space, module.at.start)?;

// Ingest the EAT.
builder.ingest(pe.exports.into_iter());
Expand Down Expand Up @@ -388,14 +379,14 @@ where
/// If the address has been symbolized before, it will be in the
/// `addr_cache` already. If not, we need to take the slow path and ask the
/// right [`PdbCache`] which might require to create one in the first place.
fn try_symbolize_addr(&self, addr: u64) -> Result<Option<Rc<String>>> {
fn try_symbolize_addr(&self, addr_space: &mut impl AddrSpace, addr: u64) -> Result<Option<Rc<String>>> {
match self.addr_cache.borrow_mut().entry(addr) {
hash_map::Entry::Occupied(o) => {
self.stats.cache_hit();
return Ok(Some(o.get().clone()));
}
hash_map::Entry::Vacant(v) => {
let Some(symbol) = self.try_symbolize_addr_from_pdbs(addr)? else {
let Some(symbol) = self.try_symbolize_addr_from_pdbs(addr_space, addr)? else {
return Ok(None);
};

Expand Down Expand Up @@ -432,8 +423,8 @@ where

/// Symbolize `addr` in the `module!function+offset` style and write the
/// result into `output`.
pub fn full(&mut self, addr: u64, output: &mut impl Write) -> Result<()> {
match self.try_symbolize_addr(addr)? {
pub fn full(&mut self, addr_space: &mut impl AddrSpace, addr: u64, output: &mut impl Write) -> Result<()> {
match self.try_symbolize_addr(addr_space, addr)? {
Some(sym) => {
output
.write_all(sym.as_bytes())
Expand Down
File renamed without changes.
File renamed without changes.
34 changes: 17 additions & 17 deletions crates/symbolizer/tests/basics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const EXPECTED_RAW: [(u64, &str, &str); 4] = [
(0xdeadbeef, "0x00000000deadbeef", "0x00000000deadbeef"),
];

fn fixture(name: &str) -> PathBuf {
fn testdata(name: &str) -> PathBuf {
PathBuf::from(&env!("CARGO_MANIFEST_DIR"))
.join("fixtures")
.join("testdatas")
.join(name)
}

Expand Down Expand Up @@ -74,19 +74,19 @@ impl AddrSpace for RawAddressSpace {

#[test]
fn raw_virt() {
let mut raw_addr_space = RawAddressSpace::new(&fixture("mrt100.raw")).unwrap();
let mut raw_addr_space = RawAddressSpace::new(&testdata("mrt100.raw")).unwrap();
let len = raw_addr_space.len();

let mut symb = Builder::default()
.modules(&vec![Module::new("mrt100", 0x0, len)])
.modules(vec![Module::new("mrt100", 0x0, len)])
.msft_symsrv()
.symcache(&symcache("basics"))
.build(&mut raw_addr_space)
.build()
.unwrap();

for (addr, expected_full, expected_modoff) in EXPECTED_RAW {
let mut full = Vec::new();
symb.full(addr, &mut full).unwrap();
symb.full(&mut raw_addr_space, addr, &mut full).unwrap();
assert_eq!(String::from_utf8(full).unwrap(), expected_full);

let mut modoff = Vec::new();
Expand Down Expand Up @@ -164,21 +164,21 @@ impl<'data> AddrSpace for FileAddressSpace<'data> {

#[test]
fn raw_file() {
let file = File::open(fixture("mrt100.dll")).unwrap();
let file = File::open(testdata("mrt100.dll")).unwrap();
let cache = ReadCache::new(file);
let mut file_addr_space = FileAddressSpace::new(&cache).unwrap();
let len = file_addr_space.len();

let mut symb = Builder::default()
.modules(&vec![Module::new("mrt100", 0x0, len)])
.modules(vec![Module::new("mrt100", 0x0, len)])
.online(vec!["https://msdl.microsoft.com/download/symbols/"].into_iter())
.symcache(&symcache("basics"))
.build(&mut file_addr_space)
.build()
.unwrap();

for (addr, expected_full, expected_modoff) in EXPECTED_RAW {
let mut full = Vec::new();
symb.full(addr, &mut full).unwrap();
symb.full(&mut file_addr_space, addr, &mut full).unwrap();
assert_eq!(String::from_utf8(full).unwrap(), expected_full);

let mut modoff = Vec::new();
Expand Down Expand Up @@ -237,7 +237,7 @@ impl<'a> AddrSpace for UserDumpAddrSpace<'a> {

#[test]
fn user_dump() {
let dump = UserDumpParser::new(fixture("udmp.dmp")).unwrap();
let dump = UserDumpParser::new(testdata("udmp.dmp")).unwrap();
let modules = dump
.modules()
.values()
Expand All @@ -252,17 +252,17 @@ fn user_dump() {

let mut udmp_addr_space = UserDumpAddrSpace(dump);
let mut symb = Builder::default()
.modules(&modules)
.modules(modules.clone())
.msft_symsrv()
.symcache(&symcache("basics"))
.build(&mut udmp_addr_space)
.build()
.unwrap();

// 0:000> u 00007ff9`aa4f8eb2
// ntdll!EvtIntReportEventWorker$fin$0+0x2:
// 00007ff9`aa4f8eb2 4883ec50 sub rsp,50h
let mut output = Vec::new();
symb.full(0x7ff9aa4f8eb2, &mut output).unwrap();
symb.full(&mut udmp_addr_space, 0x7ff9aa4f8eb2, &mut output).unwrap();
assert_eq!(
String::from_utf8(output).unwrap(),
"ntdll.dll!EvtIntReportEventWorker$fin$0+0x2"
Expand All @@ -282,15 +282,15 @@ fn user_dump() {
drop(symb);
let mut symb_offline = Builder::default()
.symcache(&symcache("basics"))
.modules(&modules)
.build(&mut udmp_addr_space)
.modules(modules)
.build()
.unwrap();

// 0:000> u 00007ff9`aa4f8eb2
// ntdll!EvtIntReportEventWorker$fin$0+0x2:
// 00007ff9`aa4f8eb2 4883ec50 sub rsp,50h
let mut output = Vec::new();
symb_offline.full(0x7ff9aa4f8eb2, &mut output).unwrap();
symb_offline.full(&mut udmp_addr_space, 0x7ff9aa4f8eb2, &mut output).unwrap();
assert_ne!(
String::from_utf8(output).unwrap(),
"ntdll.dll!EvtIntReportEventWorker$fin$0+0x2"
Expand Down
Loading

0 comments on commit 4ad6548

Please sign in to comment.