summaryrefslogtreecommitdiff
path: root/rust/examples/idb_import/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/examples/idb_import/src')
-rw-r--r--rust/examples/idb_import/src/addr_info.rs57
-rw-r--r--rust/examples/idb_import/src/lib.rs344
-rw-r--r--rust/examples/idb_import/src/types.rs699
3 files changed, 0 insertions, 1100 deletions
diff --git a/rust/examples/idb_import/src/addr_info.rs b/rust/examples/idb_import/src/addr_info.rs
deleted file mode 100644
index 8d1b1c1b..00000000
--- a/rust/examples/idb_import/src/addr_info.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::Result;
-
-use idb_rs::id0::ID0Section;
-use idb_rs::til;
-
-#[derive(Default)]
-pub struct AddrInfo<'a> {
- // TODO does binja diferenciate comments types on the API?
- pub comments: Vec<&'a [u8]>,
- pub label: Option<&'a str>,
- // TODO make this a ref
- pub ty: Option<til::Type>,
-}
-
-pub fn get_info(id0: &ID0Section, version: u16) -> Result<HashMap<u64, AddrInfo<'_>>> {
- let mut addr_info: HashMap<u64, AddrInfo> = HashMap::new();
-
- // the old style comments, most likely empty on new versions
- let old_comments = id0.functions_and_comments()?.filter_map(|fc| {
- use idb_rs::id0::FunctionsAndComments::*;
- match fc {
- Err(e) => Some(Err(e)),
- Ok(Comment { address, comment }) => Some(Ok((address, comment))),
- Ok(Name | Function(_) | Unknown { .. }) => None,
- }
- });
- for old_comment in old_comments {
- let (addr, comment) = old_comment?;
- let comment = comment.message();
- addr_info.entry(addr).or_default().comments.push(comment);
- }
-
- // comments defined on the address information
- for info in id0.address_info(version)? {
- use idb_rs::id0::AddressInfo::*;
- let (addr, info) = info?;
- let entry = addr_info.entry(addr).or_default();
- match info {
- Comment(comments) => entry.comments.push(comments.message()),
- Label(name) => {
- if let Some(_old) = entry.label.replace(name) {
- panic!("Duplicated label for an address should be impossible this is most likelly a programing error")
- }
- }
- TilType(ty) => {
- if let Some(_old) = entry.ty.replace(ty) {
- panic!("Duplicated type for an address should be impossible this is most likelly a programing error")
- }
- }
- Other { .. } => {}
- }
- }
-
- Ok(addr_info)
-}
diff --git a/rust/examples/idb_import/src/lib.rs b/rust/examples/idb_import/src/lib.rs
deleted file mode 100644
index 48d1b8fd..00000000
--- a/rust/examples/idb_import/src/lib.rs
+++ /dev/null
@@ -1,344 +0,0 @@
-mod types;
-use types::*;
-mod addr_info;
-use addr_info::*;
-
-use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::debuginfo::{
- CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser,
-};
-
-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;
-use binaryninja::logger::Logger;
-
-struct IDBDebugInfoParser;
-impl CustomDebugInfoParser for IDBDebugInfoParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if let Some(project_file) = view.file().get_project_file() {
- project_file.name().as_str().ends_with(".i64")
- || project_file.name().as_str().ends_with(".idb")
- } else {
- view.file().filename().as_str().ends_with(".i64")
- || view.file().filename().as_str().ends_with(".idb")
- }
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- match parse_idb_info(debug_info, bv, debug_file, progress) {
- Ok(()) => true,
- Err(error) => {
- error!("Unable to parse IDB file: {error}");
- false
- }
- }
- }
-}
-
-struct TILDebugInfoParser;
-impl CustomDebugInfoParser for TILDebugInfoParser {
- fn is_valid(&self, view: &BinaryView) -> bool {
- if let Some(project_file) = view.file().get_project_file() {
- project_file.name().as_str().ends_with(".til")
- } else {
- view.file().filename().as_str().ends_with(".til")
- }
- }
-
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- _bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool {
- match parse_til_info(debug_info, debug_file, progress) {
- Ok(()) => true,
- Err(error) => {
- error!("Unable to parse TIL file: {error}");
- false
- }
- }
- }
-}
-
-struct BinaryViewReader<'a> {
- bv: &'a BinaryView,
- offset: u64,
-}
-impl std::io::Read for BinaryViewReader<'_> {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- if !self.bv.offset_valid(self.offset) {
- return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
- }
- let len = self.bv.read(buf, self.offset);
- self.offset += u64::try_from(len).unwrap();
- Ok(len)
- }
-}
-
-impl std::io::Seek for BinaryViewReader<'_> {
- fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
- let new_offset = match pos {
- std::io::SeekFrom::Start(offset) => Some(offset),
- std::io::SeekFrom::End(end) => u64::try_from(self.bv.len())
- .unwrap()
- .checked_add_signed(end),
- std::io::SeekFrom::Current(next) => self.offset.checked_add_signed(next),
- };
- let new_offset =
- new_offset.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))?;
- if !self.bv.offset_valid(new_offset) {
- return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
- }
- self.offset = new_offset;
- Ok(new_offset)
- }
-}
-
-fn parse_idb_info(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- trace!("Opening a IDB file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- trace!("Parsing a IDB file");
- let file = std::io::BufReader::new(file);
- let mut parser = idb_rs::IDBParser::new(file)?;
- if let Some(til_section) = parser.til_section_offset() {
- trace!("Parsing the TIL section");
- let til = parser.read_til_section(til_section)?;
- // progress 0%-50%
- 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)?;
- }
-
- Ok(())
-}
-
-fn parse_til_info(
- debug_info: &mut DebugInfo,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- trace!("Opening a TIL file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- let file = std::io::BufReader::new(file);
- trace!("Parsing the TIL section");
- let til = TILSection::parse(file)?;
- import_til_section(debug_info, debug_file, &til, progress)
-}
-
-pub fn import_til_section(
- debug_info: &mut DebugInfo,
- debug_file: &BinaryView,
- til: &TILSection,
- progress: impl Fn(usize, usize) -> Result<(), ()>,
-) -> Result<()> {
- let types = types::translate_til_types(debug_file.default_arch().unwrap(), til, progress)?;
-
- // print any errors
- for ty in &types {
- match &ty.ty {
- TranslateTypeResult::NotYet => {
- panic!(
- "type could not be processed `{}`: {:#?}",
- &String::from_utf8_lossy(&ty.name),
- &ty.og_ty
- );
- }
- TranslateTypeResult::Error(error) => {
- 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}",
- &String::from_utf8_lossy(&ty.name)
- );
- } else {
- 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 &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)
- )
- }
- }
- }
-
- Ok(())
-}
-
-fn parse_id0_section_info(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- id0: &ID0Section,
-) -> Result<()> {
- let version = match id0.ida_info()? {
- idb_rs::id0::IDBParam::V1(IDBParam1 { version, .. })
- | idb_rs::id0::IDBParam::V2(IDBParam2 { version, .. }) => version,
- };
-
- 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(),
- );
- }
-
- 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}")
- }
- }
- (_, 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}")
- }
- }
- }
- (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}")
- }
- }
-
- // just comments at this address
- (None, None, None) => {}
-
- (_, None, Some(_)) => unreachable!(),
- }
- }
-
- Ok(())
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn CorePluginInit() -> bool {
- Logger::new("IDB Import")
- .with_level(LevelFilter::Error)
- .init();
- DebugInfoParser::register("IDB Parser", IDBDebugInfoParser);
- DebugInfoParser::register("TIL Parser", TILDebugInfoParser);
- true
-}
diff --git a/rust/examples/idb_import/src/types.rs b/rust/examples/idb_import/src/types.rs
deleted file mode 100644
index c614634b..00000000
--- a/rust/examples/idb_import/src/types.rs
+++ /dev/null
@@ -1,699 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::{anyhow, Result};
-use binaryninja::architecture::CoreArchitecture;
-use binaryninja::binaryninjacore_sys::{BNMemberAccess, BNMemberScope};
-use binaryninja::binaryview::{BinaryView, BinaryViewExt};
-use binaryninja::rc::Ref;
-use binaryninja::types::{
- Conf, EnumerationBuilder, FunctionParameter, StructureBuilder, StructureType, Type,
-};
-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,
-};
-
-#[derive(Debug, Clone)]
-pub 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(BnTypeFunctionError),
- Array(Box<BnTypeError>),
- Pointer(Box<BnTypeError>),
- /// Error for members
- Struct(Vec<(usize, BnTypeError)>),
- Union(Vec<(usize, BnTypeError)>),
-}
-
-#[derive(Default, Debug, Clone)]
-pub struct BnTypeFunctionError {
- pub ret: Option<Box<BnTypeError>>,
- pub 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(BnTypeFunctionError { 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}"),
- }
- }
-}
-
-#[derive(Default)]
-pub 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),
- }
- }
-}
-
-pub struct TranslatesIDBType<'a> {
- // sanitized name from IDB
- pub name: Vec<u8>,
- // the result, if converted
- pub ty: TranslateTypeResult,
- pub og_ty: &'a TILTypeInfo,
- pub is_symbol: bool,
-}
-
-pub struct TranslateIDBTypes<'a, F: Fn(usize, usize) -> Result<(), ()>> {
- pub arch: CoreArchitecture,
- pub progress: F,
- pub til: &'a TILSection,
- // note it's mapped 1:1 with the same index from til types.chain(symbols)
- pub types: Vec<TranslatesIDBType<'a>>,
- // ordinals with index to types
- pub types_by_ord: HashMap<u64, usize>,
- // original names with index to types
- pub types_by_name: HashMap<Vec<u8>, 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: &[u8]) -> 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 {
- b"Unkown" | b"uint8_t" => Some(TranslateTypeResult::Translated(Type::int(1, false))),
- b"IUnkown" | b"int8_t" => Some(TranslateTypeResult::Translated(Type::int(1, true))),
- b"SHORT" | b"USHORT" => Some(TranslateTypeResult::Translated(Type::int(
- self.til.size_short.get() as usize,
- name == b"SHORT",
- ))),
- b"int16_t" => Some(TranslateTypeResult::Translated(Type::int(2, true))),
- b"uint16_t" => Some(TranslateTypeResult::Translated(Type::int(2, false))),
- b"int32_t" => Some(TranslateTypeResult::Translated(Type::int(4, true))),
- b"uint32_t" => Some(TranslateTypeResult::Translated(Type::int(4, false))),
- b"int64_t" => Some(TranslateTypeResult::Translated(Type::int(8, true))),
- b"uint64_t" => Some(TranslateTypeResult::Translated(Type::int(8, false))),
- b"int128_t" => Some(TranslateTypeResult::Translated(Type::int(16, true))),
- b"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(&String::from_utf8_lossy(&ty.name), 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(&String::from_utf8_lossy(&ty.name), 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: BnTypeFunctionError = 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
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .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 condensate_bitfields_from_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];
- member
- .name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .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.condensate_bitfields_from_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
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .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.condensate_bitfields_from_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<Vec<u8>>, 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
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .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_enum(members: &[(Option<Vec<u8>>, u64)], bytesize: u64) -> Ref<Type> {
- let eb = EnumerationBuilder::new();
- for (i, (name, bytesize)) in members.iter().enumerate() {
- let name = name
- .as_ref()
- .map(|name| String::from_utf8_lossy(name).to_string())
- .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()),
- }
- }
-
- pub fn translate_type(&self, ty: &TILType) -> TranslateTypeResult {
- match &ty {
- // types that are always translatable
- TILType::Basic(meta) => TranslateTypeResult::Translated(Self::translate_basic(meta)),
- TILType::Bitfield(bit) => TranslateTypeResult::Translated(field_from_bytes(bit.nbytes)),
- TILType::Enum(TILEnum::NonRef {
- members, bytesize, ..
- }) => TranslateTypeResult::Translated(Self::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(
- String::from_utf8_lossy(name).to_string(),
- ))
- })
- }
-
- // 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),
- }
- }
-}
-
-pub fn translate_ephemeral_type(debug_file: &BinaryView, ty: &TILType) -> TranslateTypeResult {
- // in case we need to translate types
- let translator = TranslateIDBTypes {
- arch: debug_file.default_arch().unwrap(/* TODO */),
- progress: |_, _| Ok(()),
- // TODO it's unclear what to do here
- til: &TILSection {
- format: 12,
- title: Vec::new(),
- description: Vec::new(),
- id: 0,
- cm: 0,
- def_align: 1,
- symbols: vec![],
- type_ordinal_alias: None,
- types: vec![],
- size_i: 4.try_into().unwrap(),
- size_b: 1.try_into().unwrap(),
- size_short: 2.try_into().unwrap(),
- size_long: 4.try_into().unwrap(),
- size_long_long: 8.try_into().unwrap(),
- size_long_double: None,
- macros: None,
- is_universal: false,
- },
- types: vec![],
- types_by_ord: HashMap::new(),
- types_by_name: HashMap::new(),
- };
-
- translator.translate_type(ty)
-}
-
-pub fn translate_til_types(
- arch: CoreArchitecture,
- til: &TILSection,
- progress: impl Fn(usize, usize) -> Result<(), ()>,
-) -> Result<Vec<TranslatesIDBType>> {
- 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,
- 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.is_empty() {
- 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,
- &String::from_utf8_lossy(&ty.name),
- &old_type.og_ty,
- &new_type.og_ty,
- )
- }
- }
- }
-
- let mut translator = TranslateIDBTypes {
- arch,
- progress,
- til,
- types,
- types_by_ord,
- types_by_name,
- };
- if (translator.progress)(0, total).is_err() {
- return Err(anyhow!("IDB import aborted"));
- }
-
- // solve types until there is nothing else that can be solved
- loop {
- // if 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;
- }
- TranslateTypeResult::PartiallyTranslated(_, None) => {
- let result = translator.translate_type(&translator.types[i].og_ty.tinfo);
- // don't allow regress, it can goes from PartiallyTranslated to any state other then NotYet
- 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
- if let TranslateTypeResult::Translated(_) = &translator.types[i].ty {
- 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;
- }
- }
-
- Ok(translator.types)
-}
-
-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()),
- }
-}