// Copyright 2021-2024 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, DebugInfoBuilderContext, TypeUID}; use crate::types::get_type; use crate::{helpers::*, ReaderType}; use binaryninja::{ rc::*, types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder}, }; use gimli::Dwarf; use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Unit}; pub(crate) fn handle_base_type( dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &DebugInfoBuilderContext, ) -> Option> { // All base types have: // DW_AT_encoding (our concept of type_class) // DW_AT_byte_size and/or DW_AT_bit_size // *DW_AT_name // *DW_AT_endianity (assumed default for arch) // *DW_AT_data_bit_offset (assumed 0) // *Some indication of signedness? // * = Optional let name = debug_info_builder_context.get_name(dwarf, unit, entry)?; let size = get_size_as_usize(entry)?; match entry.attr_value(constants::DW_AT_encoding) { 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)), 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(crate) fn handle_enum( dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &DebugInfoBuilderContext, ) -> Option> { // 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 = debug_info_builder_context.get_name(dwarf, unit, child.entry())?; let attr = &child .entry() .attr(constants::DW_AT_const_value) .unwrap() .unwrap(); if let Some(value) = get_attr_as_u64(attr) { enumeration_builder.insert(name, value); } else { log::error!("Unhandled enum member value type - please report this"); return None; } } } let width = match get_size_as_usize(entry).unwrap_or(8) { 0 => debug_info_builder_context.default_address_size(), x => x, }; Some(Type::enumeration( &enumeration_builder.finalize(), // TODO: This looks bad, look at the comment in [`Type::width`]. width.try_into().unwrap(), false, )) } pub(crate) fn handle_typedef( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, typedef_name: &str, ) -> (Option>, 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(t) = debug_info_builder.get_type(entry_type_offset) { return (Some(t.get_type()), typedef_name != t.name); } } // 5.3: "typedef represents a declaration of the type that is not also a definition" (None, false) } pub(crate) fn handle_pointer( entry: &DebuggingInformationEntry, debug_info_builder_context: &DebugInfoBuilderContext, debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, reference_type: ReferenceType, ) -> Option> { // 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() .get_type(); 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 if let Some(entry_type_offset) = entry_type { let parent_type = debug_info_builder .get_type(entry_type_offset) .unwrap() .get_type(); Some(Type::pointer_of_width( parent_type.as_ref(), debug_info_builder_context.default_address_size(), false, false, Some(reference_type), )) } else { Some(Type::pointer_of_width( Type::void().as_ref(), debug_info_builder_context.default_address_size(), false, false, Some(reference_type), )) } } pub(crate) fn handle_array( unit: &Unit, entry: &DebuggingInformationEntry, debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, ) -> Option> { // 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() .get_type(); 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> = 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(crate) fn handle_function( dwarf: &Dwarf, unit: &Unit, entry: &DebuggingInformationEntry, debug_info_builder_context: &DebugInfoBuilderContext, debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, ) -> Option> { // 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") .get_type(), None => Type::void(), }; // Alias function type in the case that it contains itself if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) { let ntr = Type::named_type_from_type(&name, &Type::function(return_type.as_ref(), vec![], false)); debug_info_builder.add_type(get_uid(dwarf, unit, entry), name, ntr, false); } let mut parameters: Vec = vec![]; let mut variable_arguments = false; // Get all the children and populate 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_context, debug_info_builder, ), debug_info_builder_context.get_name(dwarf, unit, child.entry()), ) } { let child_type = debug_info_builder.get_type(child_uid).unwrap().get_type(); parameters.push(FunctionParameter::new(child_type, name, None)); } } else if child.entry().tag() == constants::DW_TAG_unspecified_parameters { variable_arguments = true; } } if debug_info_builder_context .get_name(dwarf, unit, entry) .is_some() { debug_info_builder.remove_type(get_uid(dwarf, unit, entry)); } Some(Type::function( return_type.as_ref(), parameters, variable_arguments, )) } pub(crate) fn handle_const( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, ) -> Option> { // 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() .get_type(); Some((*parent_type).to_builder().set_const(true).finalize()) } else { Some(TypeBuilder::void().set_const(true).finalize()) } } pub(crate) fn handle_volatile( debug_info_builder: &mut DebugInfoBuilder, entry_type: Option, ) -> Option> { // 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() .get_type(); Some((*parent_type).to_builder().set_volatile(true).finalize()) } else { Some(TypeBuilder::void().set_volatile(true).finalize()) } }