Skip to content

Commit

Permalink
Working type deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
mahaloz committed Aug 25, 2024
1 parent dd284ae commit 104acaa
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 6 deletions.
28 changes: 28 additions & 0 deletions libbs/decompilers/ida/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
idaapi.BWN_FUNCS: "functions",
idaapi.BWN_STRUCTS: "structs",
idaapi.BWN_ENUMS: "enums",
idaapi.BWN_TILIST: "types",
}

FUNC_FORMS = {"decompilation", "disassembly"}
Expand Down Expand Up @@ -183,6 +184,32 @@ def get_types(structs=True, enums=True, typedefs=True) -> typing.Dict[str, Artif
return types


@execute_write
def get_ord_to_type_names() -> typing.Dict[int, typing.Tuple[str, typing.Type[Artifact]]]:
idati = idaapi.get_idati()
ord_to_name = {}
for ord_num in range(ida_typeinf.get_ordinal_qty(idati)):
tif = ida_typeinf.tinfo_t()
success = tif.get_numbered_type(idati, ord_num)
if not success:
continue

type_name = tif.get_type_name()
if tif.is_typedef():
type_type = Typedef
elif tif.is_struct():
type_type = Struct
elif tif.is_enum():
type_type = Enum
else:
type_type = None

if type_name:
ord_to_name[ord_num] = (type_name, type_type)

return ord_to_name


def get_ida_type(ida_ord=None, name=None):
tif = ida_typeinf.tinfo_t()
idati = idaapi.get_idati()
Expand Down Expand Up @@ -1274,6 +1301,7 @@ def get_decompiler_version() -> typing.Optional[Version]:

return vers


def view_to_bs_context(view, get_var=True) -> typing.Optional[Context]:
form_type = idaapi.get_widget_type(view)
if form_type is None:
Expand Down
39 changes: 33 additions & 6 deletions libbs/decompilers/ida/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,54 @@ def __init__(self, interface):
self.interface: "IDAInterface" = interface
self._seen_function_prototypes = {}

def bs_type_deleted(self, ordinal):
old_name, old_type = self.interface.cached_ord_to_type_names[ordinal]
if old_type == Struct:
self.interface.struct_changed(Struct(old_name, -1, members={}), deleted=True)
elif old_type == Enum:
self.interface.enum_changed(Enum(old_name, members={}), deleted=True)
elif old_type == Typedef:
self.interface.typedef_changed(Typedef(nam=old_name), deleted=True)

del self.interface.cached_ord_to_type_names[ordinal]

def local_types_changed(self, ltc, ordinal, name):
# this can't be a decorator for this function due to how IDA implements these overrides
if not self.interface.should_watch_artifacts():
return 0

tif = compat.get_ida_type(ida_ord=ordinal, name=name)
# was the type deleted?
if tif is None:
if ltc == ida_idp.LTC_DELETED and ordinal in self.interface.cached_ord_to_type_names:
self.bs_type_deleted(ordinal)

return 0

# first need to eliminate the typedefs
# was the type renamed?
if ordinal in self.interface.cached_ord_to_type_names:
old_name, _ = self.interface.cached_ord_to_type_names[ordinal]
if old_name != name:
self.bs_type_deleted(ordinal)

# at this point, the type is either completely new or renamed from an existing type.
# in either case we need to just gather the new info and trigger an update
#
# check if it's a typedef first since this these can also trigger strucs
is_typedef, name, type_name = compat.typedef_info(tif, use_new_check=True)
new_type_type = None
if is_typedef:
self.interface.typedef_changed(Typedef(nam=name, type_=type_name))
return 0

# TODO: refactor this more for new API in 9
# now it could be a struct or an enum
if tif.is_struct():
new_type_type = Typedef
elif tif.is_struct():
bs_struct = compat.bs_struct_from_tif(tif)
self.interface.struct_changed(bs_struct)
new_type_type = Struct
elif tif.is_enum():
# TODO: handle enum changes in IDA 9
pass

self.interface.cached_ord_to_type_names[ordinal] = (name, new_type_type)
return 0

@while_should_watch
Expand Down
4 changes: 4 additions & 0 deletions libbs/decompilers/ida/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self, **kwargs):
self._artifact_watcher_hooks = []
self._gui_active_context = None
self._deleted_artifacts = defaultdict(set)
self.cached_ord_to_type_names = {}

super().__init__(
name="ida", qt_version="PyQt5", artifact_lifter=IDAArtifactLifter(self),
Expand Down Expand Up @@ -209,6 +210,9 @@ def start_artifact_watchers(self):
idb_hook = IDBHooks(self)
if self.dec_version < Version("8.4"):
idb_hook.local_types_changed = lambda: 0
else:
# this code in this block must exist in 9.0, so don't delete it!
self.cached_ord_to_type_names = compat.get_ord_to_type_names()

self._artifact_watcher_hooks = [
idb_hook,
Expand Down

0 comments on commit 104acaa

Please sign in to comment.