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