binaryninja/
debuginfo.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// TODO : These docs are here, but could afford to be cleaned up
16
17//! Parsers and providers of debug information to Binary Ninja.
18//!
19//! The debug information is used by Binary Ninja as ground-truth information about the attributes of functions,
20//! types, and variables that Binary Ninja's analysis pipeline would otherwise work to deduce. By providing
21//! debug info, Binary Ninja's output can be generated quicker, more accurately, and more completely.
22//!
23//! A DebugInfoParser consists of:
24//!     1. A name
25//!     2. An `is_valid` function which takes a BV and returns a bool
26//!     3. A `parse` function which takes a `DebugInfo` object and uses the member functions `add_type`, `add_function`, and `add_data_variable` to populate all the info it can.
27//! And finally calling `binaryninja::debuginfo::DebugInfoParser::register` to register it with the core.
28//!
29//! Here's a minimal, complete example boilerplate-plugin:
30//! ```no_run
31//! use binaryninja::{
32//!     binary_view::BinaryView,
33//!     debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
34//! };
35//!
36//! struct ExampleDebugInfoParser;
37//!
38//! impl CustomDebugInfoParser for ExampleDebugInfoParser {
39//!     fn is_valid(&self, _view: &BinaryView) -> bool {
40//!         true
41//!     }
42//!
43//!     fn parse_info(
44//!         &self,
45//!         _debug_info: &mut DebugInfo,
46//!         _view: &BinaryView,
47//!         _debug_file: &BinaryView,
48//!         _progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
49//!     ) -> bool {
50//!         println!("Parsing info");
51//!         true
52//!     }
53//! }
54//!
55//! #[no_mangle]
56//! pub extern "C" fn CorePluginInit() -> bool {
57//!     DebugInfoParser::register("example debug info parser", ExampleDebugInfoParser {});
58//!     true
59//! }
60//! ```
61//!
62//! `DebugInfo` will then be automatically applied to binary views that contain debug information (via the setting `analysis.debugInfo.internal`), binary views that provide valid external debug info files (`analysis.debugInfo.external`), or manually fetched/applied as below:
63//! ```no_run
64//! # use binaryninja::debuginfo::DebugInfoParser;
65//! # use binaryninja::binary_view::BinaryViewExt;
66//! let bv = binaryninja::load("example").unwrap();
67//! let valid_parsers = DebugInfoParser::parsers_for_view(&bv);
68//! let parser = valid_parsers.get(0);
69//! let debug_info = parser.parse_debug_info(&bv, &bv, None).unwrap();
70//! bv.apply_debug_info(&debug_info);
71//! ```
72//!
73//! Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling `parse_debug_info` with the
74//! `DebugInfo` object just returned. This is automatic when opening a binary view with multiple valid debug info parsers. If you
75//! wish to set the debug info for a binary view without applying it as well, you can call `binaryninja::binaryview::BinaryView::set_debug_info`.
76
77use binaryninjacore_sys::*;
78use std::ffi::c_void;
79
80use crate::progress::{NoProgressCallback, ProgressCallback};
81use crate::string::strings_to_string_list;
82use crate::variable::{NamedDataVariableWithType, NamedVariableWithType};
83use crate::{
84    binary_view::BinaryView,
85    platform::Platform,
86    rc::*,
87    string::{raw_to_string, BnString, IntoCStr},
88    types::{NameAndType, Type},
89};
90
91/// Implement this trait to implement a debug info parser.  See `DebugInfoParser` for more details.
92pub trait CustomDebugInfoParser: 'static + Sync {
93    fn is_valid(&self, view: &BinaryView) -> bool;
94
95    fn parse_info(
96        &self,
97        debug_info: &mut DebugInfo,
98        view: &BinaryView,
99        debug_file: &BinaryView,
100        progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
101    ) -> bool;
102}
103
104/// Represents the registered parsers and providers of debug information to Binary Ninja.
105/// See `binaryninja::debuginfo` for more information
106#[derive(PartialEq, Eq, Hash)]
107pub struct DebugInfoParser {
108    pub(crate) handle: *mut BNDebugInfoParser,
109}
110
111impl DebugInfoParser {
112    pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfoParser) -> Ref<Self> {
113        debug_assert!(!handle.is_null());
114
115        Ref::new(Self { handle })
116    }
117
118    /// Returns debug info parser of the given name, if it exists
119    pub fn from_name(name: &str) -> Result<Ref<Self>, ()> {
120        let name = name.to_cstr();
121        let parser = unsafe { BNGetDebugInfoParserByName(name.as_ptr()) };
122
123        if parser.is_null() {
124            Err(())
125        } else {
126            unsafe { Ok(Self::from_raw(parser)) }
127        }
128    }
129
130    /// List all debug-info parsers
131    pub fn list() -> Array<DebugInfoParser> {
132        let mut count = 0;
133        let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
134        unsafe { Array::new(raw_parsers, count, ()) }
135    }
136
137    /// Returns a list of debug-info parsers that are valid for the provided binary view
138    pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
139        let mut count = 0;
140        let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
141        unsafe { Array::new(raw_parsers, count, ()) }
142    }
143
144    /// Returns the name of the current parser
145    pub fn name(&self) -> String {
146        unsafe { BnString::into_string(BNGetDebugInfoParserName(self.handle)) }
147    }
148
149    /// Returns whether this debug-info parser is valid for the provided binary view
150    pub fn is_valid_for_view(&self, view: &BinaryView) -> bool {
151        unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
152    }
153
154    /// Returns [`DebugInfo`] populated with debug info by this debug-info parser.
155    ///
156    /// Only provide a `DebugInfo` object if you wish to append to the existing debug info
157    pub fn parse_debug_info(
158        &self,
159        view: &BinaryView,
160        debug_file: &BinaryView,
161        existing_debug_info: Option<&DebugInfo>,
162    ) -> Option<Ref<DebugInfo>> {
163        self.parse_debug_info_with_progress(
164            view,
165            debug_file,
166            existing_debug_info,
167            NoProgressCallback,
168        )
169    }
170
171    /// Returns [`DebugInfo`] populated with debug info by this debug-info parser.
172    ///
173    /// Only provide a `DebugInfo` object if you wish to append to the existing debug info
174    pub fn parse_debug_info_with_progress<P: ProgressCallback>(
175        &self,
176        view: &BinaryView,
177        debug_file: &BinaryView,
178        existing_debug_info: Option<&DebugInfo>,
179        mut progress: P,
180    ) -> Option<Ref<DebugInfo>> {
181        let info: *mut BNDebugInfo = match existing_debug_info {
182            Some(debug_info) => unsafe {
183                BNParseDebugInfo(
184                    self.handle,
185                    view.handle,
186                    debug_file.handle,
187                    debug_info.handle,
188                    Some(P::cb_progress_callback),
189                    &mut progress as *mut P as *mut c_void,
190                )
191            },
192            None => unsafe {
193                BNParseDebugInfo(
194                    self.handle,
195                    view.handle,
196                    debug_file.handle,
197                    std::ptr::null_mut(),
198                    Some(P::cb_progress_callback),
199                    &mut progress as *mut P as *mut c_void,
200                )
201            },
202        };
203
204        if info.is_null() {
205            return None;
206        }
207        Some(unsafe { DebugInfo::ref_from_raw(info) })
208    }
209
210    // Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details.
211    pub fn register<C>(name: &str, parser_callbacks: C) -> Ref<Self>
212    where
213        C: CustomDebugInfoParser,
214    {
215        extern "C" fn cb_is_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
216        where
217            C: CustomDebugInfoParser,
218        {
219            let cmd = unsafe { &*(ctxt as *const C) };
220            let view = unsafe { BinaryView::ref_from_raw(view) };
221            let _span = ffi_span!("CustomDebugInfoParser::is_valid", view);
222            cmd.is_valid(&view)
223        }
224
225        extern "C" fn cb_parse_info<C>(
226            ctxt: *mut c_void,
227            debug_info: *mut BNDebugInfo,
228            view: *mut BNBinaryView,
229            debug_file: *mut BNBinaryView,
230            progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
231            progress_ctxt: *mut c_void,
232        ) -> bool
233        where
234            C: CustomDebugInfoParser,
235        {
236            let cmd = unsafe { &*(ctxt as *const C) };
237            let view = unsafe { BinaryView::ref_from_raw(view) };
238            let debug_file = unsafe { BinaryView::ref_from_raw(debug_file) };
239            let mut debug_info = unsafe { DebugInfo::ref_from_raw(debug_info) };
240
241            let _span = ffi_span!("CustomDebugInfoParser::parse_info", view);
242            cmd.parse_info(
243                &mut debug_info,
244                &view,
245                &debug_file,
246                Box::new(move |cur: usize, max: usize| match progress {
247                    Some(func) => unsafe {
248                        if func(progress_ctxt, cur, max) {
249                            Ok(())
250                        } else {
251                            Err(())
252                        }
253                    },
254                    _ => Ok(()),
255                }),
256            )
257        }
258
259        let name = name.to_cstr();
260        let name_ptr = name.as_ptr();
261        let ctxt = Box::into_raw(Box::new(parser_callbacks));
262
263        unsafe {
264            DebugInfoParser::from_raw(BNRegisterDebugInfoParser(
265                name_ptr,
266                Some(cb_is_valid::<C>),
267                Some(cb_parse_info::<C>),
268                ctxt as *mut _,
269            ))
270        }
271    }
272}
273
274unsafe impl RefCountable for DebugInfoParser {
275    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
276        Ref::new(Self {
277            handle: BNNewDebugInfoParserReference(handle.handle),
278        })
279    }
280
281    unsafe fn dec_ref(handle: &Self) {
282        BNFreeDebugInfoParserReference(handle.handle);
283    }
284}
285
286impl ToOwned for DebugInfoParser {
287    type Owned = Ref<Self>;
288
289    fn to_owned(&self) -> Self::Owned {
290        unsafe { RefCountable::inc_ref(self) }
291    }
292}
293
294impl CoreArrayProvider for DebugInfoParser {
295    type Raw = *mut BNDebugInfoParser;
296    type Context = ();
297    type Wrapped<'a> = Guard<'a, DebugInfoParser>;
298}
299
300unsafe impl CoreArrayProviderInner for DebugInfoParser {
301    unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
302        BNFreeDebugInfoParserList(raw, count);
303    }
304
305    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
306        Guard::new(Self { handle: *raw }, context)
307    }
308}
309
310///////////////////////
311// DebugFunctionInfo
312
313/// Collates ground-truth function-external attributes for use in BinaryNinja's internal analysis.
314///
315/// When contributing function info, provide only what you know - BinaryNinja will figure out everything else that it can, as it usually does.
316///
317/// Functions will not be created if an address is not provided, but will be able to be queried from debug info for later user analysis.
318pub struct DebugFunctionInfo {
319    // TODO: These need to be BnString if we want to support invalid UTF-8
320    short_name: Option<String>,
321    full_name: Option<String>,
322    raw_name: Option<String>,
323    type_: Option<Ref<Type>>,
324    address: u64,
325    platform: Option<Ref<Platform>>,
326    components: Vec<String>,
327    local_variables: Vec<NamedVariableWithType>,
328}
329
330impl DebugFunctionInfo {
331    pub(crate) fn from_raw(value: &BNDebugFunctionInfo) -> Self {
332        let raw_components =
333            unsafe { std::slice::from_raw_parts(value.components, value.componentN) };
334        let components = raw_components
335            .iter()
336            .filter_map(|&c| raw_to_string(c))
337            .collect();
338        let raw_local_variables =
339            unsafe { std::slice::from_raw_parts(value.localVariables, value.localVariableN) };
340        let local_variables = raw_local_variables
341            .iter()
342            .map(NamedVariableWithType::from_raw)
343            .collect();
344        Self {
345            short_name: raw_to_string(value.shortName),
346            full_name: raw_to_string(value.fullName),
347            raw_name: raw_to_string(value.rawName),
348            type_: if value.type_.is_null() {
349                None
350            } else {
351                Some(unsafe { Type::from_raw(value.type_) }.to_owned())
352            },
353            address: value.address,
354            platform: if value.platform.is_null() {
355                None
356            } else {
357                Some(unsafe { Platform::from_raw(value.platform) }.to_owned())
358            },
359            components,
360            local_variables,
361        }
362    }
363}
364
365impl DebugFunctionInfo {
366    #[allow(clippy::too_many_arguments)]
367    pub fn new(
368        short_name: Option<String>,
369        full_name: Option<String>,
370        raw_name: Option<String>,
371        type_: Option<Ref<Type>>,
372        address: Option<u64>,
373        platform: Option<Ref<Platform>>,
374        components: Vec<String>,
375        local_variables: Vec<NamedVariableWithType>,
376    ) -> Self {
377        Self {
378            short_name,
379            full_name,
380            raw_name,
381            type_,
382            address: address.unwrap_or(0),
383            platform,
384            components,
385            local_variables,
386        }
387    }
388}
389
390///////////////
391// DebugInfo
392
393/// Provides an interface to both provide and query debug info. The DebugInfo object is used
394/// internally by the binary view to which it is applied to determine the attributes of functions, types, and variables
395/// that would otherwise be costly to deduce.
396///
397/// DebugInfo objects themselves are independent of binary views; their data can be sourced from any arbitrary binary
398/// views and be applied to any other arbitrary binary view. A DebugInfo object can also contain debug info from multiple
399/// DebugInfoParsers. This makes it possible to gather debug info that may be distributed across several different
400/// formats and files.
401///
402/// DebugInfo cannot be instantiated by the user, instead get it from either the binary view (see `binaryninja::binaryview::BinaryView::debug_info`)
403/// or a debug-info parser (see `binaryninja::debuginfo::DebugInfoParser::parse_debug_info`).
404///
405/// Please note that calling one of `add_*` functions will not work outside of a debuginfo plugin.
406#[derive(PartialEq, Eq, Hash)]
407pub struct DebugInfo {
408    pub(crate) handle: *mut BNDebugInfo,
409}
410
411impl DebugInfo {
412    pub(crate) unsafe fn ref_from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
413        debug_assert!(!handle.is_null());
414        Ref::new(Self { handle })
415    }
416
417    /// Returns all types within the parser
418    pub fn types_by_name(&self, parser_name: &str) -> Array<NameAndType> {
419        let parser_name = parser_name.to_cstr();
420        let mut count: usize = 0;
421        let debug_types_ptr =
422            unsafe { BNGetDebugTypes(self.handle, parser_name.as_ptr(), &mut count) };
423        unsafe { Array::new(debug_types_ptr, count, ()) }
424    }
425
426    pub fn types(&self) -> Array<NameAndType> {
427        let mut count: usize = 0;
428        let debug_types_ptr =
429            unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) };
430        unsafe { Array::new(debug_types_ptr, count, ()) }
431    }
432
433    /// Returns all functions within the parser
434    pub fn functions_by_name(&self, parser_name: &str) -> Vec<DebugFunctionInfo> {
435        let parser_name = parser_name.to_cstr();
436
437        let mut count: usize = 0;
438        let functions_ptr =
439            unsafe { BNGetDebugFunctions(self.handle, parser_name.as_ptr(), &mut count) };
440
441        let result: Vec<DebugFunctionInfo> = unsafe {
442            std::slice::from_raw_parts_mut(functions_ptr, count)
443                .iter()
444                .map(DebugFunctionInfo::from_raw)
445                .collect()
446        };
447
448        unsafe { BNFreeDebugFunctions(functions_ptr, count) };
449        result
450    }
451
452    pub fn functions(&self) -> Vec<DebugFunctionInfo> {
453        let mut count: usize = 0;
454        let functions_ptr =
455            unsafe { BNGetDebugFunctions(self.handle, std::ptr::null_mut(), &mut count) };
456
457        let result: Vec<DebugFunctionInfo> = unsafe {
458            std::slice::from_raw_parts_mut(functions_ptr, count)
459                .iter()
460                .map(DebugFunctionInfo::from_raw)
461                .collect()
462        };
463
464        unsafe { BNFreeDebugFunctions(functions_ptr, count) };
465        result
466    }
467
468    /// Returns all data variables within the parser
469    pub fn data_variables_by_name(&self, parser_name: &str) -> Array<NamedDataVariableWithType> {
470        let parser_name = parser_name.to_cstr();
471        let mut count: usize = 0;
472        let data_variables_ptr =
473            unsafe { BNGetDebugDataVariables(self.handle, parser_name.as_ptr(), &mut count) };
474        unsafe { Array::new(data_variables_ptr, count, ()) }
475    }
476
477    pub fn data_variables(&self) -> Array<NamedDataVariableWithType> {
478        let mut count: usize = 0;
479        let data_variables_ptr =
480            unsafe { BNGetDebugDataVariables(self.handle, std::ptr::null_mut(), &mut count) };
481        unsafe { Array::new(data_variables_ptr, count, ()) }
482    }
483
484    pub fn type_by_name(&self, parser_name: &str, name: &str) -> Option<Ref<Type>> {
485        let parser_name = parser_name.to_cstr();
486        let name = name.to_cstr();
487
488        let result =
489            unsafe { BNGetDebugTypeByName(self.handle, parser_name.as_ptr(), name.as_ptr()) };
490        if !result.is_null() {
491            Some(unsafe { Type::ref_from_raw(result) })
492        } else {
493            None
494        }
495    }
496
497    pub fn get_data_variable_by_name(
498        &self,
499        parser_name: &str,
500        name: &str,
501    ) -> Option<NamedDataVariableWithType> {
502        let parser_name = parser_name.to_cstr();
503        let name = name.to_cstr();
504        let mut dv = BNDataVariableAndName::default();
505        unsafe {
506            if BNGetDebugDataVariableByName(
507                self.handle,
508                parser_name.as_ptr(),
509                name.as_ptr(),
510                &mut dv,
511            ) {
512                Some(NamedDataVariableWithType::from_owned_raw(dv))
513            } else {
514                None
515            }
516        }
517    }
518
519    pub fn get_data_variable_by_address(
520        &self,
521        parser_name: &str,
522        address: u64,
523    ) -> Option<NamedDataVariableWithType> {
524        let parser_name = parser_name.to_cstr();
525        let mut dv = BNDataVariableAndName::default();
526        unsafe {
527            if BNGetDebugDataVariableByAddress(self.handle, parser_name.as_ptr(), address, &mut dv)
528            {
529                Some(NamedDataVariableWithType::from_owned_raw(dv))
530            } else {
531                None
532            }
533        }
534    }
535
536    /// Returns a list of [`NameAndType`] where the `name` is the parser the type originates from.
537    pub fn get_types_by_name(&self, name: &str) -> Array<NameAndType> {
538        let name = name.to_cstr();
539        let mut count: usize = 0;
540        let raw_names_and_types_ptr =
541            unsafe { BNGetDebugTypesByName(self.handle, name.as_ptr(), &mut count) };
542        unsafe { Array::new(raw_names_and_types_ptr, count, ()) }
543    }
544
545    // The tuple is (DebugInfoParserName, address, type)
546    pub fn get_data_variables_by_name(&self, name: &str) -> Array<NamedDataVariableWithType> {
547        let name = name.to_cstr();
548        let mut count: usize = 0;
549        let raw_variables_and_names =
550            unsafe { BNGetDebugDataVariablesByName(self.handle, name.as_ptr(), &mut count) };
551        unsafe { Array::new(raw_variables_and_names, count, ()) }
552    }
553
554    /// The tuple is (DebugInfoParserName, TypeName, type)
555    pub fn get_data_variables_by_address(&self, address: u64) -> Vec<(String, String, Ref<Type>)> {
556        let mut count: usize = 0;
557        let raw_variables_and_names =
558            unsafe { BNGetDebugDataVariablesByAddress(self.handle, address, &mut count) };
559
560        let variables_and_names: &[*mut BNDataVariableAndNameAndDebugParser] =
561            unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
562
563        let result = variables_and_names
564            .iter()
565            .take(count)
566            .map(|&variable_and_name| unsafe {
567                (
568                    raw_to_string((*variable_and_name).parser).unwrap(),
569                    raw_to_string((*variable_and_name).name).unwrap(),
570                    Type::from_raw((*variable_and_name).type_).to_owned(),
571                )
572            })
573            .collect();
574
575        unsafe { BNFreeDataVariableAndNameAndDebugParserList(raw_variables_and_names, count) };
576        result
577    }
578
579    pub fn remove_parser_info(&self, parser_name: &str) -> bool {
580        let parser_name = parser_name.to_cstr();
581
582        unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ptr()) }
583    }
584
585    pub fn remove_parser_types(&self, parser_name: &str) -> bool {
586        let parser_name = parser_name.to_cstr();
587
588        unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ptr()) }
589    }
590
591    pub fn remove_parser_functions(&self, parser_name: &str) -> bool {
592        let parser_name = parser_name.to_cstr();
593
594        unsafe { BNRemoveDebugParserFunctions(self.handle, parser_name.as_ptr()) }
595    }
596
597    pub fn remove_parser_data_variables(&self, parser_name: &str) -> bool {
598        let parser_name = parser_name.to_cstr();
599
600        unsafe { BNRemoveDebugParserDataVariables(self.handle, parser_name.as_ptr()) }
601    }
602
603    pub fn remove_type_by_name(&self, parser_name: &str, name: &str) -> bool {
604        let parser_name = parser_name.to_cstr();
605        let name = name.to_cstr();
606
607        unsafe { BNRemoveDebugTypeByName(self.handle, parser_name.as_ptr(), name.as_ptr()) }
608    }
609
610    pub fn remove_function_by_index(&self, parser_name: &str, index: usize) -> bool {
611        let parser_name = parser_name.to_cstr();
612
613        unsafe { BNRemoveDebugFunctionByIndex(self.handle, parser_name.as_ptr(), index) }
614    }
615
616    pub fn remove_data_variable_by_address(&self, parser_name: &str, address: u64) -> bool {
617        let parser_name = parser_name.to_cstr();
618
619        unsafe { BNRemoveDebugDataVariableByAddress(self.handle, parser_name.as_ptr(), address) }
620    }
621
622    /// Adds a type scoped under the current parser's name to the debug info
623    pub fn add_type(&self, name: &str, new_type: &Type, components: &[&str]) -> bool {
624        // NOTE: Must be freed after call to BNAddDebugType is over.
625        let raw_components_ptr = strings_to_string_list(components);
626        let name = name.to_cstr();
627        let result = unsafe {
628            BNAddDebugType(
629                self.handle,
630                name.as_ptr(),
631                new_type.handle,
632                raw_components_ptr as *mut _,
633                components.len(),
634            )
635        };
636        unsafe { BNFreeStringList(raw_components_ptr, components.len()) };
637        result
638    }
639
640    /// Adds a function scoped under the current parser's name to the debug info
641    pub fn add_function(&self, new_func: &DebugFunctionInfo) -> bool {
642        let short_name_bytes = new_func.short_name.as_ref().map(|name| name.to_cstr());
643        let short_name = short_name_bytes
644            .as_ref()
645            .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
646        let full_name_bytes = new_func.full_name.as_ref().map(|name| name.to_cstr());
647        let full_name = full_name_bytes
648            .as_ref()
649            .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
650        let raw_name_bytes = new_func.raw_name.as_ref().map(|name| name.to_cstr());
651        let raw_name = raw_name_bytes
652            .as_ref()
653            .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
654
655        let mut components_array: Vec<*mut ::std::os::raw::c_char> =
656            Vec::with_capacity(new_func.components.len());
657
658        let mut local_variables_array: Vec<BNVariableNameAndType> =
659            Vec::with_capacity(new_func.local_variables.len());
660
661        unsafe {
662            for component in &new_func.components {
663                let component = component.to_cstr();
664                components_array.push(BNAllocString(component.as_ptr()));
665            }
666
667            for local_variable in &new_func.local_variables {
668                // NOTE: must be manually freed after call to BNAddDebugFunction is over.
669                local_variables_array.push(NamedVariableWithType::into_raw(local_variable.clone()));
670            }
671
672            let result = BNAddDebugFunction(
673                self.handle,
674                &mut BNDebugFunctionInfo {
675                    shortName: short_name,
676                    fullName: full_name,
677                    rawName: raw_name,
678                    address: new_func.address,
679                    type_: match &new_func.type_ {
680                        Some(type_) => type_.handle,
681                        _ => std::ptr::null_mut(),
682                    },
683                    platform: match &new_func.platform {
684                        Some(platform) => platform.handle,
685                        _ => std::ptr::null_mut(),
686                    },
687                    components: components_array.as_ptr() as _,
688                    componentN: new_func.components.len(),
689                    localVariables: local_variables_array.as_ptr() as _,
690                    localVariableN: local_variables_array.len(),
691                },
692            );
693
694            for i in components_array {
695                BnString::free_raw(i);
696            }
697
698            for i in &local_variables_array {
699                NamedVariableWithType::free_raw(*i);
700            }
701            result
702        }
703    }
704
705    /// Adds a data variable scoped under the current parser's name to the debug info
706    pub fn add_data_variable(
707        &self,
708        address: u64,
709        t: &Type,
710        name: Option<&str>,
711        components: &[&str],
712    ) -> bool {
713        let mut components_array: Vec<*const ::std::os::raw::c_char> =
714            Vec::with_capacity(components.len());
715        for component in components {
716            components_array.push(component.as_ptr() as _);
717        }
718
719        match name {
720            Some(name) => {
721                let name = name.to_cstr();
722                unsafe {
723                    BNAddDebugDataVariable(
724                        self.handle,
725                        address,
726                        t.handle,
727                        name.as_ptr(),
728                        components.as_ptr() as _,
729                        components.len(),
730                    )
731                }
732            }
733            None => unsafe {
734                BNAddDebugDataVariable(
735                    self.handle,
736                    address,
737                    t.handle,
738                    std::ptr::null_mut(),
739                    components.as_ptr() as _,
740                    components.len(),
741                )
742            },
743        }
744    }
745
746    pub fn add_data_variable_info(&self, var: NamedDataVariableWithType) -> bool {
747        let raw_data_var = NamedDataVariableWithType::into_raw(var);
748        let success = unsafe { BNAddDebugDataVariableInfo(self.handle, &raw_data_var) };
749        NamedDataVariableWithType::free_raw(raw_data_var);
750        success
751    }
752}
753
754unsafe impl RefCountable for DebugInfo {
755    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
756        Ref::new(Self {
757            handle: BNNewDebugInfoReference(handle.handle),
758        })
759    }
760
761    unsafe fn dec_ref(handle: &Self) {
762        BNFreeDebugInfoReference(handle.handle);
763    }
764}
765
766impl ToOwned for DebugInfo {
767    type Owned = Ref<Self>;
768
769    fn to_owned(&self) -> Self::Owned {
770        unsafe { RefCountable::inc_ref(self) }
771    }
772}