diff options
| author | Rubens Brandao <git@rubens.io> | 2024-10-03 12:55:16 -0300 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2024-10-10 13:39:07 -0400 |
| commit | a465c25bfc8a963b3e02a1f4bc3a10eaadb91cad (patch) | |
| tree | 3f5708b0c682830cdf2bcffbb7c188eb9611f2b1 /rust/examples/idb_import/src/lib.rs | |
| parent | d6946973f49e6d6b181ed0efb6d40c23c8afdb7d (diff) | |
Update IDB import to idb-rs 0.1.4
Diffstat (limited to 'rust/examples/idb_import/src/lib.rs')
| -rw-r--r-- | rust/examples/idb_import/src/lib.rs | 963 |
1 files changed, 140 insertions, 823 deletions
diff --git a/rust/examples/idb_import/src/lib.rs b/rust/examples/idb_import/src/lib.rs index bb285a30..d611d1d7 100644 --- a/rust/examples/idb_import/src/lib.rs +++ b/rust/examples/idb_import/src/lib.rs @@ -1,84 +1,22 @@ -use std::collections::HashMap; +mod types; +use types::*; +mod addr_info; +use addr_info::*; -use binaryninja::architecture::CoreArchitecture; -use binaryninja::binaryninjacore_sys::{BNMemberAccess, BNMemberScope}; use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; use binaryninja::debuginfo::{ CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser, }; use binaryninja::logger; -use binaryninja::rc::Ref; -use binaryninja::types::{ - Conf, EnumerationBuilder, FunctionParameter, NamedTypeReferenceClass, StructureBuilder, - StructureType, Type, -}; -use idb_rs::id0::ID0Section; -use idb_rs::til::{ - array::Array as TILArray, function::Function as TILFunction, r#enum::Enum as TILEnum, - r#struct::Struct as TILStruct, r#struct::StructMember as TILStructMember, section::TILSection, - union::Union as TILUnion, TILTypeInfo, Type as TILType, Typedef as TILTypedef, -}; +use idb_rs::id0::{ID0Section, IDBParam1, IDBParam2}; +use idb_rs::til::section::TILSection; +use idb_rs::til::Type as TILType; use log::{error, trace, warn, LevelFilter}; use anyhow::Result; -#[derive(Debug, Clone)] -enum BnTypeError { - // TODO delete this and make this verification during the TIL/IDB parsing, translating the ordinal - // into a kind of type_idx - OrdinalNotFound(u32), - NameNotFound(String), - - Typedef(Box<BnTypeError>), - Function(FunctionError), - Array(Box<BnTypeError>), - Pointer(Box<BnTypeError>), - /// Error for members - Struct(Vec<(usize, BnTypeError)>), - Union(Vec<(usize, BnTypeError)>), -} - -#[derive(Default, Debug, Clone)] -struct FunctionError { - ret: Option<Box<BnTypeError>>, - args: Vec<(usize, BnTypeError)>, -} - -impl std::fmt::Display for BnTypeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - BnTypeError::OrdinalNotFound(i) => write!(f, "Reference to non existing Ordinal {i}"), - BnTypeError::NameNotFound(name) => write!(f, "Reference to non existing name {name}"), - BnTypeError::Typedef(error) => write!(f, "Typedef: {error}"), - BnTypeError::Function(FunctionError { ret, args }) => { - if let Some(error) = ret { - write!(f, "Function return: {error} ")?; - } - for (i, error) in args { - write!(f, "Function argument {i}: {error} ")?; - } - Ok(()) - } - BnTypeError::Array(error) => write!(f, "Array: {error}"), - BnTypeError::Struct(errors) => { - for (i, error) in errors { - write!(f, "Struct Member {i}: {error} ")?; - } - Ok(()) - } - BnTypeError::Union(errors) => { - for (i, error) in errors { - write!(f, "Union Member {i}: {error} ")?; - } - Ok(()) - } - BnTypeError::Pointer(error) => write!(f, "Pointer: {error}"), - } - } -} - struct IDBDebugInfoParser; impl CustomDebugInfoParser for IDBDebugInfoParser { fn is_valid(&self, view: &BinaryView) -> bool { @@ -178,57 +116,19 @@ fn parse_idb_info( trace!("Parsing the TIL section"); let til = parser.read_til_section(til_section)?; // progress 0%-50% - parse_til_section_info(debug_info, debug_file, til, |value, total| { - progress(value, total.wrapping_mul(2)) - })?; + import_til_section(debug_info, debug_file, &til, progress)?; } if let Some(id0_section) = parser.id0_section_offset() { trace!("Parsing the ID0 section"); let id0 = parser.read_id0_section(id0_section)?; // progress 50%-100% - parse_id0_section_info(debug_info, bv, debug_file, id0, |value, old_total| { - let new_total = old_total.wrapping_mul(2); - progress(value + old_total, new_total) - })?; + parse_id0_section_info(debug_info, bv, debug_file, &id0)?; } Ok(()) } -fn translate_enum(members: &[(Option<String>, u64)], bytesize: u64) -> Ref<Type> { - let eb = EnumerationBuilder::new(); - for (i, (name, bytesize)) in members.iter().enumerate() { - let name = name.to_owned().unwrap_or_else(|| format!("member_{i}")); - eb.insert(name, *bytesize); - } - Type::enumeration( - &eb.finalize(), - usize::try_from(bytesize).unwrap(), - Conf::new(false, 0), - ) -} - -fn translate_basic(mdata: &idb_rs::til::Basic) -> Ref<Type> { - match *mdata { - idb_rs::til::Basic::Void => Type::void(), - idb_rs::til::Basic::Unknown { bytes: 0 } => Type::void(), - idb_rs::til::Basic::Unknown { bytes } => Type::array(&Type::char(), bytes.into()), - idb_rs::til::Basic::Bool { bytes } if bytes.get() == 1 => Type::bool(), - // NOTE Binja don't have any representation for bool other then the default - idb_rs::til::Basic::Bool { bytes } => Type::int(bytes.get().into(), false), - idb_rs::til::Basic::Char => Type::char(), - // TODO what exacly is Segment Register? - idb_rs::til::Basic::SegReg => Type::char(), - idb_rs::til::Basic::Int { bytes, is_signed } => { - // default into signed - let is_signed = is_signed.as_ref().copied().unwrap_or(true); - Type::int(bytes.get().into(), is_signed) - } - idb_rs::til::Basic::Float { bytes } => Type::float(bytes.get().into()), - } -} - fn parse_til_info( debug_info: &mut DebugInfo, debug_file: &BinaryView, @@ -242,630 +142,75 @@ fn parse_til_info( let file = std::io::BufReader::new(file); trace!("Parsing the TIL section"); let til = TILSection::parse(file)?; - parse_til_section_info(debug_info, debug_file, til, progress) -} - -#[derive(Default)] -enum TranslateTypeResult { - #[default] - NotYet, - /// Unable to solve type, there is no point in trying again - Error(BnTypeError), - /// a type that is not final, but equivalent to the final type, if error, there is no - /// point in trying again - PartiallyTranslated(Ref<Type>, Option<BnTypeError>), - Translated(Ref<Type>), -} - -impl From<Result<Ref<Type>, BnTypeError>> for TranslateTypeResult { - fn from(value: Result<Ref<Type>, BnTypeError>) -> Self { - match value { - Ok(ty) => Self::Translated(ty), - Err(error) => Self::Error(error), - } - } -} - -struct TranslatesIDBType<'a> { - // sanitized name form IDB - name: String, - // class, just to make easy to create named_type - _class: NamedTypeReferenceClass, - // the result, if converted - ty: TranslateTypeResult, - og_ty: &'a TILTypeInfo, - is_symbol: bool, + import_til_section(debug_info, debug_file, &til, progress) } -struct TranslateIDBTypes<'a, F: Fn(usize, usize) -> Result<(), ()>> { - arch: CoreArchitecture, - debug_info: &'a mut DebugInfo, - _debug_file: &'a BinaryView, - progress: F, - til: &'a TILSection, - // note it's mapped 1:1 with the same index from til types.chain(symbols) - types: Vec<TranslatesIDBType<'a>>, - // ordinals with index to types - types_by_ord: HashMap<u64, usize>, - // original names with index to types - types_by_name: HashMap<String, usize>, -} - -impl<F: Fn(usize, usize) -> Result<(), ()>> TranslateIDBTypes<'_, F> { - fn find_typedef_by_ordinal(&self, ord: u64) -> Option<TranslateTypeResult> { - self.types_by_ord - .get(&ord) - .map(|idx| self.find_typedef(&self.types[*idx])) - } - - fn find_typedef_by_name(&self, name: &str) -> Option<TranslateTypeResult> { - if name.is_empty() { - // TODO this is my assumption, maybe an empty names Typedef means something else. - return Some(TranslateTypeResult::Translated(Type::void())); - } - - if let Some(other_ty) = self - .types_by_name - .get(name) - .map(|idx| self.find_typedef(&self.types[*idx])) - { - return Some(other_ty); - } - - // check for types that ar usually not defined directly - match name { - "Unkown" | "uint8_t" => Some(TranslateTypeResult::Translated(Type::int(1, false))), - "IUnkown" | "int8_t" => Some(TranslateTypeResult::Translated(Type::int(1, true))), - "SHORT" | "USHORT" => Some(TranslateTypeResult::Translated(Type::int( - self.til - .sizes - .map(|x| x.size_short.get()) - .unwrap_or(2) - .into(), - name == "SHORT", - ))), - "int16_t" => Some(TranslateTypeResult::Translated(Type::int(2, true))), - "uint16_t" => Some(TranslateTypeResult::Translated(Type::int(2, false))), - "int32_t" => Some(TranslateTypeResult::Translated(Type::int(4, true))), - "uint32_t" => Some(TranslateTypeResult::Translated(Type::int(4, false))), - "int64_t" => Some(TranslateTypeResult::Translated(Type::int(8, true))), - "uint64_t" => Some(TranslateTypeResult::Translated(Type::int(8, false))), - "int128_t" => Some(TranslateTypeResult::Translated(Type::int(16, true))), - "uint128_t" => Some(TranslateTypeResult::Translated(Type::int(16, false))), - _ => None, - } - } - - fn find_typedef(&self, ty: &TranslatesIDBType) -> TranslateTypeResult { - // only return a typedef, if it's solved, at least partially - match &ty.ty { - TranslateTypeResult::NotYet => TranslateTypeResult::NotYet, - TranslateTypeResult::Error(error) => { - TranslateTypeResult::Error(BnTypeError::Typedef(Box::new(error.to_owned()))) - } - TranslateTypeResult::PartiallyTranslated(og_ty, error) => { - TranslateTypeResult::PartiallyTranslated( - Type::named_type_from_type(ty.name.as_str(), &og_ty), - error - .as_ref() - .map(|x| BnTypeError::Typedef(Box::new(x.clone()))) - .clone(), - ) - } - TranslateTypeResult::Translated(og_ty) => TranslateTypeResult::Translated( - Type::named_type_from_type(ty.name.as_str(), &og_ty), - ), - } - } - - fn translate_pointer(&self, ty: &TILType) -> TranslateTypeResult { - match self.translate_type(ty) { - TranslateTypeResult::Translated(trans) => { - TranslateTypeResult::Translated(Type::pointer(&self.arch, &trans)) - } - TranslateTypeResult::PartiallyTranslated(trans, error) => { - TranslateTypeResult::PartiallyTranslated( - Type::pointer(&self.arch, &trans), - error.map(|e| BnTypeError::Pointer(Box::new(e))), - ) - } - TranslateTypeResult::Error(error) => TranslateTypeResult::PartiallyTranslated( - Type::pointer(&self.arch, &Type::void()), - Some(error), - ), - TranslateTypeResult::NotYet => TranslateTypeResult::PartiallyTranslated( - Type::pointer(&self.arch, &Type::void()), - None, - ), - } - } - - fn translate_function(&self, fun: &TILFunction) -> TranslateTypeResult { - let mut is_partial = false; - let mut errors: FunctionError = Default::default(); - // funtions are always 0 len, so it's translated or partial(void) - let return_ty = match self.translate_type(&fun.ret) { - TranslateTypeResult::Translated(trans) => trans, - TranslateTypeResult::PartiallyTranslated(trans, error) => { - is_partial |= true; - errors.ret = error.map(Box::new); - trans - } - TranslateTypeResult::Error(error) => { - errors.ret = Some(Box::new(error)); - return TranslateTypeResult::PartiallyTranslated( - Type::void(), - Some(BnTypeError::Function(errors)), - ); - } - TranslateTypeResult::NotYet => { - return TranslateTypeResult::PartiallyTranslated(Type::void(), None) - } - }; - let mut partial_error_args = vec![]; - let mut bn_args = Vec::with_capacity(fun.args.len()); - for (i, (arg_name, arg_type, _arg_loc)) in fun.args.iter().enumerate() { - let arg = match self.translate_type(arg_type) { - TranslateTypeResult::Translated(trans) => trans, - TranslateTypeResult::PartiallyTranslated(trans, error) => { - is_partial = true; - if let Some(error) = error { - errors.args.push((i, error)); - } - trans - } - TranslateTypeResult::NotYet => { - return TranslateTypeResult::PartiallyTranslated(Type::void(), None) - } - TranslateTypeResult::Error(error) => { - partial_error_args.push((i, error)); - return TranslateTypeResult::PartiallyTranslated( - Type::void(), - Some(BnTypeError::Function(errors)), - ); - } - }; - // TODO create location from `arg_loc`? - let loc = None; - let name = arg_name.to_owned().unwrap_or_else(|| format!("arg_{i}")); - bn_args.push(FunctionParameter::new(arg, name, loc)); - } - - let ty = Type::function(&return_ty, &bn_args, false); - if is_partial { - let error = (errors.ret.is_some() || !errors.args.is_empty()) - .then(|| BnTypeError::Function(errors)); - TranslateTypeResult::PartiallyTranslated(ty, error) - } else { - assert!(errors.ret.is_none() && errors.args.is_empty()); - TranslateTypeResult::Translated(ty) - } - } - - fn translate_array(&self, array: &TILArray) -> TranslateTypeResult { - match self.translate_type(&*array.elem_type) { - TranslateTypeResult::NotYet => TranslateTypeResult::NotYet, - TranslateTypeResult::Translated(ty) => { - TranslateTypeResult::Translated(Type::array(&ty, array.nelem.into())) - } - TranslateTypeResult::PartiallyTranslated(ty, error) => { - TranslateTypeResult::PartiallyTranslated( - Type::array(&ty, array.nelem.into()), - error.map(Box::new).map(BnTypeError::Array), - ) - } - TranslateTypeResult::Error(error) => { - TranslateTypeResult::Error(BnTypeError::Array(Box::new(error))) - } - } - } - - fn translate_bitfields_into_struct( - &self, - offset: usize, - members_slice: &[TILStructMember], - struct_builder: &StructureBuilder, - ) { - if members_slice.is_empty() { - unreachable!() - } - let mut members = members_slice - .iter() - .map(|ty| match &ty.member_type { - TILType::Bitfield(b) => b, - _ => unreachable!(), - }) - .enumerate(); - let (_, first_field) = members.next().unwrap(); - let mut current_field_bytes = first_field.nbytes; - let mut current_field_bits: u32 = first_field.width.into(); - let mut start_idx = 0; - - let create_field = |start_idx, i, bytes| { - let name = if start_idx == i - 1 { - let member: &TILStructMember = &members_slice[i - 1]; - let name: &Option<String> = &member.name; - name.to_owned() - .unwrap_or_else(|| format!("bitfield_{}", offset + start_idx)) - } else { - format!("bitfield_{}_{}", offset + start_idx, offset + (i - 1)) - }; - let field = field_from_bytes(bytes); - struct_builder.append( - &field, - name, - BNMemberAccess::NoAccess, - BNMemberScope::NoScope, - ); - }; - - for (i, member) in members { - // starting a new field - let max_bits = u32::try_from(current_field_bytes).unwrap() * 8; - // this bitfield start a a new field, or can't contain other bitfields - // finish the previous and start a new - if current_field_bytes != member.nbytes - || max_bits < current_field_bits + u32::from(member.width) - { - create_field(start_idx, i, current_field_bytes); - current_field_bytes = member.nbytes; - current_field_bits = 0; - start_idx = i; - } - - // just add the current bitfield into the field - current_field_bits += u32::from(member.width); - } - - if current_field_bits != 0 { - create_field(start_idx, members_slice.len(), current_field_bytes); - } - } - - fn translate_struct( - &self, - members: &[TILStructMember], - effective_alignment: u16, - ) -> TranslateTypeResult { - if members.is_empty() { - // binary ninja crashes if you create an empty struct, because it divide by 0 - return TranslateTypeResult::Translated(Type::void()); - } - let mut is_partial = false; - let structure = StructureBuilder::new(); - structure.set_alignment(effective_alignment.into()); - - let mut errors = vec![]; - let mut first_bitfield_seq = None; - for (i, member) in members.iter().enumerate() { - match (&member.member_type, first_bitfield_seq) { - // accumulate the bitfield to be condensated - (TILType::Bitfield(_bit), None) => { - first_bitfield_seq = Some(i); - continue; - } - (TILType::Bitfield(_bit), Some(_)) => continue, - - // condensate the bitfields into byte-wide fields - (_, Some(start_idx)) => { - first_bitfield_seq = None; - let members_bitrange = &members[start_idx..i]; - self.translate_bitfields_into_struct(start_idx, members_bitrange, &structure); - } - - (_, None) => {} - } - - let mem = match self.translate_type(&member.member_type) { - TranslateTypeResult::Translated(ty) => ty, - TranslateTypeResult::PartiallyTranslated(partial_ty, error) => { - is_partial |= true; - if let Some(error) = error { - errors.push((i, error)); - } - partial_ty - } - TranslateTypeResult::NotYet => return TranslateTypeResult::NotYet, - TranslateTypeResult::Error(error) => { - errors.push((i, error)); - return TranslateTypeResult::Error(BnTypeError::Struct(errors)); - } - }; - let name = member - .name - .to_owned() - .unwrap_or_else(|| format!("member_{i}")); - structure.append(&mem, name, BNMemberAccess::NoAccess, BNMemberScope::NoScope); - } - if let Some(start_idx) = first_bitfield_seq { - let members_bitrange = &members[start_idx..]; - self.translate_bitfields_into_struct(start_idx, members_bitrange, &structure); - } - let bn_ty = Type::structure(&structure.finalize()); - if is_partial { - let partial_error = (!errors.is_empty()).then_some(BnTypeError::Struct(errors)); - TranslateTypeResult::PartiallyTranslated(bn_ty, partial_error) - } else { - assert!(errors.is_empty()); - TranslateTypeResult::Translated(bn_ty) - } - } - - fn translate_union( - &self, - members: &[(Option<String>, TILType)], - _effective_alignment: u16, - ) -> TranslateTypeResult { - let mut is_partial = false; - let structure = StructureBuilder::new(); - structure.set_structure_type(StructureType::UnionStructureType); - let mut errors = vec![]; - for (i, (member_name, member_type)) in members.iter().enumerate() { - // bitfields can be translated into complete fields - let mem = match member_type { - TILType::Bitfield(field) => field_from_bytes(field.nbytes), - member_type => match self.translate_type(member_type) { - TranslateTypeResult::Translated(ty) => ty, - TranslateTypeResult::Error(error) => { - errors.push((i, error)); - return TranslateTypeResult::Error(BnTypeError::Union(errors)); - } - TranslateTypeResult::NotYet => return TranslateTypeResult::NotYet, - TranslateTypeResult::PartiallyTranslated(partial, error) => { - is_partial |= true; - if let Some(error) = error { - errors.push((i, error)); - } - partial - } - }, - }; - - let name = member_name - .to_owned() - .unwrap_or_else(|| format!("member_{i}")); - structure.append(&mem, name, BNMemberAccess::NoAccess, BNMemberScope::NoScope); - } - let str_ref = structure.finalize(); - - let bn_ty = Type::structure(&str_ref); - if is_partial { - let partial_error = (!errors.is_empty()).then_some(BnTypeError::Struct(errors)); - TranslateTypeResult::PartiallyTranslated(bn_ty, partial_error) - } else { - assert!(errors.is_empty()); - TranslateTypeResult::Translated(bn_ty) - } - } - - fn translate_type(&self, ty: &TILType) -> TranslateTypeResult { - match &ty { - // types that are always translatable - TILType::Basic(meta) => TranslateTypeResult::Translated(translate_basic(meta)), - TILType::Bitfield(bit) => TranslateTypeResult::Translated(field_from_bytes(bit.nbytes)), - TILType::Enum(TILEnum::NonRef { - members, bytesize, .. - }) => TranslateTypeResult::Translated(translate_enum(members, *bytesize)), - TILType::Typedef(TILTypedef::Ordinal(ord)) => self - .find_typedef_by_ordinal((*ord).into()) - .unwrap_or_else(|| TranslateTypeResult::Error(BnTypeError::OrdinalNotFound(*ord))), - TILType::Typedef(TILTypedef::Name(name)) => { - self.find_typedef_by_name(name).unwrap_or_else(|| { - TranslateTypeResult::Error(BnTypeError::NameNotFound(name.to_owned())) - }) - } - - // may not be translatable imediatly, but the size is known and can be - // updated after alBasicers are finished - TILType::Union(TILUnion::Ref { ref_type, .. }) - | TILType::Struct(TILStruct::Ref { ref_type, .. }) - | TILType::Enum(TILEnum::Ref { ref_type, .. }) => self.translate_pointer(&**ref_type), - TILType::Pointer(ty) => self.translate_pointer(&ty.typ), - TILType::Function(fun) => self.translate_function(fun), - - // can only be partially solved if all fields are solved or partially solved - TILType::Array(array) => self.translate_array(array), - TILType::Struct(TILStruct::NonRef { - members, - effective_alignment, - .. - }) => self.translate_struct(members, *effective_alignment), - TILType::Union(TILUnion::NonRef { - members, - effective_alignment, - .. - }) => self.translate_union(members, *effective_alignment), - } - } -} - -fn find_typedef_named_type_class(ty: &TILType) -> NamedTypeReferenceClass { - match ty { - TILType::Typedef(_) => NamedTypeReferenceClass::TypedefNamedTypeClass, - TILType::Struct(_) => NamedTypeReferenceClass::StructNamedTypeClass, - TILType::Union(_) => NamedTypeReferenceClass::UnionNamedTypeClass, - TILType::Enum(_) => NamedTypeReferenceClass::EnumNamedTypeClass, - _ => NamedTypeReferenceClass::UnknownNamedTypeClass, - } -} - -fn field_from_bytes(bytes: i32) -> Ref<Type> { - match bytes { - 0 => unreachable!(), - num @ (1 | 2 | 4 | 8 | 16) => Type::int(num.try_into().unwrap(), false), - nelem => Type::array(&Type::char(), nelem.try_into().unwrap()), - } -} - -fn parse_til_section_info( +pub fn import_til_section( debug_info: &mut DebugInfo, debug_file: &BinaryView, - til: TILSection, + til: &TILSection, progress: impl Fn(usize, usize) -> Result<(), ()>, ) -> Result<()> { - let total = til.symbols.len() + til.types.len(); - let mut types = Vec::with_capacity(total); - let mut types_by_ord = HashMap::with_capacity(total); - let mut types_by_name = HashMap::with_capacity(total); - let all_types = til.types.iter().zip(core::iter::repeat(false)); - // TODO: it's unclear how the demangle symbols and types names/ord, for now only parse types - //let all_types = all_types.chain(til.symbols.iter().zip(core::iter::repeat(true))); - for (i, (ty, is_symbol)) in all_types.enumerate() { - // TODO sanitized the input - // TODO find out how the namespaces used by TIL works - let name = ty.name.to_owned(); - types.push(TranslatesIDBType { - name, - is_symbol, - og_ty: ty, - _class: find_typedef_named_type_class(&ty.tinfo), - ty: TranslateTypeResult::NotYet, - }); - if ty.ordinal != 0 && !is_symbol { - let dup1 = types_by_ord.insert(ty.ordinal, i); - if let Some(old) = dup1 { - let old_type = &types[old]; - let new_type = types.last().unwrap(); - // TODO error? - panic!( - "dup ord {}:{} {}:\n{:?}\n{:?}", - old_type.is_symbol, - new_type.is_symbol, - ty.ordinal, - &old_type.og_ty, - &new_type.og_ty, - ) - } - } - if ty.name != "" { - let dup2 = types_by_name.insert(ty.name.to_owned(), i); - if let Some(old) = dup2 { - let old_type = &types[old]; - let new_type = types.last().unwrap(); - // TODO error? - panic!( - "dup name {}:{}: {}:\n{:?}\n{:?}", - old_type.is_symbol, - new_type.is_symbol, - &ty.name, - &old_type.og_ty, - &new_type.og_ty, - ) - } - } - } - - let mut translator = TranslateIDBTypes { - debug_info, - _debug_file: debug_file, - arch: debug_file.default_arch().unwrap(/* TODO */), - progress, - til: &til, - types, - types_by_ord, - types_by_name, - }; - if (translator.progress)(0, total).is_err() { - warn!("IDB import aborted"); - return Ok(()); - } - - // solve types until there is nothing else being solved - loop { - // is something was solved, mark this variable as true - let mut did_something = false; - let mut num_translated = 0usize; - for i in 0..translator.types.len() { - match &translator.types[i].ty { - TranslateTypeResult::NotYet => { - let result = translator.translate_type(&translator.types[i].og_ty.tinfo); - did_something |= !matches!(&result, TranslateTypeResult::NotYet); - translator.types[i].ty = result; - // if originaly NotKnow and now translated, update the result on bn - match &translator.types[i].ty { - // ignore partial errors here, they will be printed below - TranslateTypeResult::PartiallyTranslated(bn_ty, _) - | TranslateTypeResult::Translated(bn_ty) => { - let name = &translator.types[i].name; - let success = - translator.debug_info.add_type(name, &bn_ty, &[/* TODO */]); - if !success { - error!("Unable to add type `{}`", name) - } - } - _ => {} - } - } - TranslateTypeResult::PartiallyTranslated(_, None) => { - let result = translator.translate_type(&translator.types[i].og_ty.tinfo); - assert!(!matches!(&result, TranslateTypeResult::NotYet)); - did_something |= - !matches!(&result, TranslateTypeResult::PartiallyTranslated(_, None)); - translator.types[i].ty = result; - // don't need to add again they will be fixed on the loop below - } - // if an error was produced, there is no point in try again - TranslateTypeResult::PartiallyTranslated(_, Some(_)) => {} - // NOTE for now we are just accumulating errors, just try to translate the max number - // of types as possible - TranslateTypeResult::Error(_) => {} - // already translated, nothing do to here - TranslateTypeResult::Translated(_) => {} - } - - // count the number of finished types - match &translator.types[i].ty { - TranslateTypeResult::Translated(_) => num_translated += 1, - _ => {} - } - } - - if !did_something { - // means we acomplilshed nothing during this loop, there is no point in trying again - break; - } - if (translator.progress)(num_translated, total).is_err() { - // error means the user aborted the progress - break; - } - } + let types = types::translate_til_types(debug_file.default_arch().unwrap(), til, progress)?; // print any errors - for ty in &translator.types { + for ty in &types { match &ty.ty { TranslateTypeResult::NotYet => { panic!( "type could not be processed `{}`: {:#?}", - &ty.name, &ty.og_ty + &String::from_utf8_lossy(&ty.name), + &ty.og_ty ); } TranslateTypeResult::Error(error) => { - error!("Unable to parse type `{}`: {error}", &ty.name); + error!( + "Unable to parse type `{}`: {error}", + &String::from_utf8_lossy(&ty.name) + ); } TranslateTypeResult::PartiallyTranslated(_, error) => { if let Some(error) = error { - error!("Unable to parse type `{}` correctly: {error}", &ty.name); + error!( + "Unable to parse type `{}` correctly: {error}", + &String::from_utf8_lossy(&ty.name) + ); } else { - warn!("Type `{}` maybe not be fully translated", &ty.name); + warn!( + "Type `{}` maybe not be fully translated", + &String::from_utf8_lossy(&ty.name) + ); } } TranslateTypeResult::Translated(_) => {} }; } + // add all type to binary ninja + for ty in &types { + if let TranslateTypeResult::Translated(bn_ty) + | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty + { + if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), &bn_ty, &[/* TODO */]) { + error!( + "Unable to add type `{}`", + &String::from_utf8_lossy(&ty.name) + ) + } + } + } + // add a second time to fix the references LOL - for ty in &translator.types { - match &ty.ty { - TranslateTypeResult::Translated(bn_ty) - | TranslateTypeResult::PartiallyTranslated(bn_ty, _) => { - let success = translator - .debug_info - .add_type(&ty.name, &bn_ty, &[/* TODO */]); - if !success { - error!("Unable to fix type `{}`", &ty.name) - } + for ty in &types { + if let TranslateTypeResult::Translated(bn_ty) + | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty + { + if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), &bn_ty, &[/* TODO */]) { + error!( + "Unable to fix type `{}`", + &String::from_utf8_lossy(&ty.name) + ) } - _ => {} } } @@ -876,134 +221,106 @@ fn parse_id0_section_info( debug_info: &mut DebugInfo, bv: &BinaryView, debug_file: &BinaryView, - id0: ID0Section, - progress: impl Fn(usize, usize) -> Result<(), ()>, + id0: &ID0Section, ) -> Result<()> { - // TODO verify the best way to add comments, try to use the debug_info instead - for fc in id0.functions_and_comments()? { - match fc? { - idb_rs::id0::FunctionsAndComments::Name => {} - // function will be create bellow using the entry points - idb_rs::id0::FunctionsAndComments::Function(_) => {} - idb_rs::id0::FunctionsAndComments::RepeatableComment { address, value } - | idb_rs::id0::FunctionsAndComments::Comment { address, value } => { - for function in &bv.functions_containing(address) { - function.set_comment_at(address, value); - } - } - idb_rs::id0::FunctionsAndComments::Unknown { .. } => {} - } - } + let version = match id0.ida_info()? { + idb_rs::id0::IDBParam::V1(IDBParam1 { version, .. }) + | idb_rs::id0::IDBParam::V2(IDBParam2 { version, .. }) => version, + }; - let entry_points = id0.entry_points()?; - let total = entry_points.len(); - for (i, entry_point) in entry_points.into_iter().enumerate() { - if progress(i, total).is_err() { - warn!("Aborted while adding the functions"); - break; + for (addr, info) in get_info(id0, version)? { + // just in case we change this struct in the future, this line will for us to review this code + // TODO merge this data with folder locations + let AddrInfo { + comments, + label, + ty, + } = info; + // TODO set comments to address here + for function in &bv.functions_containing(addr) { + function.set_comment_at( + addr, + String::from_utf8_lossy(&comments.join(&b"\n"[..])).to_string(), + ); } - // TODO handle entry_point.forwarded type currently on the til section - //if let Some(forwarded) = entry_point.forwarded { - // todo!() - //} - match (entry_point.entry_type, entry_point.forwarded) { - (None, _) => { - // TODO add label without type + + let bnty = ty + .as_ref() + .and_then(|ty| match translate_ephemeral_type(debug_file, &ty) { + TranslateTypeResult::Translated(result) => Some(result), + TranslateTypeResult::PartiallyTranslated(result, None) => { + warn!("Unable to fully translate the type at {addr:#x}"); + Some(result) + } + TranslateTypeResult::NotYet => { + error!("Unable to translate the type at {addr:#x}"); + None + } + TranslateTypeResult::PartiallyTranslated(_, Some(bn_type_error)) + | TranslateTypeResult::Error(bn_type_error) => { + error!("Unable to translate the type at {addr:#x}: {bn_type_error}",); + None + } + }); + + match (label, &ty, bnty) { + (_, Some(TILType::Function(_)), bnty) => { + if bnty.is_none() { + error!("Unable to convert the function type at {addr:#x}",) + } + if !debug_info.add_function(DebugFunctionInfo::new( + None, + None, + label.map(str::to_string), + bnty, + Some(addr), + None, + vec![], + vec![], + )) { + error!("Unable to add the function at {addr:#x}") + } } - // TODO handle forwarded types/functions - (Some(_ty), Some(_forw)) => {} - // regular type - (Some(ty), None) => { - let bnty = translate_ephemeral_type( - &mut *debug_info, - debug_file, - &ty, - entry_point.address, - ); - match (bnty, ty) { - // TODO handle types that can't be translated - (None, _) => {} - (Some(bnty), TILType::Function(_)) => { - if !debug_info.add_function(DebugFunctionInfo::new( - None, - None, - Some(entry_point.name), - Some(bnty), - Some(entry_point.address), - None, - vec![], - vec![], - )) { - error!("Unable to add the function at {:#x}", entry_point.address) - } - } - (Some(bnty), _) => { - if !debug_info.add_data_variable( - entry_point.address, - &bnty, - Some(entry_point.name), - &[], - ) { - error!("Unable to add the type at {:#x}", entry_point.address) - } + (_, Some(_ty), Some(bnty)) => { + if !debug_info.add_data_variable(addr, &bnty, label, &[]) { + error!("Unable to add the type at {addr:#x}") + } + } + (_, Some(_ty), None) => { + // TODO types come from the TIL sections, can we make all types be just NamedTypes? + error!("Unable to convert type {addr:#x}"); + // TODO how to add a label without a type associacted with it? + if let Some(name) = label { + if !debug_info.add_data_variable( + addr, + &binaryninja::types::Type::void(), + Some(name), + &[], + ) { + error!("Unable to add the label at {addr:#x}") } } } - } - } - Ok(()) -} + (Some(name), None, None) => { + // TODO how to add a label without a type associacted with it? + if !debug_info.add_data_variable( + addr, + &binaryninja::types::Type::void(), + Some(name), + &[], + ) { + error!("Unable to add the label at {addr:#x}") + } + } -fn translate_ephemeral_type( - debug_info: &mut DebugInfo, - debug_file: &BinaryView, - ty: &TILType, - address: u64, -) -> Option<Ref<Type>> { - // in case we need to translate types - let translator = TranslateIDBTypes { - debug_info: &mut *debug_info, - _debug_file: debug_file, - arch: debug_file.default_arch().unwrap(/* TODO */), - progress: |_, _| Ok(()), - // TODO it's unclear what to do here - til: &TILSection { - format: 12, - title: String::new(), - description: String::new(), - id: 0, - cm: 0, - def_align: 1, - symbols: vec![], - type_ordinal_numbers: None, - types: vec![], - size_i: 4.try_into().unwrap(), - size_b: 1.try_into().unwrap(), - sizes: None, - size_long_double: None, - macros: None, - is_universal: false, - }, - types: vec![], - types_by_ord: HashMap::new(), - types_by_name: HashMap::new(), - }; + // just comments at this address + (None, None, None) => {} - match translator.translate_type(ty) { - TranslateTypeResult::Translated(result) => Some(result), - TranslateTypeResult::PartiallyTranslated(_, None) | TranslateTypeResult::NotYet => { - error!("Unable to translate the type at {:#x}", address); - None - } - TranslateTypeResult::PartiallyTranslated(_, Some(bn_type_error)) - | TranslateTypeResult::Error(bn_type_error) => { - error!( - "Unable to translate the type at {:#x}: {bn_type_error}", - address - ); - None + (_, None, Some(_)) => unreachable!(), } } + + Ok(()) } #[allow(non_snake_case)] |
