summaryrefslogtreecommitdiff
path: root/plugins/dwarf/dwarf_import/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/dwarf/dwarf_import/src/lib.rs')
-rw-r--r--plugins/dwarf/dwarf_import/src/lib.rs757
1 files changed, 757 insertions, 0 deletions
diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs
new file mode 100644
index 00000000..f850d3ad
--- /dev/null
+++ b/plugins/dwarf/dwarf_import/src/lib.rs
@@ -0,0 +1,757 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod die_handlers;
+mod dwarfdebuginfo;
+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_variable;
+
+use binaryninja::binary_view::BinaryViewBase;
+use binaryninja::{
+ binary_view::{BinaryView, BinaryViewExt},
+ debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
+ settings::Settings,
+ template_simplifier::simplify_str_to_str,
+};
+use dwarfreader::{
+ create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
+};
+
+use functions::parse_lexical_block;
+use gimli::{
+ constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section,
+ SectionId, Unit, UnwindContext, UnwindSection,
+};
+
+use binaryninja::logger::Logger;
+use helpers::{get_build_id, load_debug_info_for_build_id};
+use log::{debug, error, warn};
+
+trait ReaderType: Reader<Offset = usize> {}
+impl<T: Reader<Offset = usize>> ReaderType for T {}
+
+pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>(
+ original_fn: F,
+ subpart: usize,
+ subpart_weights: &[f64],
+) -> Box<dyn Fn(usize, usize) -> Result<(), ()> + 'b> {
+ // Normalize weights
+ let weight_sum: f64 = subpart_weights.iter().sum();
+ if weight_sum < 0.0001 {
+ return Box::new(|_, _| Ok(()));
+ }
+
+ // Keep a running count of weights for the start
+ let mut subpart_starts = vec![];
+ let mut start = 0f64;
+ for w in subpart_weights {
+ subpart_starts.push(start);
+ start += *w;
+ }
+
+ let subpart_start = subpart_starts[subpart] / weight_sum;
+ let weight = subpart_weights[subpart] / weight_sum;
+
+ Box::new(move |cur: usize, max: usize| {
+ // Just use a large number for easy divisibility
+ let steps = 1000000f64;
+ let subpart_size = steps * weight;
+ let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size;
+
+ original_fn(
+ (subpart_start * steps + subpart_progress) as usize,
+ steps as usize,
+ )
+ })
+}
+
+fn calculate_total_unit_bytes<R: ReaderType>(
+ dwarf: &Dwarf<R>,
+ debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
+) {
+ let mut iter = dwarf.units();
+ let mut total_size: usize = 0;
+ while let Ok(Some(header)) = iter.next() {
+ total_size += header.length_including_self();
+ }
+ debug_info_builder_context.total_unit_size_bytes = total_size;
+}
+
+fn recover_names<R: ReaderType>(
+ dwarf: &Dwarf<R>,
+ debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
+ progress: &dyn Fn(usize, usize) -> Result<(), ()>,
+) -> bool {
+ let mut res = true;
+ if let Some(sup_dwarf) = dwarf.sup() {
+ res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress);
+ }
+
+ if res {
+ res = recover_names_internal(dwarf, debug_info_builder_context, progress);
+ }
+ res
+}
+
+fn recover_names_internal<R: ReaderType>(
+ dwarf: &Dwarf<R>,
+ debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
+ progress: &dyn Fn(usize, usize) -> Result<(), ()>,
+) -> bool {
+ let mut iter = dwarf.units();
+ let mut current_byte_offset: usize = 0;
+ while let Ok(Some(header)) = iter.next() {
+ let unit_offset = header.offset().as_debug_info_offset().unwrap().0;
+ let unit = dwarf.unit(header).unwrap();
+ let mut namespace_qualifiers: Vec<(isize, String)> = vec![];
+ let mut entries = unit.entries();
+ let mut depth = 0;
+
+ // The first entry in the unit is the header for the unit
+ if let Ok(Some((delta_depth, _))) = entries.next_dfs() {
+ depth += delta_depth;
+ debug_info_builder_context.total_die_count += 1;
+ }
+
+ while let Ok(Some((delta_depth, entry))) = entries.next_dfs() {
+ debug_info_builder_context.total_die_count += 1;
+
+ if (*progress)(
+ current_byte_offset,
+ debug_info_builder_context.total_unit_size_bytes,
+ )
+ .is_err()
+ {
+ return false; // Parsing canceled
+ };
+ current_byte_offset = unit_offset + entry.offset().0;
+
+ depth += delta_depth;
+ if depth < 0 {
+ error!("DWARF information is seriously malformed. Aborting parsing.");
+ return false;
+ }
+
+ // TODO : Better module/component support
+ namespace_qualifiers.retain(|&(entry_depth, _)| entry_depth < depth);
+
+ match entry.tag() {
+ constants::DW_TAG_namespace => {
+ fn resolve_namespace_name<R: ReaderType>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder_context: &DebugInfoBuilderContext<R>,
+ namespace_qualifiers: &mut Vec<(isize, String)>,
+ depth: isize,
+ ) {
+ if let Some(namespace_qualifier) =
+ get_name(dwarf, unit, entry, debug_info_builder_context)
+ {
+ namespace_qualifiers.push((depth, namespace_qualifier));
+ } else if let Some(die_reference) = get_attr_die(
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder_context,
+ constants::DW_AT_extension,
+ ) {
+ match die_reference {
+ DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
+ resolve_namespace_name(
+ dwarf,
+ entry_unit,
+ &entry_unit.entry(entry_offset).unwrap(),
+ debug_info_builder_context,
+ namespace_qualifiers,
+ depth,
+ )
+ }
+ DieReference::Err => {
+ warn!(
+ "Failed to fetch DIE when resolving namespace. Debug information may be incomplete."
+ );
+ }
+ }
+ } else {
+ namespace_qualifiers.push((depth, "anonymous_namespace".to_string()));
+ }
+ }
+
+ resolve_namespace_name(
+ dwarf,
+ &unit,
+ entry,
+ debug_info_builder_context,
+ &mut namespace_qualifiers,
+ depth,
+ );
+ }
+ constants::DW_TAG_class_type
+ | constants::DW_TAG_structure_type
+ | constants::DW_TAG_union_type => {
+ if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
+ namespace_qualifiers.push((depth, name))
+ } else {
+ namespace_qualifiers.push((
+ depth,
+ match entry.tag() {
+ constants::DW_TAG_class_type => "anonymous_class".to_string(),
+ constants::DW_TAG_structure_type => {
+ "anonymous_structure".to_string()
+ }
+ constants::DW_TAG_union_type => "anonymous_union".to_string(),
+ _ => unreachable!(),
+ },
+ ))
+ }
+ debug_info_builder_context.set_name(
+ get_uid(dwarf, &unit, entry),
+ simplify_str_to_str(
+ namespace_qualifiers
+ .iter()
+ .map(|(_, namespace)| namespace.to_owned())
+ .collect::<Vec<String>>()
+ .join("::"),
+ )
+ .to_string(),
+ );
+ }
+ constants::DW_TAG_typedef
+ | constants::DW_TAG_subprogram
+ | constants::DW_TAG_enumeration_type => {
+ if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
+ debug_info_builder_context.set_name(
+ get_uid(dwarf, &unit, entry),
+ simplify_str_to_str(
+ namespace_qualifiers
+ .iter()
+ .chain(vec![&(-1, name)].into_iter())
+ .map(|(_, namespace)| namespace.to_owned())
+ .collect::<Vec<String>>()
+ .join("::"),
+ )
+ .to_string(),
+ );
+ }
+ }
+ _ => {
+ if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
+ debug_info_builder_context.set_name(get_uid(dwarf, &unit, entry), name);
+ }
+ }
+ }
+ }
+ }
+
+ true
+}
+
+fn parse_unit<R: ReaderType>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ debug_info_builder_context: &DebugInfoBuilderContext<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ progress: &dyn Fn(usize, usize) -> Result<(), ()>,
+ current_die_number: &mut usize,
+) {
+ let mut entries = unit.entries();
+
+ let mut current_depth: isize = 0;
+ let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
+ let mut lexical_blocks_by_depth: Vec<(iset::IntervalSet<u64>, 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((depth_delta, entry))) = entries.next_dfs() {
+ *current_die_number += 1;
+ if (*progress)(
+ *current_die_number,
+ debug_info_builder_context.total_die_count,
+ )
+ .is_err()
+ {
+ 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;
+ }
+
+ if let Some((_lexical_block, depth)) = lexical_blocks_by_depth.last() {
+ if current_depth <= *depth {
+ lexical_blocks_by_depth.pop();
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ match entry.tag() {
+ constants::DW_TAG_subprogram => {
+ let fn_idx = parse_function_entry(
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder_context,
+ debug_info_builder,
+ );
+ functions_by_depth.push((fn_idx, current_depth));
+ }
+ constants::DW_TAG_lexical_block => {
+ if let Some(block_ranges) = parse_lexical_block(dwarf, unit, entry) {
+ lexical_blocks_by_depth.push((block_ranges, current_depth));
+ }
+ }
+ constants::DW_TAG_variable => {
+ let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
+ let current_lexical_block = lexical_blocks_by_depth.last().and_then(|x| Some(&x.0));
+ parse_variable(
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder_context,
+ debug_info_builder,
+ current_fn_idx,
+ current_lexical_block,
+ )
+ }
+ constants::DW_TAG_class_type
+ | constants::DW_TAG_enumeration_type
+ | constants::DW_TAG_structure_type
+ | constants::DW_TAG_union_type
+ | constants::DW_TAG_typedef => {
+ // Ensure types are loaded even if they're unused
+ types::get_type(
+ dwarf,
+ unit,
+ entry,
+ debug_info_builder_context,
+ debug_info_builder,
+ );
+ }
+ _ => (),
+ }
+ }
+}
+
+fn parse_unwind_section<R: Reader, U: UnwindSection<R>>(
+ view: &BinaryView,
+ unwind_section: U,
+) -> gimli::Result<iset::IntervalMap<u64, i64>>
+where
+ <U as UnwindSection<R>>::Offset: std::hash::Hash,
+{
+ let mut bases = gimli::BaseAddresses::default();
+
+ if let Some(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 Some(section) = view
+ .section_by_name(".eh_frame")
+ .or(view.section_by_name("__eh_frame"))
+ {
+ bases = bases.set_eh_frame(section.start());
+ } else if let Some(section) = view
+ .section_by_name(".debug_frame")
+ .or(view.section_by_name("__debug_frame"))
+ {
+ bases = bases.set_eh_frame(section.start());
+ }
+
+ if let Some(section) = view
+ .section_by_name(".text")
+ .or(view.section_by_name("__text"))
+ {
+ bases = bases.set_text(section.start());
+ }
+
+ if let Some(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 cfa_offsets = iset::IntervalMap::new();
+
+ let mut entries = unwind_section.entries(&bases);
+ let mut unwind_context = UnwindContext::new();
+ loop {
+ match entries.next()? {
+ None => return Ok(cfa_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(|| unwind_section.cie_from_offset(bases, o))
+ .clone()
+ }) {
+ Ok(fde) => fde,
+ Err(e) => {
+ error!("Failed to parse FDE: {}", e);
+ continue;
+ }
+ };
+
+ if fde.len() == 0 {
+ // This FDE is a terminator
+ return Ok(cfa_offsets);
+ }
+
+ if fde.initial_address().overflowing_add(fde.len()).1 {
+ warn!(
+ "FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}",
+ fde.offset(),
+ fde.initial_address(),
+ fde.len()
+ );
+ } else {
+ // Walk the FDE table rows and store their CFA
+ let mut fde_table = fde.rows(&unwind_section, &bases, &mut unwind_context)?;
+
+ while let Some(row) = fde_table.next_row()? {
+ match row.cfa() {
+ CfaRule::RegisterAndOffset {
+ register: _,
+ offset,
+ } => {
+ // TODO: we should store offsets by register
+ if row.start_address() < row.end_address() {
+ cfa_offsets
+ .insert(row.start_address()..row.end_address(), *offset);
+ } else {
+ debug!(
+ "Invalid FDE table row addresses: {:#x}..{:#x}",
+ row.start_address(),
+ row.end_address()
+ );
+ }
+ }
+ CfaRule::Expression(_) => {
+ debug!("Unhandled CFA expression when determining offset");
+ }
+ };
+ }
+ }
+ }
+ }
+ }
+}
+
+fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
+ let raw_view = bv.raw_view()?;
+ if let Some(section) = raw_view.section_by_name(".gnu_debugaltlink") {
+ let start = section.start();
+ let len = section.len();
+
+ if len < 20 {
+ // Not large enough to hold a build id
+ return None;
+ }
+
+ raw_view
+ .read_vec(start, len)
+ .splitn(2, |x| *x == 0)
+ .last()
+ .map(|a| a.iter().map(|b| format!("{:02x}", b)).collect())
+ } else {
+ None
+ }
+}
+
+fn parse_dwarf(
+ _bv: &BinaryView,
+ debug_bv: &BinaryView,
+ supplementary_bv: Option<&BinaryView>,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+) -> Result<DebugInfoBuilder, ()> {
+ // TODO: warn if no supplementary file and .gnu_debugaltlink section present
+
+ // 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 = &debug_bv.raw_view().ok_or(())?;
+ let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
+ debug_bv
+ } else {
+ raw_view
+ };
+
+ let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
+
+ // gimli setup
+ let endian = get_endian(view);
+ let mut section_reader =
+ |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) };
+
+ let mut dwarf = match Dwarf::load(&mut section_reader) {
+ Ok(x) => x,
+ Err(e) => {
+ error!("Failed to load DWARF info: {}", e);
+ return Err(());
+ }
+ };
+
+ if dwo_file {
+ dwarf.file_type = DwarfFileType::Dwo;
+ } else {
+ dwarf.file_type = DwarfFileType::Main;
+ }
+
+ if let Some(sup_bv) = supplementary_bv {
+ let sup_endian = get_endian(sup_bv);
+ let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv);
+ let sup_section_reader = |section_id: SectionId| -> _ {
+ create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file)
+ };
+ if let Err(e) = dwarf.load_sup(sup_section_reader) {
+ error!("Failed to load supplementary file: {}", e);
+ }
+ }
+
+ let range_data_offsets;
+ if view.section_by_name(".eh_frame").is_some() || view.section_by_name("__eh_frame").is_some() {
+ let eh_frame_endian = get_endian(view);
+ let eh_frame_section_reader = |section_id: SectionId| -> _ {
+ create_section_reader(section_id, view, eh_frame_endian, dwo_file)
+ };
+ let mut eh_frame = gimli::EhFrame::load(eh_frame_section_reader).unwrap();
+ eh_frame.set_address_size(view.address_size() as u8);
+ range_data_offsets = parse_unwind_section(view, eh_frame)
+ .map_err(|e| error!("Error parsing .eh_frame: {}", e))?;
+ } else if view.section_by_name(".debug_frame").is_some()
+ || view.section_by_name("__debug_frame").is_some()
+ {
+ let debug_frame_endian = get_endian(view);
+ let debug_frame_section_reader = |section_id: SectionId| -> _ {
+ create_section_reader(section_id, view, debug_frame_endian, dwo_file)
+ };
+ let mut debug_frame = gimli::DebugFrame::load(debug_frame_section_reader).unwrap();
+ debug_frame.set_address_size(view.address_size() as u8);
+ range_data_offsets = parse_unwind_section(view, debug_frame)
+ .map_err(|e| error!("Error parsing .debug_frame: {}", e))?;
+ } else {
+ range_data_offsets = Default::default();
+ }
+
+ // 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) {
+ calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context);
+
+ let progress_weights = [0.5, 0.5];
+ let name_progress = split_progress(&progress, 0, &progress_weights);
+ let parse_progress = split_progress(&progress, 1, &progress_weights);
+
+ if !recover_names(&dwarf, &mut debug_info_builder_context, &name_progress)
+ || debug_info_builder_context.total_die_count == 0
+ {
+ return Ok(debug_info_builder);
+ }
+
+ // Parse all the compilation units
+ let mut current_die_number = 0;
+
+ for unit in debug_info_builder_context.sup_units() {
+ parse_unit(
+ dwarf.sup().unwrap(),
+ unit,
+ &debug_info_builder_context,
+ &mut debug_info_builder,
+ &parse_progress,
+ &mut current_die_number,
+ );
+ }
+
+ for unit in debug_info_builder_context.units() {
+ parse_unit(
+ &dwarf,
+ unit,
+ &debug_info_builder_context,
+ &mut debug_info_builder,
+ &parse_progress,
+ &mut current_die_number,
+ );
+ }
+ }
+
+ Ok(debug_info_builder)
+}
+
+struct DWARFParser;
+
+impl CustomDebugInfoParser for DWARFParser {
+ fn is_valid(&self, view: &BinaryView) -> bool {
+ if dwarfreader::is_valid(view) || dwarfreader::can_use_debuginfod(view) {
+ return true;
+ }
+ if dwarfreader::has_build_id_section(view) {
+ if let Ok(build_id) = get_build_id(view) {
+ if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() {
+ return true;
+ }
+ }
+ }
+ if helpers::find_sibling_debug_file(view).is_some() {
+ return true;
+ }
+ false
+ }
+
+ fn parse_info(
+ &self,
+ debug_info: &mut DebugInfo,
+ bv: &BinaryView,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+ ) -> bool {
+ let (external_file, close_external) = if !dwarfreader::is_valid(bv) {
+ if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) {
+ (Some(debug_view), x)
+ } else if let Ok(build_id) = get_build_id(bv) {
+ load_debug_info_for_build_id(&build_id, bv)
+ } else {
+ (None, false)
+ }
+ } else {
+ (None, false)
+ };
+
+ let sup_bv = get_supplementary_build_id(external_file.as_deref().unwrap_or(debug_file))
+ .and_then(|build_id| {
+ load_debug_info_for_build_id(&build_id, bv)
+ .0
+ .map(|x| x.raw_view().unwrap())
+ });
+
+ let result = match parse_dwarf(
+ bv,
+ external_file.as_deref().unwrap_or(debug_file),
+ sup_bv.as_deref(),
+ progress,
+ ) {
+ Ok(mut builder) => {
+ builder.post_process(bv, debug_info).commit_info(debug_info);
+ true
+ }
+ Err(_) => false,
+ };
+
+ if let (Some(ext), true) = (external_file, close_external) {
+ ext.file().close();
+ }
+
+ result
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn CorePluginInit() -> bool {
+ Logger::new("DWARF").init();
+
+ let settings = Settings::new();
+
+ settings.register_setting_json(
+ "network.enableDebuginfod",
+ r#"{
+ "title" : "Enable Debuginfod Support",
+ "type" : "boolean",
+ "default" : false,
+ "description" : "Enable using Debuginfod servers to fetch DWARF debug info for files with a .note.gnu.build-id section.",
+ "ignore" : []
+ }"#,
+ );
+
+ settings.register_setting_json(
+ "network.debuginfodServers",
+ r#"{
+ "title" : "Debuginfod Server URLs",
+ "type" : "array",
+ "sorted" : true,
+ "default" : [],
+ "description" : "Servers to use for fetching DWARF debug info for files with a .note.gnu.build-id section.",
+ "ignore" : []
+ }"#,
+ );
+
+ settings.register_setting_json(
+ "analysis.debugInfo.enableDebugDirectories",
+ r#"{
+ "title" : "Enable Debug File Directories",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Enable searching local debug directories for DWARF debug info.",
+ "ignore" : []
+ }"#,
+ );
+
+ settings.register_setting_json(
+ "analysis.debugInfo.debugDirectories",
+ r#"{
+ "title" : "Debug File Directories",
+ "type" : "array",
+ "sorted" : true,
+ "default" : [],
+ "description" : "Paths to folder containing DWARF debug info stored by build id.",
+ "ignore" : []
+ }"#,
+ );
+
+ settings.register_setting_json(
+ "analysis.debugInfo.loadSiblingDebugFiles",
+ r#"{
+ "title" : "Enable Loading of Sibling Debug Files",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Enable automatic loading of X.debug and X.dSYM files next to a file named X.",
+ "ignore" : []
+ }"#,
+ );
+
+ DebugInfoParser::register("DWARF", DWARFParser {});
+ true
+}