diff options
Diffstat (limited to 'plugins/dwarf/shared/src/lib.rs')
| -rw-r--r-- | plugins/dwarf/shared/src/lib.rs | 158 |
1 files changed, 66 insertions, 92 deletions
diff --git a/plugins/dwarf/shared/src/lib.rs b/plugins/dwarf/shared/src/lib.rs index 7aa3b486..634aa784 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}; use binaryninja::{ binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, @@ -35,6 +36,9 @@ pub enum Error { #[error("{0}")] IoError(#[from] std::io::Error), + + #[error("{0}")] + ObjectError(#[from] object::Error), } pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool { @@ -89,105 +93,75 @@ pub fn get_endian(view: &BinaryView) -> RunTimeEndian { } } -pub fn create_section_reader<'a, Endian: 'a + Endianity>( +pub fn create_section_reader<Endian: Endianity>( section_id: SectionId, - view: &'a BinaryView, + view: &BinaryView, endian: Endian, - dwo_file: bool, -) -> Result<EndianRcSlice<Endian>, Error> { - let section_name = if dwo_file && section_id.dwo_name().is_some() { - section_id.dwo_name().unwrap() - } else { - section_id.name() - }; + is_dwo: bool, +) -> Result<RelocateOwned<gimli::EndianRcSlice<Endian>>, Error> { + let raw_view = view.raw_view().unwrap(); + let view_data = raw_view.read_vec(0, raw_view.len() as usize); + let file = object::File::parse(&*view_data)?; + create_section_reader_object(section_id, &file, endian, is_dwo) +} - if let Some(section) = view.section_by_name(section_name) { - // TODO : This is kinda broke....should add rust wrappers for some of this - if let Some(symbol) = view - .symbols() - .iter() - .find(|symbol| symbol.full_name().to_string_lossy() == "__elf_section_headers") - { - if let Some(data_var) = view - .data_variables() - .iter() - .find(|var| var.address == symbol.address()) - { - // TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it - let data_type = &data_var.ty.contents; - let data = view.read_vec(data_var.address, data_type.width() as usize); - let element_type = data_type.element_type().unwrap().contents; +#[derive(Debug, Clone)] +pub struct RelocationMap(Rc<object::read::RelocationMap>); - if let Some(current_section_header) = data - .chunks(element_type.width() as usize) - .find(|section_header| { - if view.address_size() == 4 { - endian.read_u32(§ion_header[16..20]) as u64 == section.start() - } else { - endian.read_u64(§ion_header[24..32]) == section.start() - } - }) - { - let section_flags = if view.address_size() == 4 { - endian.read_u32(¤t_section_header[8..12]) as u64 - } else { - endian.read_u64(¤t_section_header[8..16]) - }; - // If the section has the compressed bit set - if (section_flags & 2048) != 0 { - // Get section, trim header, decompress, return - let compressed_header_size = view.address_size() * 3; +impl Default for RelocationMap { + fn default() -> Self { + Self(Rc::new(object::read::RelocationMap::default())) + } +} - let offset = section.start() + compressed_header_size as u64; - let len = section.len() - compressed_header_size; +impl RelocationMap { + fn add(&mut self, file: &object::File, section: &object::Section) -> Result<(), Error> { + let map = + Rc::get_mut(&mut self.0).expect("Failed to get mutable reference to RelocationMap"); + for (offset, relocation) in section.relocations() { + map.add(file, offset, relocation)? + } + Ok(()) + } +} - let ch_type_vec = view.read_vec(section.start(), 4); - let ch_type = endian.read_u32(&ch_type_vec); +impl gimli::read::Relocate for RelocationMap { + fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> { + Ok(self.0.relocate(offset as u64, value)) + } - 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 offset = section.start(); - let len = section.len(); - if len == 0 { - Ok(EndianRcSlice::new(Rc::from([]), endian)) - } else { - Ok(EndianRcSlice::new( - Rc::from(view.read_vec(offset, len).as_slice()), - endian, - )) - } + fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> { + <usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64)) } - // Truncate Mach-O section names to 16 bytes - else if let Some(section) = view.section_by_name(&format!( - "__{}", - §ion_name[1..section_name.len().min(15)] - )) { - Ok(EndianRcSlice::new( - Rc::from(view.read_vec(section.start(), section.len()).as_slice()), - endian, - )) +} + +type RelocateOwned<R> = gimli::RelocateReader<R, RelocationMap>; + +pub fn create_section_reader_object<Endian: gimli::Endianity>( + id: gimli::SectionId, + file: &object::File, + endian: Endian, + is_dwo: bool, +) -> Result<RelocateOwned<gimli::EndianRcSlice<Endian>>, Error> { + let mut relocations = RelocationMap::default(); + let name = if is_dwo { + id.dwo_name() + } else if file.format() == object::BinaryFormat::Xcoff { + id.xcoff_name() } else { - Ok(EndianRcSlice::new(Rc::from([]), endian)) - } + Some(id.name()) + }; + let data = match name.and_then(|name| file.section_by_name(name)) { + Some(ref section) => { + // DWO sections never have relocations, so don't bother. + if !is_dwo { + relocations.add(file, section)?; + } + section.uncompressed_data()?.into_owned() + } + // Use a non-zero capacity so that `ReaderOffsetId`s are unique. + None => Vec::with_capacity(1), + }; + let section = EndianRcSlice::new(Rc::from(data.into_boxed_slice()), endian); + Ok(gimli::RelocateReader::new(section, relocations)) } |
