Skip to content

Commit f52693d

Browse files
committed
[DWARF] Improve logging when loading fails, remove extra bv creation
1 parent 06c5d79 commit f52693d

File tree

2 files changed

+93
-139
lines changed

2 files changed

+93
-139
lines changed

plugins/dwarf/dwarf_import/src/helpers.rs

Lines changed: 29 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@
1414

1515
use std::ffi::OsStr;
1616
use std::path::{Path, PathBuf};
17-
use std::{ops::Deref, str::FromStr, sync::mpsc};
17+
use std::{str::FromStr, sync::mpsc};
1818

1919
use crate::{DebugInfoBuilderContext, ReaderType};
2020
use binaryninja::binary_view::BinaryViewBase;
21-
use binaryninja::file_metadata::FileMetadata;
2221
use binaryninja::Endianness;
2322
use binaryninja::{
2423
binary_view::{BinaryView, BinaryViewExt},
2524
download::{DownloadInstanceInputOutputCallbacks, DownloadProvider},
26-
rc::Ref,
2725
settings::Settings,
2826
};
2927
use gimli::Dwarf;
@@ -381,8 +379,8 @@ pub(crate) fn get_expr_value<R: ReaderType>(unit: &Unit<R>, attr: Attribute<R>)
381379
}
382380
}
383381

384-
pub(crate) fn get_build_id(view: &BinaryView) -> Result<String, String> {
385-
let mut build_id: Option<String> = None;
382+
pub(crate) fn get_build_id(view: &BinaryView) -> Result<Option<Vec<u8>>, String> {
383+
let mut build_id: Option<Vec<u8>> = None;
386384

387385
if let Some(raw_view) = view.raw_view() {
388386
if let Some(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") {
@@ -432,21 +430,18 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result<String, String> {
432430
}
433431

434432
let desc: &[u8] = &build_id_bytes[(12 + name_len as usize)..expected_len];
435-
build_id = Some(desc.iter().map(|b| format!("{:02x}", b)).collect());
433+
build_id = Some(desc.to_vec());
436434
}
437435
}
438436

439-
if let Some(x) = build_id {
440-
Ok(x)
441-
} else {
442-
Err("Failed to get build id".to_string())
443-
}
437+
Ok(build_id)
444438
}
445439

446440
pub(crate) fn download_debug_info(
447-
build_id: &str,
441+
build_id: &[u8],
448442
view: &BinaryView,
449-
) -> Result<Ref<BinaryView>, String> {
443+
) -> Result<Option<Vec<u8>>, String> {
444+
let build_id_hex: String = build_id.iter().map(|x| format!("{:02x}", x)).collect();
450445
let mut settings_query_opts = QueryOptions::new_with_view(view);
451446
let settings = Settings::new();
452447
let debug_server_urls =
@@ -456,7 +451,7 @@ pub(crate) fn download_debug_info(
456451
let artifact_url = format!(
457452
"{}/buildid/{}/debuginfo",
458453
debug_server_url.trim_end_matches("/"),
459-
build_id
454+
build_id_hex
460455
);
461456

462457
// Download from remote
@@ -510,15 +505,9 @@ pub(crate) fn download_debug_info(
510505
));
511506
}
512507
}
513-
514-
let options = "{\"analysis.debugInfo.internal\": false}";
515-
let bv = BinaryView::from_data(FileMetadata::new().deref(), &data)
516-
.map_err(|_| "Unable to create binary view from downloaded data".to_string())?;
517-
518-
return binaryninja::load_view(bv.deref(), false, Some(options))
519-
.ok_or("Unable to load binary view from downloaded data".to_string());
508+
return Ok(Some(data));
520509
}
521-
Err("Could not find a server with debug info for this file".to_string())
510+
Ok(None)
522511
}
523512

524513
pub(crate) fn find_local_debug_file_from_path(path: &PathBuf, view: &BinaryView) -> Option<String> {
@@ -557,38 +546,35 @@ pub(crate) fn find_local_debug_file_from_path(path: &PathBuf, view: &BinaryView)
557546
}
558547

559548
pub(crate) fn find_local_debug_file_for_build_id(
560-
build_id: &str,
549+
build_id: &[u8],
561550
view: &BinaryView,
562551
) -> Option<String> {
563-
let debug_ext_path = PathBuf::from(&build_id[..2]).join(format!("{}.debug", &build_id[2..]));
552+
let build_id_hex: String = build_id.iter().map(|x| format!("{:02x}", x)).collect();
553+
let debug_ext_path =
554+
PathBuf::from(&build_id_hex[..2]).join(format!("{}.debug", &build_id_hex[2..]));
564555

565-
let elf_path = PathBuf::from(&build_id[..2])
566-
.join(&build_id[2..])
556+
let elf_path = PathBuf::from(&build_id_hex[..2])
557+
.join(&build_id_hex[2..])
567558
.join("elf");
568559

569560
find_local_debug_file_from_path(&debug_ext_path, view)
570561
.or_else(|| find_local_debug_file_from_path(&elf_path, view))
571562
}
572563

