summaryrefslogtreecommitdiff
path: root/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
diff options
context:
space:
mode:
authorKyleMiles <krm504@nyu.edu>2023-04-18 12:38:09 -0400
committerKyleMiles <krm504@nyu.edu>2023-07-10 12:58:35 -0400
commit6325884782a47e0c89154cf7ce0952368e25ea2a (patch)
treeaacb435dc9e386035b75471715abcc912ce18697 /rust/examples/dwarf/dwarf_import/src/die_handlers.rs
parent068a2ff42ef4e15e67d2904166e53a51420b65a1 (diff)
DWARF Import DebugInfo Plugin
Resolves #3206
Diffstat (limited to 'rust/examples/dwarf/dwarf_import/src/die_handlers.rs')
-rw-r--r--rust/examples/dwarf/dwarf_import/src/die_handlers.rs369
1 files changed, 369 insertions, 0 deletions
diff --git a/rust/examples/dwarf/dwarf_import/src/die_handlers.rs b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
new file mode 100644
index 00000000..bc7624b3
--- /dev/null
+++ b/rust/examples/dwarf/dwarf_import/src/die_handlers.rs
@@ -0,0 +1,369 @@
+// Copyright 2021-2023 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::dwarfdebuginfo::{DebugInfoBuilder, TypeUID};
+use crate::helpers::*;
+use crate::types::get_type;
+
+use binaryninja::{
+ rc::*,
+ types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder},
+};
+
+use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Dwarf, Reader, Unit};
+
+use std::ffi::CString;
+
+pub fn handle_base_type<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+) -> Option<Ref<Type>> {
+ // All base types have:
+ // DW_AT_name
+ // DW_AT_encoding (our concept of type_class)
+ // DW_AT_byte_size and/or DW_AT_bit_size
+ // *DW_AT_endianity (assumed default for arch)
+ // *DW_AT_data_bit_offset (assumed 0)
+ // *Some indication of signedness?
+ // * = Optional
+
+ // TODO : By spec base types need to have a name, what if it's spec non-conforming?
+ let name = get_name(dwarf, unit, entry).expect("DW_TAG_base does not have name attribute");
+
+ // TODO : Handle other size specifiers (bits, offset, high_pc?, etc)
+ let size = get_size_as_usize(entry).expect("DW_TAG_base does not have size attribute");
+
+ match entry.attr_value(constants::DW_AT_encoding) {
+ // TODO : Need more binaries to see what's going on
+ Ok(Some(Encoding(encoding))) => {
+ match encoding {
+ constants::DW_ATE_address => None,
+ constants::DW_ATE_boolean => Some(Type::bool()),
+ constants::DW_ATE_complex_float => None,
+ constants::DW_ATE_float => Some(Type::named_float(size, name)),
+ constants::DW_ATE_signed => Some(Type::named_int(size, true, name)),
+ constants::DW_ATE_signed_char => Some(Type::named_int(size, true, name)),
+ constants::DW_ATE_unsigned => Some(Type::named_int(size, false, name)),
+ constants::DW_ATE_unsigned_char => Some(Type::named_int(size, false, name)),
+ constants::DW_ATE_imaginary_float => None,
+ constants::DW_ATE_packed_decimal => None,
+ constants::DW_ATE_numeric_string => None,
+ constants::DW_ATE_edited => None,
+ constants::DW_ATE_signed_fixed => None,
+ constants::DW_ATE_unsigned_fixed => None,
+ constants::DW_ATE_decimal_float => Some(Type::named_float(size, name)), // TODO : How is this different from binary floating point, ie. DW_ATE_float?
+ constants::DW_ATE_UTF => Some(Type::named_int(size, false, name)), // TODO : Verify
+ constants::DW_ATE_UCS => None,
+ constants::DW_ATE_ASCII => None, // Some sort of array?
+ constants::DW_ATE_lo_user => None,
+ constants::DW_ATE_hi_user => None,
+ _ => None, // Anything else is invalid at time of writing (gimli v0.23.0)
+ }
+ }
+ _ => None,
+ }
+}
+
+pub fn handle_enum<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+) -> Option<Ref<Type>> {
+ // All base types have:
+ // DW_AT_byte_size
+ // *DW_AT_name
+ // *DW_AT_enum_class
+ // *DW_AT_type
+ // ?DW_AT_abstract_origin
+ // ?DW_AT_accessibility
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_bit_size
+ // ?DW_AT_bit_stride
+ // ?DW_AT_byte_stride
+ // ?DW_AT_data_location
+ // ?DW_AT_declaration
+ // ?DW_AT_description
+ // ?DW_AT_sibling
+ // ?DW_AT_signature
+ // ?DW_AT_specification
+ // ?DW_AT_start_scope
+ // ?DW_AT_visibility
+ // * = Optional
+
+ // Children of enumeration_types are enumerators which contain:
+ // DW_AT_name
+ // DW_AT_const_value
+ // *DW_AT_description
+
+ let enumeration_builder = EnumerationBuilder::new();
+
+ let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
+ let mut children = tree.root().unwrap().children();
+ while let Ok(Some(child)) = children.next() {
+ if child.entry().tag() == constants::DW_TAG_enumerator {
+ let name =
+ get_name(dwarf, unit, child.entry()) // TODO : Might need to recover the full name here
+ .expect("DW_TAG_enumeration_type does not have name attribute");
+ let value = get_attr_as_u64(
+ &child
+ .entry()
+ .attr(constants::DW_AT_const_value)
+ .unwrap()
+ .unwrap(),
+ )
+ .unwrap();
+
+ enumeration_builder.insert(name, value);
+ }
+ }
+
+ Some(Type::enumeration(
+ &enumeration_builder.finalize(),
+ get_size_as_usize(entry).unwrap_or(8),
+ false,
+ ))
+}
+
+pub fn handle_typedef(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+ typedef_name: CString,
+) -> (Option<Ref<Type>>, bool) {
+ // All base types have:
+ // DW_AT_name
+ // *DW_AT_type
+ // * = Optional
+
+ // This will fail in the case where we have a typedef to a type that doesn't exist (failed to parse, incomplete, etc)
+ if let Some(entry_type_offset) = entry_type {
+ if let Some((name, t)) = debug_info_builder.get_type(entry_type_offset) {
+ // return (Some(t), true);
+ // TODO : This doesn't filter all the bad typedefs...we should consider not typedefing pointers or arrays, or it's a bug in my parsing
+ if typedef_name == name {
+ return (Some(t), false);
+ } else if typedef_name != name {
+ return (Some(t), true);
+ }
+ }
+ }
+
+ // 5.3: "typedef represents a declaration of the type that is not also a definition"
+ (None, false)
+}
+
+pub fn handle_pointer<R: Reader<Offset = usize>>(
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+ reference_type: ReferenceType,
+) -> Option<Ref<Type>> {
+ // All pointer types have:
+ // DW_AT_type
+ // *DW_AT_byte_size
+ // ?DW_AT_name
+ // ?DW_AT_address
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // * = Optional
+
+ if let Some(pointer_size) = get_size_as_usize(entry) {
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some(Type::pointer_of_width(
+ parent_type.as_ref(),
+ pointer_size,
+ false,
+ false,
+ Some(reference_type),
+ ))
+ } else {
+ Some(Type::pointer_of_width(
+ Type::void().as_ref(),
+ pointer_size,
+ false,
+ false,
+ Some(reference_type),
+ ))
+ }
+ } else {
+ None
+ }
+}
+
+pub fn handle_array<R: Reader<Offset = usize>>(
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All array types have:
+ // DW_AT_type
+ // *DW_AT_name
+ // *DW_AT_ordering
+ // *DW_AT_byte_stride or DW_AT_bit_stride
+ // *DW_AT_byte_size or DW_AT_bit_size
+ // *DW_AT_allocated
+ // *DW_AT_associated and
+ // *DW_AT_data_location
+ // * = Optional
+ // For multidimensional arrays, DW_TAG_subrange_type or DW_TAG_enumeration_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+
+ let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
+ let mut children = tree.root().unwrap().children();
+
+ // TODO : This is currently applying the size in reverse order
+ let mut result_type: Option<Ref<Type>> = None;
+ while let Ok(Some(child)) = children.next() {
+ if let Some(inner_type) = result_type {
+ result_type = Some(Type::array(
+ inner_type.as_ref(),
+ get_subrange_size(child.entry()),
+ ));
+ } else {
+ result_type = Some(Type::array(
+ parent_type.as_ref(),
+ get_subrange_size(child.entry()),
+ ));
+ }
+ }
+
+ result_type.map_or(Some(Type::array(parent_type.as_ref(), 0)), Some)
+ } else {
+ None
+ }
+}
+
+pub fn handle_function<R: Reader<Offset = usize>>(
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ entry: &DebuggingInformationEntry<R>,
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All subroutine types have:
+ // *DW_AT_name
+ // *DW_AT_type (if not provided, void)
+ // *DW_AT_prototyped
+ // ?DW_AT_abstract_origin
+ // ?DW_AT_accessibility
+ // ?DW_AT_address_class
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_declaration
+ // ?DW_AT_description
+ // ?DW_AT_sibling
+ // ?DW_AT_start_scope
+ // ?DW_AT_visibility
+ // * = Optional
+
+ // May have children, including DW_TAG_formal_parameters, which all have:
+ // *DW_AT_type
+ // * = Optional
+ // or is otherwise DW_TAG_unspecified_parameters
+
+ let return_type = match entry_type {
+ Some(entry_type_offset) => {
+ debug_info_builder
+ .get_type(entry_type_offset)
+ .expect("Subroutine return type was not processed")
+ .1
+ }
+ None => Type::void(),
+ };
+
+ let mut parameters: Vec<FunctionParameter<CString>> = vec![];
+ let mut variable_arguments = false;
+
+ // Get all the children and populate
+ // TODO : Handle other attributes?
+ let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
+ let mut children = tree.root().unwrap().children();
+ while let Ok(Some(child)) = children.next() {
+ if child.entry().tag() == constants::DW_TAG_formal_parameter {
+ if let (Some(child_uid), Some(name)) = {
+ (
+ get_type(dwarf, unit, child.entry(), debug_info_builder),
+ get_name(dwarf, unit, child.entry()),
+ )
+ } {
+ let child_type = debug_info_builder.get_type(child_uid).unwrap().1;
+ parameters.push(FunctionParameter::new(child_type, name, None));
+ }
+ } else if child.entry().tag() == constants::DW_TAG_unspecified_parameters {
+ variable_arguments = true;
+ }
+ }
+
+ Some(Type::function(
+ return_type.as_ref(),
+ &parameters,
+ variable_arguments,
+ ))
+}
+
+pub fn handle_const(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All const types have:
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_name
+ // ?DW_AT_sibling
+ // ?DW_AT_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some((*parent_type).to_builder().set_const(true).finalize())
+ } else {
+ Some(TypeBuilder::void().set_const(true).finalize())
+ }
+}
+
+pub fn handle_volatile(
+ debug_info_builder: &mut DebugInfoBuilder,
+ entry_type: Option<TypeUID>,
+) -> Option<Ref<Type>> {
+ // All const types have:
+ // ?DW_AT_allocated
+ // ?DW_AT_associated
+ // ?DW_AT_data_location
+ // ?DW_AT_name
+ // ?DW_AT_sibling
+ // ?DW_AT_type
+
+ if let Some(entry_type_offset) = entry_type {
+ let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
+ Some((*parent_type).to_builder().set_volatile(true).finalize())
+ } else {
+ Some(TypeBuilder::void().set_volatile(true).finalize())
+ }
+}
+
+// Handler template:
+// pub fn handle_<R: Reader<Offset = usize>>(
+// dwarf: &Dwarf<R>,
+// unit: &Unit<R>,
+// entry: &DebuggingInformationEntry<R>,
+// debug_info_builder: &mut DebugInfoBuilder,
+// ) -> Option<Ref<Type>> {
+// }