summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Ferrell <josh@vector35.com>2024-06-13 14:13:11 -0400
committerJosh Ferrell <josh@vector35.com>2024-06-13 14:13:11 -0400
commitd75066545afdc9b5d0a52bd2518e43f683c671a5 (patch)
treebed26d348a726fc02c1d44cbdc36a8056b44baaf
parentf3a440248cda4d0c850cd32e3aa127ab29e0bb3e (diff)
Apply stack variables from DWARF
-rw-r--r--binaryninjaapi.h7
-rw-r--r--binaryninjacore.h2
-rw-r--r--debuginfo.cpp33
-rw-r--r--rust/Cargo.lock7
-rw-r--r--rust/examples/dwarf/dwarf_import/Cargo.toml1
-rw-r--r--rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs88
-rw-r--r--rust/examples/dwarf/dwarf_import/src/functions.rs4
-rw-r--r--rust/examples/dwarf/dwarf_import/src/helpers.rs2
-rw-r--r--rust/examples/dwarf/dwarf_import/src/lib.rs104
-rw-r--r--rust/examples/dwarf/dwarf_import/src/types.rs37
-rw-r--r--rust/examples/pdb-ng/src/parser.rs2
-rw-r--r--rust/src/debuginfo.rs55
-rw-r--r--rust/src/types.rs61
13 files changed, 348 insertions, 55 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index b5ce7010..0dd1b66b 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -16401,11 +16401,14 @@ namespace BinaryNinja {
Ref<Type> type;
Ref<Platform> platform;
std::vector<std::string> components;
+ std::vector<VariableNameAndType> localVariables;
DebugFunctionInfo(std::string shortName, std::string fullName, std::string rawName, uint64_t address,
- Ref<Type> type, Ref<Platform> platform, const std::vector<std::string>& components) :
+ Ref<Type> type, Ref<Platform> platform, const std::vector<std::string>& components,
+ const std::vector<VariableNameAndType>& localVariables) :
shortName(shortName), fullName(fullName), rawName(rawName),
- address(address), platform(platform), components(components)
+ address(address), platform(platform), components(components),
+ localVariables(localVariables)
{}
};
diff --git a/binaryninjacore.h b/binaryninjacore.h
index d9636c02..b90641e1 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -3169,6 +3169,8 @@ extern "C"
BNPlatform* platform;
char** components;
size_t componentN;
+ BNVariableNameAndType* localVariables;
+ size_t localVariableN;
} BNDebugFunctionInfo;
typedef struct BNSecretsProviderCallbacks
diff --git a/debuginfo.cpp b/debuginfo.cpp
index 412d44bb..9a4a2c9e 100644
--- a/debuginfo.cpp
+++ b/debuginfo.cpp
@@ -96,11 +96,26 @@ vector<DebugFunctionInfo> DebugInfo::GetFunctions(const string& parserName) cons
vector<string> components;
for (size_t componentN = 0; componentN < functions[i].componentN; ++componentN)
components.emplace_back(functions[i].components[componentN]);
+
+ vector<VariableNameAndType> localVariables;
+ for (size_t localVariableN = 0; localVariableN < functions[i].localVariableN; ++localVariableN)
+ {
+ auto& bnVar = functions[i].localVariables[localVariableN];
+
+ VariableNameAndType vnt;
+ vnt.var = bnVar.var;
+ vnt.name = bnVar.name;
+ vnt.autoDefined = bnVar.autoDefined;
+ vnt.type = Confidence<Ref<Type>>(new Type(BNNewTypeReference(bnVar.type)), bnVar.typeConfidence);
+ localVariables.push_back(vnt);
+ }
+
result.emplace_back(functions[i].shortName ? functions[i].shortName : "",
functions[i].fullName ? functions[i].fullName : "", functions[i].rawName ? functions[i].rawName : "",
functions[i].address,
functions[i].type ? new Type(BNNewTypeReference(functions[i].type)) : nullptr,
- functions[i].platform ? new CorePlatform(BNNewPlatformReference(functions[i].platform)) : nullptr, components);
+ functions[i].platform ? new CorePlatform(BNNewPlatformReference(functions[i].platform)) : nullptr,
+ components, localVariables);
}
BNFreeDebugFunctions(functions, count);
@@ -286,6 +301,17 @@ bool DebugInfo::AddFunction(const DebugFunctionInfo& function)
for (size_t i = 0; i < function.components.size(); ++i)
components[i] = (char*)function.components[i].c_str();
+ BNVariableNameAndType* localVariables = new BNVariableNameAndType[function.localVariables.size()];
+ for (size_t i = 0; i < function.localVariables.size(); i++)
+ {
+ auto v = function.localVariables[i];
+ localVariables[i].var = v.var;
+ localVariables[i].type = v.type.GetValue()->m_object;
+ localVariables[i].typeConfidence = v.type.GetConfidence();
+ localVariables[i].name = (char*)v.name.c_str();
+ localVariables[i].autoDefined = v.autoDefined;
+ }
+
BNDebugFunctionInfo input;
input.shortName = function.shortName.size() ? BNAllocString(function.shortName.c_str()) : nullptr;
@@ -296,13 +322,16 @@ bool DebugInfo::AddFunction(const DebugFunctionInfo& function)
input.platform = function.platform ? function.platform->GetObject() : nullptr;
input.components = components;
input.componentN = function.components.size();
+ input.localVariables = localVariables;
+ input.localVariableN = function.localVariables.size();
bool result = BNAddDebugFunction(m_object, &input);
BNFreeString(input.shortName);
BNFreeString(input.fullName);
BNFreeString(input.rawName);
-
+ delete[] components;
+ delete[] localVariables;
return result;
}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index dfccee5a..7e03d311 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -321,6 +321,7 @@ dependencies = [
"binaryninja",
"dwarfreader",
"gimli",
+ "iset",
"log",
]
@@ -467,6 +468,12 @@ dependencies = [
]
[[package]]
+name = "iset"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0716a0d7080cb7b20b9426276315e6ff5ed537bd920af47417b16de07f9ac76"
+
+[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/rust/examples/dwarf/dwarf_import/Cargo.toml b/rust/examples/dwarf/dwarf_import/Cargo.toml
index c8cea44a..715838a7 100644
--- a/rust/examples/dwarf/dwarf_import/Cargo.toml
+++ b/rust/examples/dwarf/dwarf_import/Cargo.toml
@@ -12,3 +12,4 @@ dwarfreader = { path = "../shared/" }
binaryninja = { path = "../../../" }
gimli = "0.28"
log = "0.4.20"
+iset = "0.2.2"
diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
index 0d4fda98..f3cc7b08 100644
--- a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
+++ b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
@@ -21,12 +21,13 @@ use binaryninja::{
rc::*,
symbol::SymbolType,
templatesimplifier::simplify_str_to_fqn,
- types::{Conf, FunctionParameter, Type},
+ types::{Conf, FunctionParameter, NamedTypedVariable, Type, Variable},
+ binaryninjacore_sys::BNVariableSourceType,
};
use gimli::{DebuggingInformationEntry, Dwarf, Reader, Unit};
-use log::{error, warn};
+use log::{debug, error, warn};
use std::{
cmp::Ordering,
collections::{hash_map::Values, HashMap},
@@ -48,6 +49,7 @@ pub(crate) struct FunctionInfoBuilder {
pub(crate) parameters: Vec<Option<(String, TypeUID)>>,
pub(crate) platform: Option<Ref<Platform>>,
pub(crate) variable_arguments: bool,
+ pub(crate) stack_variables: Vec<NamedTypedVariable>,
}
impl FunctionInfoBuilder {
@@ -172,6 +174,7 @@ pub(crate) struct DebugInfoBuilder {
full_function_name_indices: HashMap<String, usize>,
types: HashMap<TypeUID, DebugType>,
data_variables: HashMap<u64, (Option<String>, TypeUID)>,
+ range_data_offsets: iset::IntervalMap<u64, i64>
}
impl DebugInfoBuilder {
@@ -182,9 +185,14 @@ impl DebugInfoBuilder {
full_function_name_indices: HashMap::new(),
types: HashMap::new(),
data_variables: HashMap::new(),
+ range_data_offsets: iset::IntervalMap::new(),
}
}
+ pub(crate) fn set_range_data_offsets(&mut self, offsets: iset::IntervalMap<u64, i64>) {
+ self.range_data_offsets = offsets
+ }
+
#[allow(clippy::too_many_arguments)]
pub(crate) fn insert_function(
&mut self,
@@ -194,7 +202,8 @@ impl DebugInfoBuilder {
address: Option<u64>,
parameters: &Vec<Option<(String, TypeUID)>>,
variable_arguments: bool,
- ) {
+ ) -> Option<usize> {
+ // Returns the index of the function
// Raw names should be the primary key, but if they don't exist, use the full name
// TODO : Consider further falling back on address/architecture
@@ -222,7 +231,7 @@ impl DebugInfoBuilder {
self.full_function_name_indices.insert(function.full_name.clone().unwrap(), *idx);
}
- return;
+ return Some(*idx);
}
}
@@ -244,13 +253,13 @@ impl DebugInfoBuilder {
self.raw_function_name_indices.insert(function.raw_name.clone().unwrap(), *idx);
}
- return;
+ return Some(*idx);
}
}
if raw_name.is_none() && full_name.is_none() {
error!("Function entry in DWARF without full or raw name. Please report this issue.");
- return;
+ return None;
}
let function = FunctionInfoBuilder {
@@ -261,6 +270,7 @@ impl DebugInfoBuilder {
parameters: parameters.clone(),
platform: None,
variable_arguments,
+ stack_variables: vec![],
};
if let Some(n) = &function.full_name {
@@ -272,6 +282,7 @@ impl DebugInfoBuilder {
}
self.functions.push(function);
+ Some(self.functions.len()-1)
}
pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] {
@@ -321,6 +332,70 @@ impl DebugInfoBuilder {
self.types.get(&type_uid).is_some()
}
+
+ pub(crate) fn add_stack_variable(
+ &mut self,
+ fn_idx: Option<usize>,
+ offset: i64,
+ name: Option<String>,
+ type_uid: Option<TypeUID>,
+ ) {
+ let name = match name {
+ Some(x) => {
+ if x.len() == 1 && x.chars().next() == Some('\x00') {
+ // Anonymous variable, generate name
+ format!("debug_var_{}", offset)
+ }
+ else {
+ x
+ }
+ },
+ None => {
+ // Anonymous variable, generate name
+ format!("debug_var_{}", offset)
+ }
+ };
+
+ let Some(function_index) = fn_idx else {
+ // If we somehow lost track of what subprogram we're in or we're not actually in a subprogram
+ error!("Trying to add a local variable outside of a subprogram. Please report this issue.");
+ return;
+ };
+
+ // Either get the known type or use a 0 confidence void type so we at least get the name applied
+ let t = match type_uid {
+ Some(uid) => Conf::new(self.get_type(uid).unwrap().1, 128),
+ None => Conf::new(Type::void(), 0)
+ };
+ let function = &mut self.functions[function_index];
+
+ // TODO: If we can't find a known offset can we try to guess somehow?
+
+ let Some(func_addr) = function.address else {
+ // If we somehow are processing a function's variables before the function is created
+ error!("Trying to add a local variable without a known function start. Please report this issue.");
+ return;
+ };
+
+ let Some(offset_adjustment) = self.range_data_offsets.values_overlap(func_addr).next() else {
+ // Unknown why, but this is happening with MachO + external dSYM
+ debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset.", name, offset, func_addr);
+ return;
+ };
+
+ let adjusted_offset = offset - offset_adjustment;
+
+ if adjusted_offset > 0 {
+ // If we somehow end up with a positive sp offset
+ error!("Trying to add a local variable at positive storage offset {}. Please report this issue.", adjusted_offset);
+ return;
+ }
+
+ let var = Variable::new(BNVariableSourceType::StackVariableSourceType, 0, adjusted_offset);
+ function.stack_variables.push(NamedTypedVariable::new(var, name, t, false));
+
+ }
+
pub(crate) fn add_data_variable(
&mut self,
address: u64,
@@ -399,6 +474,7 @@ impl DebugInfoBuilder {
function.address,
function.platform.clone(),
vec![], // TODO : Components
+ function.stack_variables.clone(), // TODO: local non-stack variables
));
}
}
diff --git a/rust/examples/dwarf/dwarf_import/src/functions.rs b/rust/examples/dwarf/dwarf_import/src/functions.rs
index de1400d2..fabcc613 100644
--- a/rust/examples/dwarf/dwarf_import/src/functions.rs
+++ b/rust/examples/dwarf/dwarf_import/src/functions.rs
@@ -67,7 +67,7 @@ pub(crate) fn parse_function_entry<R: Reader<Offset = usize>>(
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
-) {
+) -> Option<usize> {
// Collect function properties (if they exist in this DIE)
let full_name = debug_info_builder_context.get_name(unit, entry);
let raw_name = get_raw_name(unit, entry, debug_info_builder_context);
@@ -75,5 +75,5 @@ pub(crate) fn parse_function_entry<R: Reader<Offset = usize>>(
let address = get_start_address(unit, entry, debug_info_builder_context);
let (parameters, variable_arguments) = get_parameters(unit, entry, debug_info_builder_context, debug_info_builder);
- debug_info_builder.insert_function(full_name, raw_name, return_type, address, &parameters, variable_arguments);
+ debug_info_builder.insert_function(full_name, raw_name, return_type, address, &parameters, variable_arguments)
}
diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs
index babe9713..14de90bf 100644
--- a/rust/examples/dwarf/dwarf_import/src/helpers.rs
+++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs
@@ -296,7 +296,7 @@ pub(crate) fn get_expr_value<R: Reader<Offset = usize>>(
Ok(Operation::UnsignedConstant { value }) => Some(value),
Ok(Operation::Address { address: 0 }) => None,
Ok(Operation::Address { address }) => Some(address),
- _ => None,
+ _ => None
}
} else {
None
diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs
index 71367bfc..62b8c18a 100644
--- a/rust/examples/dwarf/dwarf_import/src/lib.rs
+++ b/rust/examples/dwarf/dwarf_import/src/lib.rs
@@ -18,11 +18,14 @@ mod functions;
mod helpers;
mod types;
+use std::collections::HashMap;
+
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
use crate::functions::parse_function_entry;
use crate::helpers::{get_attr_die, get_name, get_uid, DieReference};
-use crate::types::parse_data_variable;
+use crate::types::parse_variable;
+use binaryninja::binaryview::BinaryViewBase;
use binaryninja::{
binaryview::{BinaryView, BinaryViewExt},
debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
@@ -34,7 +37,7 @@ use dwarfreader::{
create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
};
-use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, SectionId, Unit};
+use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindSection};
use log::{error, warn, LevelFilter};
@@ -188,9 +191,12 @@ fn parse_unit<R: Reader<Offset = usize>>(
) {
let mut entries = unit.entries();
+ let mut current_depth: isize = 0;
+ let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
+
// Really all we care about as we iterate the entries in a given unit is how they modify state (our perception of the file)
// There's a lot of junk we don't care about in DWARF info, so we choose a couple DIEs and mutate state (add functions (which adds the types it uses) and keep track of what namespace we're in)
- while let Ok(Some((_, entry))) = entries.next_dfs() {
+ while let Ok(Some((depth_delta, entry))) = entries.next_dfs() {
*current_die_number += 1;
if (*progress)(
*current_die_number,
@@ -201,29 +207,100 @@ fn parse_unit<R: Reader<Offset = usize>>(
return; // Parsing canceled
}
+ current_depth = current_depth.saturating_add(depth_delta);
+
+ loop {
+ if let Some((_fn_idx, depth)) = functions_by_depth.last() {
+ if current_depth <= *depth {
+ functions_by_depth.pop();
+ }
+ else {
+ break
+ }
+ }
+ else {
+ break;
+ }
+ }
+
match entry.tag() {
constants::DW_TAG_subprogram => {
- parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder)
+ let fn_idx = parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder);
+ functions_by_depth.push((fn_idx, current_depth));
}
constants::DW_TAG_variable => {
- parse_data_variable(unit, entry, debug_info_builder_context, debug_info_builder)
+ let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
+ parse_variable(unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx)
}
_ => (),
}
}
}
+fn parse_eh_frame<R: Reader>(
+ view: &BinaryView,
+ mut eh_frame: gimli::EhFrame<R>,
+) -> gimli::Result<iset::IntervalMap<u64, i64>> {
+ eh_frame.set_address_size(view.address_size() as u8);
+
+ let mut bases = gimli::BaseAddresses::default();
+ if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) {
+ bases = bases.set_eh_frame_hdr(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) {
+ bases = bases.set_eh_frame(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) {
+ bases = bases.set_text(section.start());
+ }
+ if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) {
+ bases = bases.set_got(section.start());
+ }
+
+ let mut cies = HashMap::new();
+ let mut cie_data_offsets = iset::IntervalMap::new();
+
+ let mut entries = eh_frame.entries(&bases);
+ loop {
+ match entries.next()? {
+ None => return Ok(cie_data_offsets),
+ Some(gimli::CieOrFde::Cie(_cie)) => {
+ // TODO: do we want to do anything with standalone CIEs?
+ }
+ Some(gimli::CieOrFde::Fde(partial)) => {
+ let fde = match partial.parse(|_, bases, o| {
+ cies.entry(o)
+ .or_insert_with(|| eh_frame.cie_from_offset(bases, o))
+ .clone()
+ }) {
+ Ok(fde) => fde,
+ Err(e) => {
+ error!("Failed to parse FDE: {}", e);
+ continue;
+ }
+ };
+ // Store CIE offset for FDE range
+ cie_data_offsets.insert(
+ fde.initial_address()..fde.initial_address()+fde.len(),
+ fde.cie().data_alignment_factor()
+ );
+ }
+ }
+ }
+}
+
fn parse_dwarf(
bv: &BinaryView,
+ debug_bv: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> Result<DebugInfoBuilder, ()> {
// Determine if this is a DWO
// TODO : Make this more robust...some DWOs follow non-DWO conventions
// Figure out if it's the given view or the raw view that has the dwarf info in it
- let raw_view = &bv.raw_view().unwrap();
- let view = if is_dwo_dwarf(bv) || is_non_dwo_dwarf(bv) {
- bv
+ let raw_view = &debug_bv.raw_view().unwrap();
+ let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
+ debug_bv
} else {
raw_view
};
@@ -239,11 +316,20 @@ fn parse_dwarf(
dwarf.file_type = DwarfFileType::Dwo;
}
+ let eh_frame_endian = get_endian(bv);
+ let mut eh_frame_section_reader =
+ |section_id: SectionId| -> _ { create_section_reader(section_id, bv, eh_frame_endian, dwo_file) };
+ let eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap();
+
+ let range_data_offsets = parse_eh_frame(bv, eh_frame)
+ .map_err(|e| println!("Error parsing .eh_frame: {}", e))?;
+
// Create debug info builder and recover name mapping first
// Since DWARF is stored as a tree with arbitrary implicit edges among leaves,
// it is not possible to correctly track namespaces while you're parsing "in order" without backtracking,
// so we just do it up front
let mut debug_info_builder = DebugInfoBuilder::new();
+ debug_info_builder.set_range_data_offsets(range_data_offsets);
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, dwarf) {
if !recover_names(&mut debug_info_builder_context, &progress)
|| debug_info_builder_context.total_die_count == 0
@@ -308,7 +394,7 @@ impl CustomDebugInfoParser for DWARFParser {
None
};
- match parse_dwarf(external_file.as_deref().unwrap_or(debug_file), progress) {
+ match parse_dwarf(bv, external_file.as_deref().unwrap_or(debug_file), progress) {
Ok(mut builder) => {
builder
.post_process(bv, debug_info)
diff --git a/rust/examples/dwarf/dwarf_import/src/types.rs b/rust/examples/dwarf/dwarf_import/src/types.rs
index 38bd36ea..67d0110e 100644
--- a/rust/examples/dwarf/dwarf_import/src/types.rs
+++ b/rust/examples/dwarf/dwarf_import/src/types.rs
@@ -23,27 +23,46 @@ use binaryninja::{
},
};
-use gimli::{constants, DebuggingInformationEntry, Reader, Unit};
+use gimli::{constants, AttributeValue, DebuggingInformationEntry, Operation, Reader, Unit};
-use log::warn;
+use log::{debug, error, warn};
-pub(crate) fn parse_data_variable<R: Reader<Offset = usize>>(
+pub(crate) fn parse_variable<R: Reader<Offset = usize>>(
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
+ function_index: Option<usize>,
) {
let full_name = debug_info_builder_context.get_name(unit, entry);
let type_uid = get_type(unit, entry, debug_info_builder_context, debug_info_builder);
- let address = if let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) {
- get_expr_value(unit, attr)
- } else {
- None
+ let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) else {
+ return
};
- if let (Some(address), Some(type_uid)) = (address, type_uid) {
- debug_info_builder.add_data_variable(address, full_name, type_uid);
+ let AttributeValue::Exprloc(mut expression) = attr.value() else {
+ return
+ };
+
+ match Operation::parse(&mut expression.0, unit.encoding()) {
+ Ok(Operation::FrameOffset { offset }) => {
+ debug_info_builder.add_stack_variable(function_index, offset, full_name, type_uid);
+ },
+ //Ok(Operation::RegisterOffset { register: _, offset: _, base_type: _ }) => {
+ // //TODO: look up register by index (binja register indexes don't match processor indexes?)
+ // //TODO: calculate absolute stack offset
+ // //TODO: add by absolute offset
+ //},
+ Ok(Operation::Address { address }) => {
+ if let Some(uid) = type_uid {
+ debug_info_builder.add_data_variable(address, full_name, uid)
+ }
+ },
+ Ok(op) => {
+ debug!("Unhandled operation type for variable: {:?}", op);
+ },
+ Err(e) => error!("Error parsing operation type for variable {:?}: {}", full_name, e)
}
}
diff --git a/rust/examples/pdb-ng/src/parser.rs b/rust/examples/pdb-ng/src/parser.rs
index 11bad268..1fc5bc08 100644
--- a/rust/examples/pdb-ng/src/parser.rs
+++ b/rust/examples/pdb-ng/src/parser.rs
@@ -248,6 +248,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
address,
name,
type_,
+ locals,
..
}) => {
self.log(|| {
@@ -271,6 +272,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
Some(address),
Some(self.platform.clone()),
vec![], // TODO : Components
+ vec![], //TODO: local variables
));
}
_ => {}
diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs
index f29f8ac2..68c666e8 100644
--- a/rust/src/debuginfo.rs
+++ b/rust/src/debuginfo.rs
@@ -75,7 +75,7 @@ use crate::{
platform::Platform,
rc::*,
string::{raw_to_string, BnStrCompatible, BnString},
- types::{DataVariableAndName, NameAndType, Type},
+ types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type},
};
use std::{hash::Hash, os::raw::c_void, ptr, slice};
@@ -301,6 +301,7 @@ pub struct DebugFunctionInfo {
address: u64,
platform: Option<Ref<Platform>>,
components: Vec<String>,
+ local_variables: Vec<NamedTypedVariable>,
}
impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
@@ -310,6 +311,15 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
.map(|component| raw_to_string(*component as *const _).unwrap())
.collect();
+ let local_variables: Vec<NamedTypedVariable> = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) }
+ .iter()
+ .map(|local_variable| {
+ unsafe {
+ NamedTypedVariable::from_raw(local_variable)
+ }
+ })
+ .collect();
+
Self {
short_name: raw_to_string(raw.shortName),
full_name: raw_to_string(raw.fullName),
@@ -326,6 +336,7 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
Some(unsafe { Platform::ref_from_raw(raw.platform) })
},
components,
+ local_variables,
}
}
}
@@ -339,6 +350,7 @@ impl DebugFunctionInfo {
address: Option<u64>,
platform: Option<Ref<Platform>>,
components: Vec<String>,
+ local_variables: Vec<NamedTypedVariable>,
) -> Self {
Self {
short_name,
@@ -351,6 +363,7 @@ impl DebugFunctionInfo {
},
platform,
components,
+ local_variables,
}
}
}
@@ -776,14 +789,31 @@ impl DebugInfo {
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
- let mut components_array: Vec<*const ::std::os::raw::c_char> =
+ let mut components_array: Vec<*mut ::std::os::raw::c_char> =
Vec::with_capacity(new_func.components.len());
- for component in &new_func.components {
- components_array.push(component.as_ptr() as _);
- }
+
+
+ let mut local_variables_array: Vec<BNVariableNameAndType> =
+ Vec::with_capacity(new_func.local_variables.len());
unsafe {
- BNAddDebugFunction(
+ for component in &new_func.components {
+ components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _));
+ }
+
+ for local_variable in &new_func.local_variables {
+ local_variables_array.push(
+ BNVariableNameAndType {
+ var: local_variable.var.raw(),
+ autoDefined: local_variable.auto_defined,
+ typeConfidence: local_variable.ty.confidence,
+ name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _),
+ type_: local_variable.ty.contents.handle,
+ }
+ );
+ }
+
+ let result = BNAddDebugFunction(
self.handle,
&mut BNDebugFunctionInfo {
shortName: short_name,
@@ -800,8 +830,19 @@ impl DebugInfo {
},
components: components_array.as_ptr() as _,
componentN: new_func.components.len(),
+ localVariables: local_variables_array.as_ptr() as _,
+ localVariableN: local_variables_array.len(),
},
- )
+ );
+
+ for i in components_array {
+ BNFreeString(i);
+ }
+
+ for i in &local_variables_array {
+ BNFreeString(i.name);
+ }
+ result
}
}
diff --git a/rust/src/types.rs b/rust/src/types.rs
index b42c7039..9b2271d5 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -181,6 +181,20 @@ impl<T: Display> Display for Conf<T> {
}
}
+impl<T: PartialEq> PartialEq for Conf<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.contents.eq(&other.contents)
+ }
+}
+
+impl<T: Eq> Eq for Conf<T> {}
+
+impl<T: Hash> Hash for Conf<T> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.contents.hash(state);
+ }
+}
+
impl<'a, T> From<&'a Conf<T>> for Conf<&'a T> {
fn from(c: &'a Conf<T>) -> Self {
Conf::new(&c.contents, c.confidence)
@@ -1500,21 +1514,40 @@ unsafe impl CoreArrayProviderInner for Array<SSAVariable> {
///////////////
// NamedVariable
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct NamedTypedVariable {
- var: BNVariable,
- auto_defined: bool,
- type_confidence: u8,
- name: *mut c_char,
- ty: *mut BNType,
+ pub name: String,
+ pub ty: Conf<Ref<Type>>,
+ pub var: Variable,
+ pub auto_defined: bool,
}
impl NamedTypedVariable {
+
+ pub fn new(var: Variable, name: String, ty: Conf<Ref<Type>>, auto_defined: bool) -> Self {
+ Self {
+ name,
+ ty,
+ var,
+ auto_defined,
+ }
+ }
+
+ pub(crate) unsafe fn from_raw(var: &BNVariableNameAndType) -> Self {
+ Self {
+ var: Variable::from_raw(var.var),
+ auto_defined: var.autoDefined,
+ name: CStr::from_ptr(var.name).to_str().unwrap().to_string(),
+ ty: Conf::new(Type::ref_from_raw(var.type_), var.typeConfidence)
+ }
+ }
+
pub fn name(&self) -> &str {
- unsafe { CStr::from_ptr(self.name).to_str().unwrap() }
+ &self.name
}
pub fn var(&self) -> Variable {
- unsafe { Variable::from_raw(self.var) }
+ self.var
}
pub fn auto_defined(&self) -> bool {
@@ -1522,18 +1555,18 @@ impl NamedTypedVariable {
}
pub fn type_confidence(&self) -> u8 {
- self.type_confidence
+ self.ty.confidence
}
pub fn var_type(&self) -> Ref<Type> {
- unsafe { Ref::new(Type::from_raw(self.ty)) }
+ self.ty.contents.clone()
}
}
impl CoreArrayProvider for NamedTypedVariable {
type Raw = BNVariableNameAndType;
type Context = ();
- type Wrapped<'a> = ManuallyDrop<NamedTypedVariable>;
+ type Wrapped<'a> = Guard<'a, NamedTypedVariable>;
}
unsafe impl CoreArrayProviderInner for NamedTypedVariable {
@@ -1541,13 +1574,7 @@ unsafe impl CoreArrayProviderInner for NamedTypedVariable {
BNFreeVariableNameAndTypeList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- ManuallyDrop::new(NamedTypedVariable {
- var: raw.var,
- ty: raw.type_,
- name: raw.name,
- auto_defined: raw.autoDefined,
- type_confidence: raw.typeConfidence,
- })
+ unsafe { Guard::new(NamedTypedVariable::from_raw(raw), raw) }
}
}