Skip to content

Commit b6740d0

Browse files
committed
Apply relocations when parsing DWARF
1 parent e75d64a commit b6740d0

File tree

6 files changed

+180
-216
lines changed

6 files changed

+180
-216
lines changed

Cargo.lock

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/dwarf/dwarf_import/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ log = "0.4"
1616
iset = "0.2.2"
1717
cpp_demangle = "0.4.3"
1818
regex = "1"
19-
indexmap = "2.5.0"
19+
indexmap = "2.5.0"
20+
object = "0.36"

plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub(crate) struct DebugInfoBuilderContext<R: ReaderType> {
125125
}
126126

127127
impl<R: ReaderType> DebugInfoBuilderContext<R> {
128-
pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf<R>) -> Option<Self> {
128+
pub(crate) fn new(default_address_size: usize, dwarf: &Dwarf<R>) -> Option<Self> {
129129
let mut units = vec![];
130130
let mut iter = dwarf.units();
131131
while let Ok(Some(header)) = iter.next() {
@@ -154,7 +154,7 @@ impl<R: ReaderType> DebugInfoBuilderContext<R> {
154154
units,
155155
sup_units,
156156
names: HashMap::new(),
157-
default_address_size: view.address_size(),
157+
default_address_size,
158158
total_die_count: 0,
159159
total_unit_size_bytes: 0,
160160
})

plugins/dwarf/dwarf_import/src/lib.rs

Lines changed: 92 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ use binaryninja::{
3232
settings::Settings,
3333
template_simplifier::simplify_str_to_str,
3434
};
35-
use dwarfreader::{
36-
create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
37-
};
35+
use dwarfreader::{create_section_reader_object, get_endian, is_dwo_dwarf, is_non_dwo_dwarf};
3836

3937
use functions::parse_lexical_block;
4038
use gimli::{
@@ -46,6 +44,7 @@ use binaryninja::logger::Logger;
4644
use helpers::{get_build_id, load_debug_info_for_build_id};
4745
use iset::IntervalMap;
4846
use log::{debug, error, warn};
47+
use object::{Object, ObjectSection};
4948

5049
trait ReaderType: Reader<Offset = usize> {}
5150
impl<T: Reader<Offset = usize>> ReaderType for T {}
@@ -388,48 +387,30 @@ fn parse_unit<R: ReaderType>(
388387
}
389388

390389
fn parse_unwind_section<R: Reader, U: UnwindSection<R>>(
391-
view: &BinaryView,
390+
file: &object::File,
392391
unwind_section: U,
393392
) -> gimli::Result<iset::IntervalMap<u64, i64>>
394393
where
395394
<U as UnwindSection<R>>::Offset: std::hash::Hash,
396395
{
397396
let mut bases = gimli::BaseAddresses::default();
398397

399-
// DWARF info is stored relative to the original image base (0 for relocatable images), normalize entries to the original image base
400-
let section_adjustment = view.original_image_base().wrapping_sub(view.image_base());
401-
402-
if let Some(section) = view
403-
.section_by_name(".eh_frame_hdr")
404-
.or(view.section_by_name("__eh_frame_hdr"))
405-
{
406-
bases = bases.set_eh_frame_hdr(section.start().wrapping_add(section_adjustment));
398+
if let Some(section) = file.section_by_name(".eh_frame_hdr") {
399+
bases = bases.set_eh_frame_hdr(section.address());
407400
}
408401

409-
if let Some(section) = view
410-
.section_by_name(".eh_frame")
411-
.or(view.section_by_name("__eh_frame"))
412-
{
413-
bases = bases.set_eh_frame(section.start().wrapping_add(section_adjustment));
414-
} else if let Some(section) = view
415-
.section_by_name(".debug_frame")
416-
.or(view.section_by_name("__debug_frame"))
417-
{
418-
bases = bases.set_eh_frame(section.start().wrapping_add(section_adjustment));
402+
if let Some(section) = file.section_by_name(".eh_frame") {
403+
bases = bases.set_eh_frame(section.address());
404+
} else if let Some(section) = file.section_by_name(".debug_frame") {
405+
bases = bases.set_eh_frame(section.address());
419406
}
420407

421-
if let Some(section) = view
422-
.section_by_name(".text")
423-
.or(view.section_by_name("__text"))
424-
{
425-
bases = bases.set_text(section.start().wrapping_add(section_adjustment));
408+
if let Some(section) = file.section_by_name(".text") {
409+
bases = bases.set_text(section.address());
426410
}
427411

428-
if let Some(section) = view
429-
.section_by_name(".got")
430-
.or(view.section_by_name("__got"))
431-
{
432-
bases = bases.set_got(section.start().wrapping_add(section_adjustment));
412+
if let Some(section) = file.section_by_name(".got") {
413+
bases = bases.set_got(section.address());
433414
}
434415

435416
let mut cies = HashMap::new();
@@ -535,58 +516,49 @@ fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
535516
}
536517
}
537518

538-
fn parse_range_data_offsets(
539-
bv: &BinaryView,
540-
dwo_file: bool,
541-
) -> Option<Result<IntervalMap<u64, i64>, ()>> {
542-
if bv.section_by_name(".eh_frame").is_some() || bv.section_by_name("__eh_frame").is_some() {
543-
let eh_frame_endian = get_endian(bv);
544-
let eh_frame_section_reader = |section_id: SectionId| -> _ {
545-
create_section_reader(section_id, bv, eh_frame_endian, dwo_file)
546-
};
547-
let mut eh_frame = match gimli::EhFrame::load(eh_frame_section_reader) {
548-
Ok(x) => x,
549-
Err(e) => {
550-
log::error!("Failed to load EH frame: {}", e);
551-
return None;
552-
}
553-
};
554-
if let Some(view_arch) = bv.default_arch() {
555-
if view_arch.name().as_str() == "aarch64" {
556-
eh_frame.set_vendor(gimli::Vendor::AArch64);
557-
}
519+
fn parse_range_data_offsets(bv: &BinaryView) -> Result<IntervalMap<u64, i64>, String> {
520+
let raw_view = bv.raw_view().unwrap();
521+
let raw_view_data = raw_view.read_vec(0, raw_view.len() as usize);
522+
let file =
523+
object::File::parse(&*raw_view_data).map_err(|e| format!("Failed to parse bv: {}", e))?;
524+
let dwo_file = file.section_by_name(".debug_info.dwo").is_some();
525+
let endian = match file.endianness() {
526+
object::Endianness::Little => gimli::RunTimeEndian::Little,
527+
object::Endianness::Big => gimli::RunTimeEndian::Big,
528+
};
529+
530+
let section_reader = |section_id: SectionId| -> _ {
531+
create_section_reader_object(section_id, &file, endian, dwo_file)
532+
};
533+
534+
if file.section_by_name(".eh_frame").is_some() {
535+
let mut eh_frame = gimli::EhFrame::load(section_reader)
536+
.map_err(|e| format!("Failed to load EH frame: {}", e))?;
537+
538+
if file.architecture() == object::Architecture::Aarch64 {
539+
eh_frame.set_vendor(gimli::Vendor::AArch64);
558540
}
559-
eh_frame.set_address_size(bv.address_size() as u8);
560-
Some(
561-
parse_unwind_section(bv, eh_frame)
562-
.map_err(|e| error!("Error parsing .eh_frame: {}", e)),
563-
)
564-
} else if bv.section_by_name(".debug_frame").is_some()
565-
|| bv.section_by_name("__debug_frame").is_some()
566-
{
567-
let debug_frame_endian = get_endian(bv);
568-
let debug_frame_section_reader = |section_id: SectionId| -> _ {
569-
create_section_reader(section_id, bv, debug_frame_endian, dwo_file)
570-
};
571-
let mut debug_frame = match gimli::DebugFrame::load(debug_frame_section_reader) {
572-
Ok(x) => x,
573-
Err(e) => {
574-
log::error!("Failed to load debug frame: {}", e);
575-
return None;
576-
}
577-
};
578-
if let Some(view_arch) = bv.default_arch() {
579-
if view_arch.name().as_str() == "aarch64" {
580-
debug_frame.set_vendor(gimli::Vendor::AArch64);
581-
}
541+
542+
if let Some(address_size) = file.architecture().address_size() {
543+
eh_frame.set_address_size(address_size.bytes());
582544
}
583-
debug_frame.set_address_size(bv.address_size() as u8);
584-
Some(
585-
parse_unwind_section(bv, debug_frame)
586-
.map_err(|e| error!("Error parsing .debug_frame: {}", e)),
587-
)
545+
546+
parse_unwind_section(&file, eh_frame).map_err(|e| format!("Error parsing .eh_frame: {}", e))
547+
} else if file.section_by_name(".debug_frame").is_some() {
548+
let mut debug_frame = gimli::DebugFrame::load(section_reader)
549+
.map_err(|e| format!("Failed to load debug frame: {}", e))?;
550+
551+
if file.architecture() == object::Architecture::Aarch64 {
552+
debug_frame.set_vendor(gimli::Vendor::AArch64);
553+
}
554+
555+
if let Some(address_size) = file.architecture().address_size() {
556+
debug_frame.set_address_size(address_size.bytes());
557+
}
558+
parse_unwind_section(&file, debug_frame)
559+
.map_err(|e| format!("Error parsing .debug_frame: {}", e))
588560
} else {
589-
None
561+
Ok(Default::default())
590562
}
591563
}
592564

@@ -595,75 +567,75 @@ fn parse_dwarf(
595567
debug_bv: &BinaryView,
596568
supplementary_bv: Option<&BinaryView>,
597569
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
598-
) -> Result<DebugInfoBuilder, ()> {
570+
) -> Result<DebugInfoBuilder, String> {
599571
// TODO: warn if no supplementary file and .gnu_debugaltlink section present
600572

601573
// Determine if this is a DWO
602574
// TODO : Make this more robust...some DWOs follow non-DWO conventions
603575

604576
// Figure out if it's the given view or the raw view that has the dwarf info in it
605-
let raw_view = &debug_bv.raw_view().ok_or(())?;
606-
let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
607-
debug_bv
577+
let raw_view = &debug_bv
578+
.raw_view()
579+
.ok_or("Failed to get raw view for debug binary view".to_string())?;
580+
581+
let address_size = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
582+
debug_bv.address_size()
608583
} else {
609-
raw_view
584+
raw_view.address_size()
610585
};
611586

612-
let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
613-
614-
// gimli setup
615-
let endian = get_endian(view);
616-
let mut section_reader =
617-
|section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) };
587+
// Parse this early to reduce peak memory usage
588+
let range_data_offsets = parse_range_data_offsets(bv).unwrap_or_default();
589+
590+
// Read the raw view to an object::File so relocations get handled for us
591+
let raw_view_data = raw_view.read_vec(0, raw_view.len() as usize);
592+
let debug_file =
593+
object::File::parse(&*raw_view_data).map_err(|e| format!("Failed to parse bv: {}", e))?;
594+
let dwo_file = debug_file.section_by_name(".debug_info.dwo").is_some();
595+
let endian = match debug_file.endianness() {
596+
object::Endianness::Little => gimli::RunTimeEndian::Little,
597+
object::Endianness::Big => gimli::RunTimeEndian::Big,
598+
};
618599

619-
let mut dwarf = match Dwarf::load(&mut section_reader) {
620-
Ok(x) => x,
621-
Err(e) => {
622-
error!("Failed to load DWARF info: {}", e);
623-
return Err(());
624-
}
600+
let mut section_reader = |section_id: SectionId| -> _ {
601+
create_section_reader_object(section_id, &debug_file, endian, dwo_file)
625602
};
626603

604+
let mut dwarf = Dwarf::load(&mut section_reader)
605+
.map_err(|e| format!("Failed to load DWARF info: {}", e))?;
606+
627607
if dwo_file {
628608
dwarf.file_type = DwarfFileType::Dwo;
629609
} else {
630610
dwarf.file_type = DwarfFileType::Main;
631611
}
632612

633613
if let Some(sup_bv) = supplementary_bv {
614+
let sup_raw_view = sup_bv
615+
.raw_view()
616+
.ok_or_else(|| format!("Failed to get raw view for supplementary bv"))?;
617+
let sup_view_data = sup_raw_view.read_vec(0, sup_raw_view.len() as usize);
618+
let sup_file = object::File::parse(&*sup_view_data)
619+
.map_err(|e| format!("Failed to parse supplementary bv: {}", e))?;
634620
let sup_endian = get_endian(sup_bv);
635-
let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv);
621+
let sup_dwo_file = sup_file.section_by_name(".debug_info.dwo").is_some();
636622
let sup_section_reader = |section_id: SectionId| -> _ {
637-
create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file)
623+
create_section_reader_object(section_id, &sup_file, sup_endian, sup_dwo_file)
638624
};
639625
if let Err(e) = dwarf.load_sup(sup_section_reader) {
640626
error!("Failed to load supplementary file: {}", e);
641627
}
642628
}
643629

644-
let range_data_offsets = match parse_range_data_offsets(bv, dwo_file) {
645-
Some(x) => x?,
646-
None => {
647-
if let Some(raw_view) = bv.raw_view() {
648-
if let Some(offsets) = parse_range_data_offsets(&raw_view, dwo_file) {
649-
offsets?
650-
} else {
651-
Default::default()
652-
}
653-
} else {
654-
Default::default()
655-
}
656-
}
657-
};
658-
659630
// Create debug info builder and recover name mapping first
660631
// Since DWARF is stored as a tree with arbitrary implicit edges among leaves,
661632
// it is not possible to correctly track namespaces while you're parsing "in order" without backtracking,
662633
// so we just do it up front
663634
let mut debug_info_builder = DebugInfoBuilder::new();
664635
debug_info_builder.set_range_data_offsets(range_data_offsets);
665636

666-
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, &dwarf) {
637+
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(address_size, &dwarf)
638+
{
667639
calculate_total_unit_bytes(&dwarf, &mut debug_info_builder_context);
668640

669641
let progress_weights = [0.5, 0.5];
@@ -770,7 +742,10 @@ impl CustomDebugInfoParser for DWARFParser {
770742
builder.post_process(bv, debug_info).commit_info(debug_info);
771743
true
772744
}
773-
Err(_) => false,
745+
Err(e) => {
746+
log::error!("Failed to parse DWARF: {}", e);
747+
false
748+
}
774749
};
775750

776751
if let (Some(ext), true) = (external_file, close_external) {

plugins/dwarf/shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ binaryninjacore-sys.workspace = true
1111
gimli = "0.31"
1212
zstd = "0.13.2"
1313
thiserror = "2.0"
14+
object = "0.36"

0 commit comments

Comments
 (0)