summaryrefslogtreecommitdiff
path: root/rust/examples/pdb-ng/src/symbol_parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/examples/pdb-ng/src/symbol_parser.rs')
-rw-r--r--rust/examples/pdb-ng/src/symbol_parser.rs2061
1 files changed, 0 insertions, 2061 deletions
diff --git a/rust/examples/pdb-ng/src/symbol_parser.rs b/rust/examples/pdb-ng/src/symbol_parser.rs
deleted file mode 100644
index 7a90c4ab..00000000
--- a/rust/examples/pdb-ng/src/symbol_parser.rs
+++ /dev/null
@@ -1,2061 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::{BTreeMap, HashMap, HashSet};
-use std::mem;
-use std::sync::OnceLock;
-
-use anyhow::{anyhow, Result};
-use itertools::Itertools;
-use pdb::register::Register::{AMD64, X86};
-use pdb::register::{AMD64Register, X86Register};
-use pdb::Error::UnimplementedSymbolKind;
-use pdb::{
- AnnotationReferenceSymbol, BasePointerRelativeSymbol, BlockSymbol, BuildInfoSymbol,
- CallSiteInfoSymbol, CompileFlagsSymbol, ConstantSymbol, DataReferenceSymbol, DataSymbol,
- DefRangeFramePointerRelativeFullScopeSymbol, DefRangeFramePointerRelativeSymbol,
- DefRangeRegisterRelativeSymbol, DefRangeRegisterSymbol, DefRangeSubFieldRegisterSymbol,
- DefRangeSubFieldSymbol, DefRangeSymbol, ExportSymbol, FallibleIterator, FrameProcedureSymbol,
- InlineSiteSymbol, LabelSymbol, LocalSymbol, MultiRegisterVariableSymbol, ObjNameSymbol,
- ProcedureReferenceSymbol, ProcedureSymbol, PublicSymbol, RegisterRelativeSymbol,
- RegisterVariableSymbol, Rva, SeparatedCodeSymbol, Source, Symbol, SymbolData, SymbolIndex,
- SymbolIter, ThreadStorageSymbol, ThunkSymbol, TrampolineSymbol, TypeIndex,
- UserDefinedTypeSymbol, UsingNamespaceSymbol,
-};
-
-use binaryninja::architecture::{Architecture, ArchitectureExt, Register};
-use binaryninja::binaryview::BinaryViewBase;
-use binaryninja::demangle::demangle_ms;
-use binaryninja::rc::Ref;
-use binaryninja::types::{
- max_confidence, min_confidence, Conf, ConfMergable, FunctionParameter, QualifiedName,
- StructureBuilder, Type, TypeClass, Variable, VariableSourceType,
-};
-
-use crate::PDBParserInstance;
-
-const DEMANGLE_CONFIDENCE: u8 = 32;
-
-/// Parsed Data Symbol like globals, etc
-#[derive(Debug, Clone)]
-pub struct SymbolNames {
- pub raw_name: String,
- pub short_name: Option<String>,
- pub full_name: Option<String>,
-}
-
-/// Parsed Data Symbol like globals, etc
-#[derive(Debug, Clone)]
-pub struct ParsedDataSymbol {
- /// If the symbol comes from the public symbol list (lower quality)
- pub is_public: bool,
- /// Absolute address in bv
- pub address: u64,
- /// Symbol name
- pub name: SymbolNames,
- /// Type if known
- pub type_: Option<Conf<Ref<Type>>>,
-}
-
-/// Parsed functions and function-y symbols
-#[derive(Debug, Clone)]
-pub struct ParsedProcedure {
- /// If the symbol comes from the public symbol list (lower quality)
- pub is_public: bool,
- /// Absolute address in bv
- pub address: u64,
- /// Symbol name
- pub name: SymbolNames,
- /// Function type if known
- pub type_: Option<Conf<Ref<Type>>>,
- /// List of local variables (TODO: use these)
- pub locals: Vec<ParsedVariable>,
-}
-
-/// Structure with some information about a procedure
-#[derive(Debug, Clone)]
-pub struct ParsedProcedureInfo {
- /// Known parameters for the procedure
- pub params: Vec<ParsedVariable>,
- /// Known local variables for the procedure
- pub locals: Vec<ParsedVariable>,
-}
-
-/// One parsed variable / parameter
-#[derive(Debug, Clone)]
-pub struct ParsedVariable {
- /// Variable name
- pub name: String,
- /// Variable type if known
- pub type_: Option<Conf<Ref<Type>>>,
- /// Location(s) where the variable is stored. PDB lets you store a variable in multiple locations
- /// despite binja not really understanding that. Length is probably never zero
- pub storage: Vec<ParsedLocation>,
- /// Do we think this is a parameter
- pub is_param: bool,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct ParsedLocation {
- /// Location information
- pub location: Variable,
- /// Is the storage location relative to the base pointer? See [ParsedProcedureInfo.frame_offset]
- pub base_relative: bool,
- /// Is the storage location relative to the stack pointer?
- pub stack_relative: bool,
-}
-
-/// Big enum of all the types of symbols we know how to parse
-#[derive(Debug, Clone)]
-pub enum ParsedSymbol {
- /// Parsed Data Symbol like globals, etc
- Data(ParsedDataSymbol),
- /// Parsed functions and function-y symbols
- Procedure(ParsedProcedure),
- /// Structure with some information about a procedure
- ProcedureInfo(ParsedProcedureInfo),
- /// One parsed variable / parameter
- LocalVariable(ParsedVariable),
- /// Location of a local variable
- Location(ParsedLocation),
-}
-
-/// This is all done in the parser instance namespace because the lifetimes are impossible to
-/// wrangle otherwise.
-impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
- pub fn parse_symbols(
- &mut self,
- progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>,
- ) -> Result<(Vec<ParsedSymbol>, Vec<ParsedSymbol>)> {
- let mut module_count = 0usize;
- let dbg = self.pdb.debug_information()?;
- let mut modules = dbg.modules()?;
- while let Some(_module) = modules.next()? {
- module_count += 1;
- }
-
- let global_symbols = self.pdb.global_symbols()?;
- let symbols = global_symbols.iter();
- let parsed = self.parse_mod_symbols(symbols)?;
- for sym in parsed {
- match &sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: SymbolNames { raw_name, .. },
- ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: SymbolNames { raw_name, .. },
- ..
- }) => {
- self.parsed_symbols_by_name
- .insert(raw_name.clone(), self.parsed_symbols.len());
- }
- _ => {}
- }
- self.parsed_symbols.push(sym);
- }
-
- (progress)(1, module_count + 1)?;
-
- let dbg = self.pdb.debug_information()?;
- let mut modules = dbg.modules()?;
- let mut i = 0;
- while let Some(module) = modules.next()? {
- i += 1;
- (progress)(i + 1, module_count + 1)?;
-
- self.log(|| {
- format!(
- "Module {} {}",
- module.module_name(),
- module.object_file_name()
- )
- });
- if let Some(module_info) = self.pdb.module_info(&module)? {
- let symbols = module_info.symbols()?;
- let parsed = self.parse_mod_symbols(symbols)?;
- for sym in parsed {
- match &sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: SymbolNames { raw_name, .. },
- ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: SymbolNames { raw_name, .. },
- ..
- }) => {
- self.parsed_symbols_by_name
- .insert(raw_name.clone(), self.parsed_symbols.len());
- }
- _ => {}
- }
- self.parsed_symbols.push(sym);
- }
- }
- }
-
- let use_public =
- self.settings
- .get_bool("pdb.features.loadGlobalSymbols", Some(self.bv), None);
-
- let mut best_symbols = HashMap::<String, &ParsedSymbol>::new();
- for sym in &self.parsed_symbols {
- match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- is_public,
- address,
- name:
- SymbolNames {
- raw_name,
- full_name,
- ..
- },
- type_,
- ..
- }) => {
- if *is_public && !use_public {
- continue;
- }
-
- let this_confidence = match type_ {
- Some(Conf { confidence, .. }) => *confidence,
- _ => min_confidence(),
- };
- let (new_better, old_exists) = match best_symbols.get(raw_name) {
- Some(ParsedSymbol::Data(ParsedDataSymbol {
- type_:
- Some(Conf {
- confidence: old_conf,
- ..
- }),
- ..
- })) => (this_confidence > *old_conf, true),
- Some(ParsedSymbol::Data(ParsedDataSymbol { type_: None, .. })) => {
- (true, true)
- }
- Some(..) => (false, true),
- _ => (true, false),
- };
- if new_better {
- self.log(|| {
- format!(
- "New best symbol (at 0x{:x}) for `{}` / `{}`: {:?}",
- *address,
- raw_name,
- full_name.as_ref().unwrap_or(raw_name),
- sym
- )
- });
- if old_exists {
- self.log(|| format!("Clobbering old definition"));
- }
- best_symbols.insert(raw_name.clone(), sym);
- }
- }
- _ => {}
- }
- }
-
- let mut best_functions = HashMap::<String, &ParsedSymbol>::new();
- for sym in &self.parsed_symbols {
- match sym {
- ParsedSymbol::Procedure(ParsedProcedure {
- is_public,
- address,
- name:
- SymbolNames {
- raw_name,
- full_name,
- ..
- },
- type_,
- ..
- }) => {
- if *is_public && !use_public {
- continue;
- }
-
- let this_confidence = match type_ {
- Some(Conf { confidence, .. }) => *confidence,
- _ => min_confidence(),
- };
- let (new_better, old_exists) = match best_functions.get(raw_name) {
- Some(ParsedSymbol::Procedure(ParsedProcedure {
- type_:
- Some(Conf {
- confidence: old_conf,
- ..
- }),
- ..
- })) => (this_confidence > *old_conf, true),
- Some(ParsedSymbol::Procedure(ParsedProcedure { type_: None, .. })) => {
- (true, true)
- }
- Some(..) => (false, true),
- _ => (true, false),
- };
- if new_better {
- self.log(|| {
- format!(
- "New best function (at 0x{:x}) for `{}` / `{}`: {:?}",
- *address,
- raw_name,
- full_name.as_ref().unwrap_or(raw_name),
- sym
- )
- });
- if old_exists {
- self.log(|| format!("Clobbering old definition"));
- }
- best_functions.insert(raw_name.clone(), sym);
- }
- }
- _ => {}
- }
- }
-
- Ok((
- best_symbols
- .into_iter()
- .map(|(_, sym)| sym.clone())
- .sorted_by_key(|sym| match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- ParsedSymbol::Procedure(ParsedProcedure {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- _ => 0,
- })
- .collect::<Vec<_>>(),
- best_functions
- .into_iter()
- .map(|(_, func)| func.clone())
- .sorted_by_key(|sym| match sym {
- ParsedSymbol::Data(ParsedDataSymbol {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- ParsedSymbol::Procedure(ParsedProcedure {
- type_, is_public, ..
- }) => type_
- .as_ref()
- .map(|ty| {
- if *is_public {
- ty.confidence / 2
- } else {
- ty.confidence
- }
- })
- .unwrap_or(0),
- _ => 0,
- })
- .collect::<Vec<_>>(),
- ))
- }
-
- /// Parse all the symbols in a module, via the given SymbolIter
- pub fn parse_mod_symbols(&mut self, mut symbols: SymbolIter) -> Result<Vec<ParsedSymbol>> {
- // Collect tree structure first
- let mut first = None;
- let mut last_local = None;
- let mut top_level_syms = vec![];
- let mut thunk_syms = vec![];
- let mut unparsed_syms = BTreeMap::new();
- while let Some(sym) = symbols.next()? {
- if first.is_none() {
- first = Some(sym.index());
- }
- unparsed_syms.insert(sym.index(), sym);
-
- let p = sym.parse();
- self.log(|| format!("Parsed: {:x?}", p));
-
- // It's some sort of weird tree structure where SOME symbols have "end" indices
- // and anything between them and that index is a child symbol
- // Sometimes there are "end scope" symbols at those end indices but like, sometimes
- // there aren't? Which makes that entire system seem pointless (or I'm just missing
- // something and it makes sense to _someone_)
- if let Some(&(start, _end)) = self.symbol_stack.last() {
- self.add_symbol_child(start, sym.index());
- } else {
- // Place thunk symbols in their own list at the end, so they can reference
- // other symbols parsed in the module
- match &p {
- Ok(SymbolData::Thunk(_)) => {
- thunk_syms.push(sym.index());
- }
- _ => {
- top_level_syms.push(sym.index());
- }
- }
- }
- let mut popped = false;
- while let Some(&(_start, end)) = self.symbol_stack.last() {
- if sym.index().0 >= end.0 {
- let _ = self.symbol_stack.pop();
- popped = true;
- } else {
- break;
- }
- }
-
- // These aren't actually used for parsing (I don't trust them) but we can include a little
- // debug error check here and see if it's ever actually wrong
- match p {
- Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if popped => {}
- Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if !popped => {
- self.log(|| format!("Did not pop at a scope end??? WTF??"));
- }
- _ if popped => {
- self.log(|| format!("Popped but not at a scope end??? WTF??"));
- }
- _ => {}
- }
-
- // Push new scopes on the stack to build the tree
- match p {
- Ok(SymbolData::Procedure(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::InlineSite(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::Block(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::Thunk(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::SeparatedCode(data)) => {
- self.symbol_stack.push((sym.index(), data.end));
- }
- Ok(SymbolData::FrameProcedure(..)) => {
- if let Some(&(_, proc_end)) = self.symbol_stack.last() {
- self.symbol_stack.push((sym.index(), proc_end));
- }
- }
- Ok(SymbolData::Local(..)) => {
- last_local = Some(sym.index());
- }
- Ok(SymbolData::DefRange(..))
- | Ok(SymbolData::DefRangeSubField(..))
- | Ok(SymbolData::DefRangeRegister(..))
- | Ok(SymbolData::DefRangeFramePointerRelative(..))
- | Ok(SymbolData::DefRangeFramePointerRelativeFullScope(..))
- | Ok(SymbolData::DefRangeSubFieldRegister(..))
- | Ok(SymbolData::DefRangeRegisterRelative(..)) => {
- // I'd like to retract my previous statement that someone could possibly
- // understand this:
- // These symbol types impact the previous symbol, if it was a local
- // BUT ALSO!! PART III REVENGE OF THE SYM-TH: You can have more than one of
- // these and they all (?? it's undocumented) apply to the last local, PROBABLY
- if let Some(last) = last_local {
- self.add_symbol_child(last, sym.index());
- } else {
- self.log(|| format!("Found def range with no last local: {:?}", p));
- }
- }
- _ => {}
- }
- }
- assert!(self.symbol_stack.is_empty());
- // Add thunks at the end as per above
- top_level_syms.extend(thunk_syms.into_iter());
-
- // Restart and do the processing for real this time
- if let Some(first) = first {
- symbols.seek(first);
- }
-
- let mut final_symbols = HashSet::new();
-
- for root_idx in top_level_syms {
- for child_idx in self.walk_children(root_idx).into_iter() {
- let &sym = unparsed_syms
- .get(&child_idx)
- .expect("should have parsed this");
-
- self.log(|| format!("Symbol {:?} ", sym.index()));
- let (name, address) =
- if let Some(parsed) = self.handle_symbol_index(sym.index(), &sym)? {
- final_symbols.insert(sym.index());
- match parsed {
- ParsedSymbol::Data(ParsedDataSymbol { name, address, .. }) => {
- (Some(name.clone()), Some(*address))
- }
- ParsedSymbol::Procedure(ParsedProcedure { name, address, .. }) => {
- (Some(name.clone()), Some(*address))
- }
- _ => (None, None),
- }
- } else {
- (None, None)
- };
-
- if let Some(name) = name {
- self.named_symbols.insert(name.raw_name, sym.index());
- }
- if let Some(address) = address {
- if !self.addressed_symbols.contains_key(&address) {
- self.addressed_symbols.insert(address, vec![]);
- }
- self.addressed_symbols
- .get_mut(&address)
- .expect("just created this")
- .push(
- self.indexed_symbols
- .get(&sym.index())
- .ok_or_else(|| anyhow!("Can't find sym {} ?", sym.index()))?
- .clone(),
- );
- }
- }
- }
-
- let filtered_symbols = mem::replace(&mut self.indexed_symbols, BTreeMap::new())
- .into_iter()
- .filter_map(|(idx, sym)| {
- if final_symbols.contains(&idx) {
- Some(sym)
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
-
- // The symbols overlap between modules or something, so we can't keep this info around
- self.symbol_tree.clear();
- self.module_cpu_type = None;
-
- Ok(filtered_symbols)
- }
-
- /// Set a symbol to be the parent of another, building the symbol tree
- fn add_symbol_child(&mut self, parent: SymbolIndex, child: SymbolIndex) {
- if let Some(tree) = self.symbol_tree.get_mut(&parent) {
- tree.push(child);
- } else {
- self.symbol_tree.insert(parent, Vec::from([child]));
- }
-
- self.symbol_parents.insert(child, parent);
- }
-
- /// Postorder traversal of children of symbol index (only during this module parse)
- fn walk_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> {
- let mut children = vec![];
-
- if let Some(tree) = self.symbol_tree.get(&sym) {
- for &child in tree {
- children.extend(self.walk_children(child).into_iter());
- }
- }
-
- children.push(sym);
- return children;
- }
-
- /// Direct children of symbol index (only during this module parse)
- fn symbol_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> {
- if let Some(tree) = self.symbol_tree.get(&sym) {
- tree.clone()
- } else {
- vec![]
- }
- }
-
- /// Direct parent of symbol index (only during this module parse)
- #[allow(dead_code)]
- fn symbol_parent(&self, sym: SymbolIndex) -> Option<SymbolIndex> {
- self.symbol_parents.get(&sym).map(|idx| *idx)
- }
-
- /// Find symbol by index (only during this module parse)
- fn lookup_symbol(&self, sym: &SymbolIndex) -> Option<&ParsedSymbol> {
- self.indexed_symbols.get(sym)
- }
-
- /// Parse a new symbol by its index
- fn handle_symbol_index(
- &mut self,
- idx: SymbolIndex,
- sym: &Symbol,
- ) -> Result<Option<&ParsedSymbol>> {
- if let None = self.indexed_symbols.get(&idx) {
- match sym.parse() {
- Ok(data) => match self.handle_symbol(idx, &data) {
- Ok(Some(parsed)) => {
- self.log(|| format!("Symbol {} parsed into: {:?}", idx, parsed));
- self.indexed_symbols.insert(idx, parsed);
- }
- Ok(None) => {}
- e => {
- self.log(|| format!("Error parsing symbol {}: {:?}", idx, e));
- }
- },
- Err(UnimplementedSymbolKind(k)) => {
- self.log(|| format!("Not parsing unimplemented symbol {}: kind {:x?}", idx, k));
- }
- Err(e) => {
- self.log(|| format!("Could not parse symbol: {}: {}", idx, e));
- }
- };
- }
-
- Ok(self.indexed_symbols.get(&idx))
- }
-
- /// Parse a new symbol's data
- fn handle_symbol(
- &mut self,
- index: SymbolIndex,
- data: &SymbolData,
- ) -> Result<Option<ParsedSymbol>> {
- match data {
- SymbolData::ScopeEnd => self.handle_scope_end_symbol(index),
- SymbolData::ObjName(data) => self.handle_obj_name_symbol(index, &data),
- SymbolData::RegisterVariable(data) => {
- self.handle_register_variable_symbol(index, &data)
- }
- SymbolData::Constant(data) => self.handle_constant_symbol(index, &data),
- SymbolData::UserDefinedType(data) => self.handle_user_defined_type_symbol(index, &data),
- SymbolData::MultiRegisterVariable(data) => {
- self.handle_multi_register_variable_symbol(index, &data)
- }
- SymbolData::Data(data) => self.handle_data_symbol(index, &data),
- SymbolData::Public(data) => self.handle_public_symbol(index, &data),
- SymbolData::Procedure(data) => self.handle_procedure_symbol(index, &data),
- SymbolData::ThreadStorage(data) => self.handle_thread_storage_symbol(index, &data),
- SymbolData::CompileFlags(data) => self.handle_compile_flags_symbol(index, &data),
- SymbolData::UsingNamespace(data) => self.handle_using_namespace_symbol(index, &data),
- SymbolData::ProcedureReference(data) => {
- self.handle_procedure_reference_symbol(index, &data)
- }
- SymbolData::DataReference(data) => self.handle_data_reference_symbol(index, &data),
- SymbolData::AnnotationReference(data) => {
- self.handle_annotation_reference_symbol(index, &data)
- }
- SymbolData::Trampoline(data) => self.handle_trampoline_symbol(index, &data),
- SymbolData::Export(data) => self.handle_export_symbol(index, &data),
- SymbolData::Local(data) => self.handle_local_symbol(index, &data),
- SymbolData::BuildInfo(data) => self.handle_build_info_symbol(index, &data),
- SymbolData::InlineSite(data) => self.handle_inline_site_symbol(index, &data),
- SymbolData::InlineSiteEnd => self.handle_inline_site_end_symbol(index),
- SymbolData::ProcedureEnd => self.handle_procedure_end_symbol(index),
- SymbolData::Label(data) => self.handle_label_symbol(index, &data),
- SymbolData::Block(data) => self.handle_block_symbol(index, &data),
- SymbolData::RegisterRelative(data) => {
- self.handle_register_relative_symbol(index, &data)
- }
- SymbolData::Thunk(data) => self.handle_thunk_symbol(index, &data),
- SymbolData::SeparatedCode(data) => self.handle_separated_code_symbol(index, &data),
- SymbolData::DefRange(data) => self.handle_def_range(index, &data),
- SymbolData::DefRangeSubField(data) => self.handle_def_range_sub_field(index, &data),
- SymbolData::DefRangeRegister(data) => self.handle_def_range_register(index, &data),
- SymbolData::DefRangeFramePointerRelative(data) => {
- self.handle_def_range_frame_pointer_relative_symbol(index, &data)
- }
- SymbolData::DefRangeFramePointerRelativeFullScope(data) => {
- self.handle_def_range_frame_pointer_relative_full_scope_symbol(index, &data)
- }
- SymbolData::DefRangeSubFieldRegister(data) => {
- self.handle_def_range_sub_field_register_symbol(index, &data)
- }
- SymbolData::DefRangeRegisterRelative(data) => {
- self.handle_def_range_register_relative_symbol(index, &data)
- }
- SymbolData::BasePointerRelative(data) => {
- self.handle_base_pointer_relative_symbol(index, &data)
- }
- SymbolData::FrameProcedure(data) => self.handle_frame_procedure_symbol(index, &data),
- SymbolData::CallSiteInfo(data) => self.handle_call_site_info(index, &data),
- e => Err(anyhow!("Unhandled symbol type {:?}", e)),
- }
- }
-
- fn handle_scope_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ScopeEnd symbol"));
- Ok(None)
- }
-
- fn handle_obj_name_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ObjNameSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ObjName symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_register_variable_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &RegisterVariableSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got RegisterVariable symbol: {:?}", data));
-
- let storage = if let Some(reg) = self.convert_register(data.register) {
- vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- index: 0,
- storage: reg,
- },
- base_relative: false,
- stack_relative: false,
- }]
- } else {
- // TODO: What do we do here?
- vec![]
- };
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage,
- is_param: data.slot.map_or(true, |slot| slot > 0),
- })))
- }
-
- fn handle_constant_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ConstantSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Constant symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_user_defined_type_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &UserDefinedTypeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got UserDefinedType symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_multi_register_variable_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &MultiRegisterVariableSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got MultiRegisterVariable symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_data_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DataSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Data symbol: {:?}", data));
-
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
-
- // Sometimes the demangler REALLY knows what type this is supposed to be, and the
- // data symbol is actually wrong. So in those cases, let the demangler take precedence
- // Otherwise-- the demangler is usually wrong and clueless
- let data_type = t.merge(self.lookup_type_conf(&data.type_index, false)?);
-
- // Ignore symbols with no name and no type
- if !self
- .settings
- .get_bool("pdb.features.allowUnnamedVoidSymbols", Some(self.bv), None)
- && name.is_none()
- {
- if let Some(ty) = &data_type {
- if ty.contents.type_class() == TypeClass::VoidTypeClass {
- return Ok(None);
- }
- } else {
- return Ok(None);
- }
- }
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- self.log(|| {
- format!(
- "DATA: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- &data_type
- )
- });
-
- Ok(Some(ParsedSymbol::Data(ParsedDataSymbol {
- is_public: false,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: data_type,
- })))
- }
-
- fn handle_public_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &PublicSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Public symbol: {:?}", data));
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- // These are generally low confidence because we only have the demangler to inform us of type
-
- if data.function {
- self.log(|| {
- format!(
- "PUBLIC FUNCTION: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- t
- )
- });
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: true,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: t,
- locals: vec![],
- })))
- } else {
- self.log(|| {
- format!(
- "PUBLIC DATA: 0x{:x}: {:?} {:?}",
- self.bv.start() + rva.0 as u64,
- &name,
- t
- )
- });
-
- Ok(Some(ParsedSymbol::Data(ParsedDataSymbol {
- is_public: true,
- address: self.bv.start() + rva.0 as u64,
- name,
- type_: t,
- })))
- }
- }
-
- /// Given a proc symbol index and guessed type (from demangler or tpi), find all the local variables
- /// and parameters related to that symbol.
- /// Returns Ok(Some((resolved params, locals))))
- fn lookup_locals(
- &self,
- index: SymbolIndex,
- type_index: TypeIndex,
- demangled_type: Option<Conf<Ref<Type>>>,
- ) -> Result<(Option<Conf<Ref<Type>>>, Vec<ParsedVariable>)> {
- // So generally speaking, here's the information we have:
- // - The function type is usually accurate wrt the parameter locations
- // - The parameter symbols have the names we want for the params
- // - The parameter symbols are a big ugly mess
- // We basically want to take the function type from the type, and just fill in the
- // names of all the parameters. Non-param locals don't really matter since binja
- // can't handle them anyway.
-
- // Type parameters order needs to be like this:
- // 1. `this` pointer (if exists)
- // 2. Various stack params
- // 3. Various register params
- // We assume that if a parameter is found in a register, that is where it is passed.
- // Otherwise they are in the default order as per the CC
-
- // Get child objects and search for local variable names
- let mut locals = vec![];
- let mut params = vec![];
- let mut known_frame = false;
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::ProcedureInfo(info)) => {
- params = info.params.clone();
- locals = info.locals.clone();
- known_frame = true;
- }
- _ => {}
- }
- }
-
- let raw_type = self.lookup_type_conf(&type_index, false)?;
- let fancy_type = self.lookup_type_conf(&type_index, true)?;
-
- // Best guess so far in case of error handling
- let fancier_type = fancy_type
- .clone()
- .merge(raw_type.clone())
- .merge(demangled_type.clone());
-
- if !known_frame {
- return Ok((fancier_type, vec![]));
- }
-
- // We need both of these to exist (not sure why they wouldn't)
- let (raw_type, fancy_type) = match (raw_type, fancy_type) {
- (Some(raw), Some(fancy)) => (raw, fancy),
- _ => return Ok((fancier_type, vec![])),
- };
-
- let raw_params = raw_type
- .contents
- .parameters()
- .map_err(|_| anyhow!("no params"))?;
- let mut fancy_params = fancy_type
- .contents
- .parameters()
- .map_err(|_| anyhow!("no params"))?;
-
- // Collect all the parameters we are expecting from the symbols
- let mut parsed_params = vec![];
- for p in &params {
- let param = FunctionParameter::new(
- p.type_.clone().merge(Conf::new(
- Type::int(self.arch.address_size(), false),
- min_confidence(),
- )),
- p.name.clone(),
- p.storage.get(0).map(|loc| loc.location.clone()),
- );
- // Ignore thisptr because it's not technically part of the raw type signature
- if p.name != "this" {
- parsed_params.push(param);
- }
- }
- let mut parsed_locals = vec![];
- for p in &locals {
- let param = FunctionParameter::new(
- p.type_.clone().merge(Conf::new(
- Type::int(self.arch.address_size(), false),
- min_confidence(),
- )),
- p.name.clone(),
- p.storage.get(0).map(|loc| loc.location.clone()),
- );
- // Ignore thisptr because it's not technically part of the raw type signature
- if p.name != "this" {
- parsed_locals.push(param);
- }
- }
-
- self.log(|| format!("Raw params: {:#x?}", raw_params));
- self.log(|| format!("Fancy params: {:#x?}", fancy_params));
- self.log(|| format!("Parsed params: {:#x?}", parsed_params));
-
- // We expect one parameter for each unnamed parameter in the marked up type
- let expected_param_count = fancy_params
- .iter()
- .filter(|p| p.name.as_str().is_empty())
- .count();
- // Sanity
- if expected_param_count != raw_params.len() {
- return Err(anyhow!(
- "Mismatched number of formal parameters and interpreted parameters"
- ));
- }
-
- // If we don't have enough parameters to fill the slots, there's a problem here
- // So just fallback to the unnamed params
- if expected_param_count > parsed_params.len() {
- // As per reversing of msdia140.dll (and nowhere else): if a function doesn't have
- // enough parameter variables declared as parameters, the remaining parameters are
- // the first however many locals. If you don't have enough of those, idk??
- if expected_param_count > (parsed_params.len() + parsed_locals.len()) {
- return Ok((fancier_type, vec![]));
- }
- parsed_params.extend(parsed_locals.into_iter());
- }
- let expected_parsed_params = parsed_params
- .drain(0..expected_param_count)
- .collect::<Vec<_>>();
-
- // For all formal parameters, apply names to them in fancy_params
- // These should be all types in fancy_params that are unnamed (named ones we inserted)
-
- let mut i = 0;
- for p in fancy_params.iter_mut() {
- if p.name.as_str().is_empty() {
- if p.t.contents != expected_parsed_params[i].t.contents {
- self.log(|| {
- format!(
- "Suspicious parameter {}: {:?} vs {:?}",
- i, p, expected_parsed_params[i]
- )
- });
- }
- if expected_parsed_params[i].name.as_str() == "__formal" {
- p.name = format!("__formal{}", i);
- } else {
- p.name = expected_parsed_params[i].name.clone();
- }
- i += 1;
- }
- }
-
- // Now apply the default location for the params from the cc
- let cc = fancy_type
- .contents
- .calling_convention()
- .map_or_else(|_| Conf::new(self.default_cc.clone(), 0), |cc| cc);
-
- self.log(|| {
- format!(
- "Type calling convention: {:?}",
- fancy_type.contents.calling_convention()
- )
- });
- self.log(|| format!("Default calling convention: {:?}", self.default_cc));
- self.log(|| format!("Result calling convention: {:?}", cc));
-
- let locations = cc.contents.variables_for_parameters(&fancy_params, None);
- for (p, new_location) in fancy_params.iter_mut().zip(locations.into_iter()) {
- p.location = Some(new_location);
- }
-
- self.log(|| format!("Final params: {:#x?}", fancy_params));
-
- // Use the new locals we've parsed to make the Real Definitely True function type
- let fancy_type = Conf::new(
- Type::function_with_options(
- &fancy_type
- .contents
- .return_value()
- .map_err(|_| anyhow!("no ret"))?,
- fancy_params.as_slice(),
- fancy_type.contents.has_variable_arguments().contents,
- &cc,
- fancy_type.contents.stack_adjustment(),
- ),
- max_confidence(),
- );
-
- let fancier_type = fancy_type
- .clone()
- .merge(raw_type.clone())
- .merge(demangled_type.clone());
-
- self.log(|| format!("Raw type: {:#x?}", raw_type));
- self.log(|| format!("Demangled type: {:#x?}", demangled_type));
- self.log(|| format!("Fancy type: {:#x?}", fancy_type));
- self.log(|| format!("Result type: {:#x?}", fancier_type));
-
- Ok((Some(fancier_type), vec![]))
- }
-
- fn handle_procedure_symbol(
- &mut self,
- index: SymbolIndex,
- data: &ProcedureSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Procedure symbol: {:?}", data));
-
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let address = self.bv.start() + rva.0 as u64;
-
- let mut raw_name = data.name.to_string().to_string();
-
- // Generally proc symbols have real types, but use the demangler just in case the microsoft
- // public pdbs have the function type as `void`
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let mut name = name.map(|n| n.string());
-
- // Some proc symbols don't have a mangled name, so try and look up their name
- if name.is_none() || name.as_ref().expect("just failed none") == &raw_name {
- // Lookup public symbol with the same name
- if let Some(others) = self.addressed_symbols.get(&address) {
- for o in others {
- match o {
- ParsedSymbol::Procedure(ParsedProcedure {
- name: proc_name, ..
- }) => {
- if proc_name.full_name.as_ref().unwrap_or(&proc_name.raw_name)
- == &raw_name
- {
- name = Some(raw_name);
- raw_name = proc_name.raw_name.clone();
- break;
- }
- }
- _ => {}
- }
- }
- }
- }
-
- let (fn_type, locals) = self.lookup_locals(index, data.type_index, t)?;
-
- let name = SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- };
-
- self.log(|| format!("PROC: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address,
- name,
- type_: fn_type,
- locals,
- })))
- }
-
- fn handle_thread_storage_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ThreadStorageSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ThreadStorage symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_compile_flags_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &CompileFlagsSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got CompileFlags symbol: {:?}", data));
- self.module_cpu_type = Some(data.cpu_type);
- Ok(None)
- }
-
- fn handle_using_namespace_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &UsingNamespaceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got UsingNamespace symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_procedure_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ProcedureReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ProcedureReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_data_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DataReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DataReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_annotation_reference_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &AnnotationReferenceSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got AnnotationReference symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_trampoline_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &TrampolineSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Trampoline symbol: {:?}", data));
- let rva = data.thunk.to_rva(&self.address_map).unwrap_or_default();
- let target_rva = data.target.to_rva(&self.address_map).unwrap_or_default();
-
- let address = self.bv.start() + rva.0 as u64;
- let target_address = self.bv.start() + target_rva.0 as u64;
-
- let mut target_name = None;
- let mut thunk_name = None;
-
- let mut fn_type: Option<Conf<Ref<Type>>> = None;
-
- // These have the same name as their target, so look that up
- if let Some(syms) = self.addressed_symbols.get(&target_address) {
- // Take name from the public symbol
- for sym in syms {
- match sym {
- ParsedSymbol::Procedure(proc) if proc.is_public => {
- fn_type = proc.type_.clone().merge(fn_type);
- target_name = Some(proc.name.clone());
- }
- _ => {}
- }
- }
- // Take type from the non-public symbol if we have one
- for sym in syms {
- match sym {
- ParsedSymbol::Procedure(proc) if !proc.is_public => {
- fn_type = proc.type_.clone().merge(fn_type);
- if target_name.is_none() {
- target_name = Some(proc.name.clone());
- }
- }
- _ => {}
- }
- }
- }
-
- // And handle the fact that pdb public symbols for trampolines have the name of their target
- // ugh
- if let Some(syms) = self.addressed_symbols.get_mut(&address) {
- if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() {
- if let Some(tn) = &target_name {
- if proc.name.raw_name == tn.raw_name
- || proc.name.full_name.as_ref().unwrap_or(&proc.name.raw_name)
- == tn.full_name.as_ref().unwrap_or(&tn.raw_name)
- {
- // Yeah it's one of these symbols
- let old_name = proc.name.clone();
- let new_name = SymbolNames {
- raw_name: "j_".to_string() + &old_name.raw_name,
- short_name: old_name.short_name.as_ref().map(|n| "j_".to_string() + n),
- full_name: old_name.full_name.as_ref().map(|n| "j_".to_string() + n),
- };
-
- // I'm so sorry about this
- // XXX: Update the parsed public symbol's name to use j_ syntax
- if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) {
- self.named_symbols.insert(new_name.raw_name.clone(), idx);
- }
- if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) {
- self.parsed_symbols_by_name
- .insert(new_name.raw_name.clone(), idx);
- match &mut self.parsed_symbols[idx] {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: parsed_name, ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: parsed_name,
- ..
- }) => {
- parsed_name.raw_name = new_name.raw_name.clone();
- parsed_name.short_name = new_name.short_name.clone();
- parsed_name.full_name = new_name.full_name.clone();
- }
- _ => {}
- }
- }
- proc.name = new_name.clone();
- thunk_name = Some(new_name);
- }
- }
- }
- }
-
- if thunk_name.is_none() {
- if let Some(tn) = target_name {
- thunk_name = Some(SymbolNames {
- raw_name: "j_".to_string() + &tn.raw_name,
- short_name: tn.short_name.as_ref().map(|n| "j_".to_string() + n),
- full_name: tn.full_name.as_ref().map(|n| "j_".to_string() + n),
- });
- }
- }
-
- let name = thunk_name.unwrap_or(SymbolNames {
- raw_name: format!("j_sub_{:x}", target_address),
- short_name: None,
- full_name: None,
- });
-
- self.log(|| format!("TRAMPOLINE: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address,
- name,
- type_: fn_type,
- locals: vec![],
- })))
- }
-
- fn handle_export_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ExportSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Export symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_local_symbol(
- &mut self,
- index: SymbolIndex,
- data: &LocalSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Local symbol: {:?}", data));
- // Look for definition ranges for this symbol
- let mut locations = vec![];
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::Location(loc)) => {
- locations.push(loc.clone());
- }
- _ => {}
- }
- }
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: locations,
- is_param: data.flags.isparam,
- })))
- }
-
- fn handle_build_info_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BuildInfoSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got BuildInfo symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_inline_site_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &InlineSiteSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got InlineSite symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_inline_site_end_symbol(
- &mut self,
- _index: SymbolIndex,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got InlineSiteEnd symbol"));
- Ok(None)
- }
-
- fn handle_procedure_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got ProcedureEnd symbol"));
- Ok(None)
- }
-
- fn handle_label_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &LabelSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Label symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_block_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BlockSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Block symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_register_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &RegisterRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got RegisterRelative symbol: {:?}", data));
- match self.lookup_register(data.register) {
- Some(X86(X86Register::EBP)) | Some(AMD64(AMD64Register::RBP)) => {
- // Local is relative to base pointer
- // This is just another way of writing BasePointerRelativeSymbol
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: true, // !!
- stack_relative: false, // !!
- }],
- is_param: data.slot.map_or(false, |slot| slot > 0),
- })))
- }
- Some(X86(X86Register::ESP)) | Some(AMD64(AMD64Register::RSP)) => {
- // Local is relative to stack pointer
- // This is the same as base pointer case except not base relative (ofc)
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: false, // !!
- stack_relative: true, // !!
- }],
- is_param: data.slot.map_or(false, |slot| slot > 0),
- })))
- }
- _ => {
- // Local is relative to some non-bp register.
- // This is, of course, totally possible and normal
- // Binja just can't handle it in the slightest.
- // Soooooooo ????
- // TODO
- Ok(None)
- }
- }
- }
-
- fn handle_thunk_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &ThunkSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got Thunk symbol: {:?}", data));
- let rva = data.offset.to_rva(&self.address_map).unwrap_or_default();
- let raw_name = data.name.to_string().to_string();
- let address = self.bv.start() + rva.0 as u64;
-
- let (t, name) = self.demangle_to_type(&raw_name, rva)?;
- let name = name.map(|n| n.string());
- let mut fn_type = t;
-
- // These have the same name as their target, so look that up
- if let Some(&idx) = self.named_symbols.get(&raw_name) {
- if let Some(ParsedSymbol::Procedure(proc)) = self.indexed_symbols.get(&idx) {
- fn_type = proc.type_.clone().merge(fn_type);
- }
- }
-
- let mut thunk_name = None;
-
- // And handle the fact that pdb public symbols for thunks have the name of their target
- // ugh
- if let Some(syms) = self.addressed_symbols.get_mut(&address) {
- if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() {
- // Yeah it's one of these symbols
- // Make sure we don't do this twice (does that even happen?)
- if !proc.name.raw_name.starts_with("j_") {
- let old_name = proc.name.clone();
- let new_name = SymbolNames {
- raw_name: "j_".to_string() + &old_name.raw_name,
- short_name: Some(
- "j_".to_string() + old_name.short_name.as_ref().unwrap_or(&raw_name),
- ),
- full_name: Some(
- "j_".to_string() + old_name.full_name.as_ref().unwrap_or(&raw_name),
- ),
- };
-
- // I'm so sorry about this
- // XXX: Update the parsed public symbol's name to use j_ syntax
- if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) {
- self.named_symbols.insert(new_name.raw_name.clone(), idx);
- }
- if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) {
- self.parsed_symbols_by_name
- .insert(new_name.raw_name.clone(), idx);
- match &mut self.parsed_symbols[idx] {
- ParsedSymbol::Data(ParsedDataSymbol {
- name: parsed_name, ..
- })
- | ParsedSymbol::Procedure(ParsedProcedure {
- name: parsed_name, ..
- }) => {
- parsed_name.raw_name = new_name.raw_name.clone();
- parsed_name.short_name = new_name.short_name.clone();
- parsed_name.full_name = new_name.full_name.clone();
- }
- _ => {}
- }
- }
- proc.name = new_name.clone();
- thunk_name = Some(new_name);
- }
- }
- }
-
- let locals = vec![];
- let name = thunk_name.unwrap_or(SymbolNames {
- raw_name,
- short_name: name.clone(),
- full_name: name,
- });
-
- self.log(|| format!("THUNK: 0x{:x}: {:?} {:?}", address, &name, &fn_type));
-
- Ok(Some(ParsedSymbol::Procedure(ParsedProcedure {
- is_public: false,
- address: address,
- name,
- type_: fn_type,
- locals,
- })))
- }
-
- fn handle_separated_code_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &SeparatedCodeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got SeparatedCode symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRange symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_sub_field(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSubFieldSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeSubField symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_register(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeRegisterSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeRegister symbol: {:?}", data));
- if let Some(reg) = self.convert_register(data.register) {
- Ok(Some(ParsedSymbol::Location(ParsedLocation {
- location: Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- index: 0,
- storage: reg,
- },
- base_relative: false,
- stack_relative: false,
- })))
- } else {
- Ok(None)
- }
- }
-
- fn handle_def_range_frame_pointer_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeFramePointerRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeFramePointerRelative symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_frame_pointer_relative_full_scope_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeFramePointerRelativeFullScopeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| {
- format!(
- "Got DefRangeFramePointerRelativeFullScope symbol: {:?}",
- data
- )
- });
- Ok(None)
- }
-
- fn handle_def_range_sub_field_register_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeSubFieldRegisterSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeSubFieldRegister symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_def_range_register_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &DefRangeRegisterRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got DefRangeRegisterRelative symbol: {:?}", data));
- Ok(None)
- }
-
- fn handle_base_pointer_relative_symbol(
- &mut self,
- _index: SymbolIndex,
- data: &BasePointerRelativeSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got BasePointerRelative symbol: {:?}", data));
-
- // These are usually parameters if offset > 0
-
- Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name: data.name.to_string().to_string(),
- type_: self.lookup_type_conf(&data.type_index, false)?,
- storage: vec![ParsedLocation {
- location: Variable {
- t: VariableSourceType::StackVariableSourceType,
- index: 0,
- storage: data.offset as i64,
- },
- base_relative: true,
- stack_relative: false,
- }],
- is_param: data.offset as i64 > 0 || data.slot.map_or(false, |slot| slot > 0),
- })))
- }
-
- fn handle_frame_procedure_symbol(
- &mut self,
- index: SymbolIndex,
- data: &FrameProcedureSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got FrameProcedure symbol: {:?}", data));
-
- // This symbol generally comes before a proc and all various parameters
- // It has a lot of information we don't care about, and some information we maybe do?
- // This function also tries to find all the locals and parameters of the procedure
-
- let mut params = vec![];
- let mut locals = vec![];
- let mut seen_offsets = HashSet::new();
-
- for child in self.symbol_children(index) {
- match self.lookup_symbol(&child) {
- Some(ParsedSymbol::LocalVariable(ParsedVariable {
- name,
- type_,
- storage,
- is_param,
- ..
- })) => {
- let new_storage = storage.iter().map(|&var| var.location).collect::<Vec<_>>();
-
- // See if the parameter really is a parameter. Sometimes they don't say they are
- let mut really_is_param = *is_param;
- for loc in &new_storage {
- match loc {
- Variable {
- t: VariableSourceType::RegisterVariableSourceType,
- ..
- } => {
- // Assume register vars are always parameters
- really_is_param = true;
- }
- Variable {
- t: VariableSourceType::StackVariableSourceType,
- storage,
- ..
- } if *storage >= 0 => {
- // Sometimes you can get two locals at the same offset, both rbp+(x > 0)
- // I'm guessing from looking at dumps from dia2dump that only the first
- // one is considered a parameter, although there are times that I see
- // two params at the same offset and both are considered parameters...
- // This doesn't seem possible (or correct) because they would overlap
- // and only one would be useful anyway.
- // Regardless of the mess, Binja can only handle one parameter per slot
- // so we're just going to use the first one.
- really_is_param = seen_offsets.insert(*storage);
- }
- _ => {}
- }
- }
-
- if really_is_param {
- params.push(ParsedVariable {
- name: name.clone(),
- type_: type_.clone(),
- storage: new_storage
- .into_iter()
- .map(|loc| ParsedLocation {
- location: loc,
- // This has been handled now
- base_relative: false,
- stack_relative: false,
- })
- .collect(),
- is_param: really_is_param,
- });
- } else {
- locals.push(ParsedVariable {
- name: name.clone(),
- type_: type_.clone(),
- storage: new_storage
- .into_iter()
- .map(|loc| ParsedLocation {
- location: loc,
- // This has been handled now
- base_relative: false,
- stack_relative: false,
- })
- .collect(),
- is_param: really_is_param,
- });
- }
- }
- Some(ParsedSymbol::Data(_)) => {
- // Apparently you can have static data symbols as parameters
- // Because of course you can
- }
- None => {}
- e => self.log(|| format!("Unexpected symbol type in frame: {:?}", e)),
- }
- }
-
- Ok(Some(ParsedSymbol::ProcedureInfo(ParsedProcedureInfo {
- params,
- locals,
- })))
- }
-
- fn handle_call_site_info(
- &mut self,
- _index: SymbolIndex,
- data: &CallSiteInfoSymbol,
- ) -> Result<Option<ParsedSymbol>> {
- self.log(|| format!("Got CallSiteInfo symbol: {:?}", data));
- Ok(None)
- }
-
- /// Demangle a name and get a type out
- /// Also fixes void(void) and __s_RTTI_Nonsense
- fn demangle_to_type(
- &self,
- raw_name: &String,
- rva: Rva,
- ) -> Result<(Option<Conf<Ref<Type>>>, Option<QualifiedName>)> {
- let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, true) {
- Ok((Some(t), name)) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name),
- Ok((_, name)) => (None, name),
- _ => (None, vec![raw_name.clone()]),
- };
-
- if let Some(ty) = t.as_ref() {
- if ty.contents.type_class() == TypeClass::FunctionTypeClass {
- // demangler makes (void) into (void arg1) which is wrong
- let parameters = ty
- .contents
- .parameters()
- .map_err(|_| anyhow!("no parameters"))?;
- if let [p] = parameters.as_slice() {
- if p.t.contents.type_class() == TypeClass::VoidTypeClass {
- t = Some(Conf::new(
- Type::function::<_>(
- &ty.contents
- .return_value()
- .map_err(|_| anyhow!("no return value"))?,
- &[],
- ty.contents.has_variable_arguments().contents,
- ),
- ty.confidence,
- ))
- }
- }
- }
- }
-
- // These have types but they aren't actually set anywhere. So it's the demangler's
- // job to take care of them, apparently?
- static MEM: OnceLock<Vec<(String, Vec<String>)>> = OnceLock::new();
- let name_to_type = MEM.get_or_init(|| {
- vec![
- (
- "`RTTI Complete Object Locator'".to_string(),
- vec![
- "_s_RTTICompleteObjectLocator".to_string(),
- "_s__RTTICompleteObjectLocator".to_string(),
- "_s__RTTICompleteObjectLocator2".to_string(),
- ],
- ),
- (
- "`RTTI Class Hierarchy Descriptor'".to_string(),
- vec![
- "_s_RTTIClassHierarchyDescriptor".to_string(),
- "_s__RTTIClassHierarchyDescriptor".to_string(),
- "_s__RTTIClassHierarchyDescriptor2".to_string(),
- ],
- ),
- (
- // TODO: This type is dynamic
- "`RTTI Base Class Array'".to_string(),
- vec![
- "_s_RTTIBaseClassArray".to_string(),
- "_s__RTTIBaseClassArray".to_string(),
- "_s__RTTIBaseClassArray2".to_string(),
- ],
- ),
- (
- "`RTTI Base Class Descriptor at (".to_string(),
- vec![
- "_s_RTTIBaseClassDescriptor".to_string(),
- "_s__RTTIBaseClassDescriptor".to_string(),
- "_s__RTTICBaseClassDescriptor2".to_string(),
- ],
- ),
- (
- "`RTTI Type Descriptor'".to_string(),
- vec!["_TypeDescriptor".to_string()],
- ),
- ]
- });
-
- if let Some(last_name) = name.last() {
- for (search_name, search_types) in name_to_type.iter() {
- if last_name.contains(search_name) {
- for search_type in search_types {
- if let Some(ty) = self.named_types.get(search_type) {
- // Fallback in case we don't find a specific one
- t = Some(Conf::new(
- Type::named_type_from_type(search_type, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
-
- if self.settings.get_bool(
- "pdb.features.expandRTTIStructures",
- Some(self.bv),
- None,
- ) {
- if let Some((lengthy_type, length)) =
- self.make_lengthy_type(ty, self.bv.start() + rva.0 as u64)?
- {
- // See if we have a type with this length
- let lengthy_name =
- format!("${}$_extraBytes_{}", search_type, length);
-
- if let Some(ty) = self.named_types.get(&lengthy_name) {
- // Wow!
- t = Some(Conf::new(
- Type::named_type_from_type(lengthy_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- t = Some(Conf::new(lengthy_type, DEMANGLE_CONFIDENCE));
- }
- }
- }
- }
- }
- }
- }
- }
-
- // VTables have types on their data symbols,
- if let Some((class_name, last)) = name.join("::").rsplit_once("::") {
- if last.contains("`vftable'") {
- let mut vt_name = class_name.to_string() + "::" + "VTable";
- if last.contains("{for") {
- // DerivedClass::`vftable'{for `BaseClass'}
- let mut base_name = last.to_owned();
- base_name.drain(0..("`vftable'{for `".len()));
- base_name.drain((base_name.len() - "'}".len())..(base_name.len()));
- // Multiply inherited classes have multiple vtable types
- // TODO: Do that
- vt_name = base_name + "::" + "VTable";
- }
-
- vt_name = vt_name
- .replace("class ", "")
- .replace("struct ", "")
- .replace("enum ", "");
-
- if let Some(ty) = self.named_types.get(&vt_name) {
- t = Some(Conf::new(
- Type::named_type_from_type(&vt_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- // Sometimes the demangler has trouble with `class Foo` in templates
- vt_name = vt_name
- .replace("class ", "")
- .replace("struct ", "")
- .replace("enum ", "");
-
- if let Some(ty) = self.named_types.get(&vt_name) {
- t = Some(Conf::new(
- Type::named_type_from_type(&vt_name, ty.as_ref()),
- DEMANGLE_CONFIDENCE,
- ));
- } else {
- t = Some(Conf::new(
- Type::named_type_from_type(
- &vt_name,
- Type::structure(StructureBuilder::new().finalize().as_ref())
- .as_ref(),
- ),
- DEMANGLE_CONFIDENCE,
- ));
- }
- }
- }
- }
-
- if let Some(last_name) = name.last_mut() {
- if last_name.starts_with("__imp_") {
- last_name.drain(0..("__imp_".len()));
- }
- }
-
- let name = if name.len() == 1 && &name[0] == raw_name && raw_name.starts_with('?') {
- None
- } else if name.len() == 1 && name[0] == "" {
- None
- } else if name.len() > 0 && name[0].starts_with("\x7f") {
- // Not sure why these exist but they do Weird Stuff
- name[0].drain(0..1);
- Some(QualifiedName::from(name))
- } else {
- Some(QualifiedName::from(name))
- };
-
- Ok((t, name))
- }
-
- fn make_lengthy_type(
- &self,
- base_type: &Ref<Type>,
- base_address: u64,
- ) -> Result<Option<(Ref<Type>, usize)>> {
- if base_type.type_class() != TypeClass::StructureTypeClass {
- return Ok(None);
- }
- let structure = base_type
- .get_structure()
- .map_err(|_| anyhow!("Expected structure"))?;
- let mut members = structure
- .members()
- .map_err(|_| anyhow!("Expected structure to have members"))?;
- let last_member = members
- .last_mut()
- .ok_or_else(|| anyhow!("Not enough members"))?;
-
- if last_member.ty.contents.type_class() != TypeClass::ArrayTypeClass {
- return Ok(None);
- }
- if last_member.ty.contents.count() != 0 {
- return Ok(None);
- }
-
- let member_element = last_member
- .ty
- .contents
- .element_type()
- .map_err(|_| anyhow!("Last member has no type"))?
- .contents;
- let member_width = member_element.width();
-
- // Read member_width bytes from bv starting at that member, until we read all zeroes
- let member_address = base_address + last_member.offset;
-
- let mut bytes = Vec::<u8>::new();
- bytes.resize(member_width as usize, 0);
-
- let mut element_count = 0;
- while self.bv.read(
- bytes.as_mut_slice(),
- member_address + member_width * element_count,
- ) == member_width as usize
- {
- if bytes.iter().all(|&b| b == 0) {
- break;
- }
- element_count += 1;
- }
-
- // Make a new copy of the type with the correct element count
- last_member.ty.contents = Type::array(member_element.as_ref(), element_count);
-
- Ok(Some((
- Type::structure(StructureBuilder::from(members).finalize().as_ref()),
- element_count as usize,
- )))
- }
-
- /// Sorry about the type names
- /// Given a pdb::Register (u32), get a pdb::register::Register (big enum with names)
- fn lookup_register(&self, reg: pdb::Register) -> Option<pdb::register::Register> {
- if let Some(cpu) = self.module_cpu_type {
- pdb::register::Register::new(reg, cpu).ok()
- } else {
- None
- }
- }
-
- /// Convert a pdb::Register (u32) to a binja register index for the current arch
- fn convert_register(&self, reg: pdb::Register) -> Option<i64> {
- match self.lookup_register(reg) {
- Some(X86(xreg)) => {
- self.log(|| format!("Register {:?} ==> {:?}", reg, xreg));
- self.arch
- .register_by_name(xreg.to_string().to_lowercase())
- .map(|reg| reg.id() as i64)
- }
- Some(AMD64(areg)) => {
- self.log(|| format!("Register {:?} ==> {:?}", reg, areg));
- self.arch
- .register_by_name(areg.to_string().to_lowercase())
- .map(|reg| reg.id() as i64)
- }
- // TODO: Other arches
- _ => None,
- }
- }
-}