diff options
| author | Josh Ferrell <josh@vector35.com> | 2024-06-13 14:13:11 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2024-06-13 14:13:11 -0400 |
| commit | d75066545afdc9b5d0a52bd2518e43f683c671a5 (patch) | |
| tree | bed26d348a726fc02c1d44cbdc36a8056b44baaf | |
| parent | f3a440248cda4d0c850cd32e3aa127ab29e0bb3e (diff) | |
Apply stack variables from DWARF
| -rw-r--r-- | binaryninjaapi.h | 7 | ||||
| -rw-r--r-- | binaryninjacore.h | 2 | ||||
| -rw-r--r-- | debuginfo.cpp | 33 | ||||
| -rw-r--r-- | rust/Cargo.lock | 7 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/Cargo.toml | 1 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs | 88 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/functions.rs | 4 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/helpers.rs | 2 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/lib.rs | 104 | ||||
| -rw-r--r-- | rust/examples/dwarf/dwarf_import/src/types.rs | 37 | ||||
| -rw-r--r-- | rust/examples/pdb-ng/src/parser.rs | 2 | ||||
| -rw-r--r-- | rust/src/debuginfo.rs | 55 | ||||
| -rw-r--r-- | rust/src/types.rs | 61 |
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, ¶meters, variable_arguments); + debug_info_builder.insert_function(full_name, raw_name, return_type, address, ¶meters, 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) } } } |