573564
pub(crate) fn load_debug_info_for_build_id(
574-
build_id: &str,
565+
build_id: &[u8],
575566
view: &BinaryView,
576-
) -> (Option<Ref<BinaryView>>, bool) {
567+
) -> Result<Option<Vec<u8>>, String> {
577568
let mut settings_query_opts = QueryOptions::new_with_view(view);
578569
let settings = Settings::new();
579570
if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) {
580-
return (
581-
binaryninja::load_with_options(
582-
debug_file_path,
583-
false,
584-
Some("{\"analysis.debugInfo.internal\": false}"),
585-
),
586-
true,
587-
);
571+
return std::fs::read(&debug_file_path)
572+
.map(|x| Some(x))
573+
.map_err(|e| format!("Failed to read local debug file {}: {}", debug_file_path, e));
588574
} else if settings.get_bool_with_opts("network.enableDebuginfod", &mut settings_query_opts) {
589-
return (download_debug_info(build_id, view).ok(), true);
575+
return download_debug_info(build_id, view);
590576
}
591-
(None, false)
577+
Ok(None)
592578
}
593579

594580
pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
@@ -650,21 +636,12 @@ pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
650636
None
651637
}
652638

653-
pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) {
639+
pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> Result<Option<Vec<u8>>, String> {
654640
let Some(debug_file) = find_sibling_debug_file(view) else {
655-
return (None, false);
656-
};
657-
658-
let load_settings = match view.default_platform() {
659-
Some(plat) => format!(
660-
"{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}",
661-
plat.name()
662-
),
663-
None => "{\"analysis.debugInfo.internal\": false}".to_string(),
641+
return Ok(None);
664642
};
665643

666-
(
667-
binaryninja::load_with_options(debug_file, false, Some(load_settings)),
668-
true,
669-
)
644+
std::fs::read(&debug_file)
645+
.map(|x| Some(x))
646+
.map_err(|e| format!("Failed to read sibling debug file {}: {}", debug_file, e))
670647
}

plugins/dwarf/dwarf_import/src/lib.rs

Lines changed: 64 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ mod types;
2020

2121
use std::collections::HashMap;
2222
use std::path::PathBuf;
23-
use std::str::FromStr;
2423

2524
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
2625
use crate::functions::parse_function_entry;
@@ -499,44 +498,6 @@ where
499498
}
500499
}
501500

