Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions plugins/dwarf/shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ binaryninjacore-sys.workspace = true
gimli = "0.31"
zstd = "0.13.2"
thiserror = "2.0"
object = "0.36"
187 changes: 167 additions & 20 deletions plugins/dwarf/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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;
Expand All @@ -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,
&section_name_owned,
&mut decompressed,
is_little,
);

return Ok(EndianRcSlice::new(
Rc::from(decompressed.into_boxed_slice()),
endian,
));
}
}
}
Expand All @@ -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, &section_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,
))
}
Expand All @@ -183,11 +191,150 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
"__{}",
&section_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<Vec<u8>> {
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")
}
Loading