From 014dcc8c1ab274a42e48b546a8fd8f14a1d2e84f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 15 Oct 2025 20:33:25 -0400 Subject: [PATCH] Apply relocations manually in DWARF parsing. Hopefully addresses #7480. --- Cargo.lock | 14 ++- plugins/dwarf/shared/Cargo.toml | 1 + plugins/dwarf/shared/src/lib.rs | 187 ++++++++++++++++++++++++++++---- 3 files changed, 181 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c09b9122be..65ae70f665 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -747,6 +747,7 @@ dependencies = [ "binaryninja", "binaryninjacore-sys", "gimli", + "object 0.36.7", "thiserror 2.0.12", "zstd", ] @@ -1821,7 +1822,7 @@ dependencies = [ "hashbrown 0.14.5", "indexmap", "memchr", - "ruzstd", + "ruzstd 0.5.0", ] [[package]] @@ -1830,7 +1831,9 @@ version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ + "flate2", "memchr", + "ruzstd 0.7.3", ] [[package]] @@ -2420,6 +2423,15 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.20" diff --git a/plugins/dwarf/shared/Cargo.toml b/plugins/dwarf/shared/Cargo.toml index 83ecee936b..19cb963e13 100644 --- a/plugins/dwarf/shared/Cargo.toml +++ b/plugins/dwarf/shared/Cargo.toml @@ -11,3 +11,4 @@ binaryninjacore-sys.workspace = true gimli = "0.31" zstd = "0.13.2" thiserror = "2.0" +object = "0.36" diff --git a/plugins/dwarf/shared/src/lib.rs b/plugins/dwarf/shared/src/lib.rs index 7aa3b4863f..4f023cf527 100644 --- a/plugins/dwarf/shared/src/lib.rs +++ b/plugins/dwarf/shared/src/lib.rs @@ -13,6 +13,7 @@ // limitations under the License. use gimli::{EndianRcSlice, Endianity, RunTimeEndian, SectionId}; +use object::{Object, ObjectSection, ObjectSymbol}; use binaryninja::{ binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, @@ -135,7 +136,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( }; // If the section has the compressed bit set if (section_flags & 2048) != 0 { - // Get section, trim header, decompress, return + // Get section, trim header, decompress, and apply relocations before returning let compressed_header_size = view.address_size() * 3; let offset = section.start() + compressed_header_size as u64; @@ -145,23 +146,26 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( let ch_type = endian.read_u32(&ch_type_vec); if let Ok(buffer) = view.read_buffer(offset, len) { - match ch_type { - 1 => { - return Ok(EndianRcSlice::new( - buffer.zlib_decompress().get_data().into(), - endian, - )); - } - 2 => { - return Ok(EndianRcSlice::new( - zstd::decode_all(buffer.get_data())?.as_slice().into(), - endian, - )); - } - x => { - return Err(Error::UnknownCompressionMethod(x)); - } - } + let mut decompressed = match ch_type { + 1 => buffer.zlib_decompress().get_data().to_vec(), + 2 => zstd::decode_all(buffer.get_data())?, + x => return Err(Error::UnknownCompressionMethod(x)), + }; + + let section_name_owned = section.name().to_string_lossy().into_owned(); + let is_little = + matches!(view.default_endianness(), Endianness::LittleEndian); + apply_relocations_with_object( + view, + §ion_name_owned, + &mut decompressed, + is_little, + ); + + return Ok(EndianRcSlice::new( + Rc::from(decompressed.into_boxed_slice()), + endian, + )); } } } @@ -172,8 +176,12 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( if len == 0 { Ok(EndianRcSlice::new(Rc::from([]), endian)) } else { + let mut data = view.read_vec(offset, len); + let section_name_owned = section.name().to_string_lossy().into_owned(); + let is_little = matches!(view.default_endianness(), Endianness::LittleEndian); + apply_relocations_with_object(view, §ion_name_owned, &mut data, is_little); Ok(EndianRcSlice::new( - Rc::from(view.read_vec(offset, len).as_slice()), + Rc::from(data.into_boxed_slice()), endian, )) } @@ -183,11 +191,150 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( "__{}", §ion_name[1..section_name.len().min(15)] )) { + let mut data = view.read_vec(section.start(), section.len()); + apply_relocations_with_object( + view, + section.name().to_string_lossy().as_ref(), + &mut data, + matches!(view.default_endianness(), Endianness::LittleEndian), + ); Ok(EndianRcSlice::new( - Rc::from(view.read_vec(section.start(), section.len()).as_slice()), + Rc::from(data.into_boxed_slice()), endian, )) } else { Ok(EndianRcSlice::new(Rc::from([]), endian)) } } + +fn read_int(bytes: &[u8], is_little_endian: bool) -> u64 { + let mut value = 0u64; + if is_little_endian { + for (i, byte) in bytes.iter().enumerate() { + value |= (*byte as u64) << (i * 8); + } + } else { + for (i, byte) in bytes.iter().enumerate() { + value |= (*byte as u64) << (8 * (bytes.len() - 1 - i)); + } + } + value +} + +fn write_int(bytes: &mut [u8], value: u64, is_little_endian: bool) { + if is_little_endian { + for (i, byte) in bytes.iter_mut().enumerate() { + *byte = (value >> (i * 8)) as u8; + } + } else { + let len = bytes.len(); + for (i, byte) in bytes.iter_mut().enumerate() { + let shift = 8 * (len - 1 - i); + *byte = (value >> shift) as u8; + } + } +} + +fn apply_relocations_with_object( + view: &BinaryView, + section_name: &str, + data: &mut [u8], + is_little_endian: bool, +) -> bool { + let Some(file_bytes) = read_entire_view(view) else { + return false; + }; + + let Ok(file) = object::File::parse(&*file_bytes) else { + return false; + }; + + let Some(obj_section) = file.section_by_name(section_name) else { + return false; + }; + + let mut applied = false; + + for (offset, relocation) in obj_section.relocations() { + let size_bits = relocation.size(); + if size_bits == 0 { + continue; + } + let size = (size_bits / 8) as usize; + if size == 0 || size > 8 { + continue; + } + + let offset = offset as usize; + if offset + size > data.len() { + continue; + } + + let mut base = 0i128; + let mut target_section_name: Option<&str> = None; + + match relocation.target() { + object::RelocationTarget::Symbol(symbol_index) => { + let Ok(symbol) = file.symbol_by_index(symbol_index) else { + continue; + }; + if let Some(section_index) = symbol.section_index() { + let Ok(target_section) = file.section_by_index(section_index) else { + continue; + }; + if let Ok(name) = target_section.name() { + target_section_name = Some(name); + } + base += target_section.address() as i128; + } + base += symbol.address() as i128; + } + object::RelocationTarget::Section(section_index) => { + let Ok(target_section) = file.section_by_index(section_index) else { + continue; + }; + if let Ok(name) = target_section.name() { + target_section_name = Some(name); + } + base += target_section.address() as i128; + } + _ => {} + } + + if let Some(name) = target_section_name { + if !is_debug_related_section(name) { + continue; + } + } else { + continue; + } + + if relocation.kind() != object::RelocationKind::Absolute { + continue; + } + + let _existing = read_int(&data[offset..offset + size], is_little_endian) as i128; + let addend = relocation.addend() as i128; + let value = base.wrapping_add(addend) as u64; + write_int(&mut data[offset..offset + size], value, is_little_endian); + applied = true; + } + + applied +} + +fn read_entire_view(view: &BinaryView) -> Option> { + let len = view.len(); + if len == 0 || len > usize::MAX as u64 { + return None; + } + let data = view.read_vec(0, len as usize); + if data.len() as u64 != len { + return None; + } + Some(data) +} + +fn is_debug_related_section(name: &str) -> bool { + name.starts_with(".debug") || name.starts_with(".zdebug") +}