502-
fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
503-
let raw_view = bv.raw_view()?;
504-
if let Some(section) = raw_view.section_by_name(".gnu_debugaltlink") {
505-
let start = section.start();
506-
let len = section.len();
507-
508-
if len < 20 {
509-
// Not large enough to hold a build id
510-
return None;
511-
}
512-
513-
raw_view
514-
.read_vec(start, len)
515-
.splitn(2, |x| *x == 0)
516-
.last()
517-
.map(|a| a.iter().map(|b| format!("{:02x}", b)).collect())
518-
} else {
519-
None
520-
}
521-
}
522-
523-
fn get_supplementary_file_path(bv: &BinaryView) -> Option<PathBuf> {
524-
let raw_view = bv.raw_view()?;
525-
if let Some(section) = raw_view.section_by_name(".gnu_debugaltlink") {
526-
let start = section.start();
527-
let len = section.len();
528-
529-
raw_view
530-
.read_vec(start, len)
531-
.splitn(2, |x| *x == 0)
532-
.next()
533-
.and_then(|a| String::from_utf8(a.to_vec()).ok())
534-
.and_then(|p| PathBuf::from_str(&p).ok())
535-
} else {
536-
None
537-
}
538-
}
539-
540501
fn parse_range_data_offsets(file: &object::File) -> Result<IntervalMap<u64, i64>, String> {
541502
let dwo_file = file.section_by_name(".debug_info.dwo").is_some();
542503
let endian = match file.endianness() {
@@ -697,7 +658,7 @@ impl CustomDebugInfoParser for DWARFParser {
697658
return true;
698659
}
699660
if dwarfreader::has_build_id_section(view) {
700-
if let Ok(build_id) = get_build_id(view) {
661+
if let Ok(Some(build_id)) = get_build_id(view) {
701662
if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() {
702663
return true;
703664
}
@@ -713,30 +674,46 @@ impl CustomDebugInfoParser for DWARFParser {
713674
&self,
714675
debug_info: &mut DebugInfo,
715676
bv: &BinaryView,
716-
debug_file: &BinaryView,
677+
debug_bv: &BinaryView,
717678
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
718679
) -> bool {
719-
let (external_file, close_external) = if !dwarfreader::is_valid(bv) {
720-
if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) {
721-
(Some(debug_view), x)
722-
} else if let Ok(build_id) = get_build_id(bv) {
723-
load_debug_info_for_build_id(&build_id, bv)
724-
} else {
725-
(None, false)
726-
}
727-
} else {
728-
(None, false)
729-
};
730-
731-
let debug_bv = external_file.as_deref().unwrap_or(debug_file);
732-
733-
// Read the raw view to an object::File so relocations get handled for us
734-
let Some(raw_view) = debug_bv.raw_view() else {
735-
log::error!("Failed to get raw view for debug bv");
680+
let Some(debug_data_vec) = dwarfreader::is_valid(debug_bv)
681+
.then(|| {
682+
// Load the raw view of the debug bv passed in if it has valid debug info
683+
let raw_view = debug_bv.raw_view().expect("Failed to get raw view");
684+
raw_view.read_vec(0, raw_view.len() as usize)
685+
})
686+
.or_else(|| {
687+
// Try loading sibling debug files
688+
match helpers::load_sibling_debug_file(bv) {
689+
Ok(x) => x,
690+
Err(e) => {
691+
log::error!("Failed loading sibling debug file: {}", e);
692+
None
693+
}
694+
}
695+
})
696+
.or_else(|| {
697+
// Try loading from the file's build id
698+
if let Ok(Some(build_id)) = get_build_id(bv) {
699+
match load_debug_info_for_build_id(&build_id, bv) {
700+
Ok(x) => x,
701+
Err(e) => {
702+
log::error!("Failed loading debug info from build id: {}", e);
703+
None
704+
}
705+
}
706+
} else {
707+
// No build id found
708+
None
709+
}
710+
})
711+
else {
712+
// There isn't any dwarf info available to load
736713
return false;
737714
};
738-
let raw_view_data = raw_view.read_vec(0, raw_view.len() as usize);
739-
let debug_file = match object::File::parse(&*raw_view_data) {
715+
716+
let debug_file = match object::File::parse(debug_data_vec.as_slice()) {
740717
Ok(x) => x,
741718
Err(e) => {
742719
log::error!("Failed to parse bv: {}", e);
@@ -746,31 +723,35 @@ impl CustomDebugInfoParser for DWARFParser {
746723

747724
// TODO: allow passing a supplementary file path as a setting?
748725
// Try to load supplementary file from build id, falling back to file path
749-
let sup_view_data = get_supplementary_build_id(debug_bv)
750-
.and_then(|build_id| {
751-
load_debug_info_for_build_id(&build_id, bv)
752-
.0
753-
.map(|x| x.raw_view().expect("Failed to get raw view"))
754-
})
755-
.or_else(|| {
756-
get_supplementary_file_path(debug_bv).and_then(|sup_file_path_suggestion| {
757-
find_local_debug_file_from_path(&sup_file_path_suggestion, debug_bv).and_then(
758-
|sup_file_path| {
759-
binaryninja::load_with_options(
760-
sup_file_path,
761-
false,
762-
Some("{\"analysis.debugInfo.internal\": false}"),
763-
)
726+
let sup_view_data = debug_file.gnu_debugaltlink().ok().flatten().and_then(
727+
|(sup_filename, sup_build_id)| {
728+
// Try loading from build id
729+
let sup_data = match load_debug_info_for_build_id(sup_build_id, bv) {
730+
Ok(x) => x,
731+
Err(e) => {
732+
log::error!("Failed to load supplementary debug file: {}", e);
733+
None
734+
}
735+
};
736+
737+
// Try loading from file path if build id loading didn't work
738+
sup_data.or_else(|| match std::str::from_utf8(sup_filename) {
739+
Ok(x) => find_local_debug_file_from_path(&PathBuf::from(x), bv).and_then(
740+
|sup_file_path| match std::fs::read(sup_file_path) {
741+
Ok(sup_data) => Some(sup_data),
742+
Err(e) => {
743+
log::error!("Failed reading supplementary file {}: {}", x, e);
744+
None
745+
}
764746
},
765-
)
747+
),
748+
Err(e) => {
749+
log::error!("Supplementary file path is invalid utf8: {}", e);
750+
None
751+
}
766752
})
767-
})
768-
.and_then(|sup_bv| {
769-
let sup_raw_view = sup_bv.raw_view()?;
770-
let sup_raw_data = sup_raw_view.read_vec(0, sup_raw_view.len() as usize);
771-
sup_raw_view.file().close();
772-
Some(sup_raw_data)
773-
});
753+
},
754+
);
774755

775756
let sup_file =
776757
sup_view_data
@@ -815,10 +796,6 @@ impl CustomDebugInfoParser for DWARFParser {
815796
}
816797
};
817798

818-
if let (Some(ext), true) = (external_file, close_external) {
819-
ext.file().close();
820-
}
821-
822799
result
823800
}
824801
}

0 commit comments

Comments
 (0)