From 5852db2fc206981815cca28d4c9df64262549b1d Mon Sep 17 00:00:00 2001 From: Yan Date: Wed, 24 Apr 2024 14:20:53 -0700 Subject: [PATCH 1/3] preliminary UI for decompilation inlining --- angrmanagement/ui/views/code_view.py | 1 + angrmanagement/ui/widgets/qdecomp_options.py | 31 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/angrmanagement/ui/views/code_view.py b/angrmanagement/ui/views/code_view.py index ebfb0ee1d9..1ed05a6952 100644 --- a/angrmanagement/ui/views/code_view.py +++ b/angrmanagement/ui/views/code_view.py @@ -152,6 +152,7 @@ def decomp() -> None: options=self._options.option_and_values, optimization_passes=self._options.selected_passes, peephole_optimizations=self._options.selected_peephole_opts, + inline_functions=self._options.selected_inlines, vars_must_struct=self.vars_must_struct, on_finish=decomp_ready, blocking=True, diff --git a/angrmanagement/ui/widgets/qdecomp_options.py b/angrmanagement/ui/widgets/qdecomp_options.py index fdb3942d01..1036ee4662 100644 --- a/angrmanagement/ui/widgets/qdecomp_options.py +++ b/angrmanagement/ui/widgets/qdecomp_options.py @@ -21,6 +21,7 @@ class OptionType: OPTION = 1 OPTIMIZATION_PASS = 2 PEEPHOLE_OPTIMIZATION = 3 + INLINED_FUNCTION = 3 class QDecompilationOption(QTreeWidgetItem): @@ -69,6 +70,11 @@ def state(self): else: return bool(self.checkState(0) == Qt.CheckState.Checked) +class FunctionInlineOption: + def __init__(self, func): + self.NAME = func.name + self.function = func + self.DESCRIPTION = f"Inline the {self.NAME} function during decompilation." class QDecompilationOptions(QWidget): """ @@ -98,6 +104,7 @@ def __init__(self, code_view, instance: Instance) -> None: self._qoptions = [] self._qoptipasses = [] self._qpeephole_opts = [] + self._qinlines = [ ] self._init_widgets() @@ -149,6 +156,14 @@ def selected_peephole_opts(self): selected.append(item.option) return selected + @property + def selected_inlines(self): + selected = [] + for item in self._qinlines: + if item.state: + selected.append(item.option.function) + return selected + @property def option_and_values(self): return [(item.option, item.state) for item in self._qoptions] @@ -201,6 +216,7 @@ def _reload_options(self, reset_values: bool = False) -> None: vals_options = dict(self.option_and_values) vals_peephole = self.selected_peephole_opts vals_passes = self.selected_passes + vals_inlines = self.selected_inlines self._treewidget.clear() self._qoptions.clear() @@ -239,6 +255,21 @@ def _reload_options(self, reset_values: bool = False) -> None: w = QDecompilationOption(po_category, opt_, OptionType.PEEPHOLE_OPTIMIZATION, enabled=enabled) self._qpeephole_opts.append(w) + inlining_category = QTreeWidgetItem(self._treewidget, ["Inlined Functions"]) + categories["inlining_opts"] = inlining_category + if not self._instance.project.am_none: + for func in self._instance.project.kb.functions.values(): + if func.is_plt or func.is_simprocedure: + continue + enabled = False if reset_values else func in vals_inlines + w = QDecompilationOption( + inlining_category, + FunctionInlineOption(func), + OptionType.INLINED_FUNCTION, + enabled=enabled + ) + self._qinlines.append(w) + # expand all self._treewidget.expandAll() From fb63398486ffadeeeeb1fb0d5ee24f65df2c4799 Mon Sep 17 00:00:00 2001 From: Yan Date: Thu, 25 Apr 2024 14:21:11 -0700 Subject: [PATCH 2/3] display only reachable functions in the inlining decompilation options --- angrmanagement/ui/views/code_view.py | 1 + angrmanagement/ui/widgets/qdecomp_options.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/angrmanagement/ui/views/code_view.py b/angrmanagement/ui/views/code_view.py index 1ed05a6952..3d8e9fdbd2 100644 --- a/angrmanagement/ui/views/code_view.py +++ b/angrmanagement/ui/views/code_view.py @@ -341,6 +341,7 @@ def _on_new_function( self.decompile(focus=focus, focus_addr=focus_addr, flavor=flavor) self._last_function = self._function.am_obj + self._options.reload() console_view = self.workspace.view_manager.first_view_in_category("console") if console_view is not None: console_view.set_current_function(self._function.am_obj) diff --git a/angrmanagement/ui/widgets/qdecomp_options.py b/angrmanagement/ui/widgets/qdecomp_options.py index 1036ee4662..70fa96796e 100644 --- a/angrmanagement/ui/widgets/qdecomp_options.py +++ b/angrmanagement/ui/widgets/qdecomp_options.py @@ -216,12 +216,13 @@ def _reload_options(self, reset_values: bool = False) -> None: vals_options = dict(self.option_and_values) vals_peephole = self.selected_peephole_opts vals_passes = self.selected_passes - vals_inlines = self.selected_inlines + vals_inlines = set(self.selected_inlines) self._treewidget.clear() self._qoptions.clear() self._qoptipasses.clear() self._qpeephole_opts.clear() + self._qinlines.clear() categories = {} @@ -258,18 +259,26 @@ def _reload_options(self, reset_values: bool = False) -> None: inlining_category = QTreeWidgetItem(self._treewidget, ["Inlined Functions"]) categories["inlining_opts"] = inlining_category if not self._instance.project.am_none: - for func in self._instance.project.kb.functions.values(): - if func.is_plt or func.is_simprocedure: + for function in self._instance.project.kb.functions.values(): + if function.is_plt or function.is_simprocedure: continue - enabled = False if reset_values else func in vals_inlines + enabled = False if reset_values else function in vals_inlines w = QDecompilationOption( inlining_category, - FunctionInlineOption(func), + FunctionInlineOption(function), OptionType.INLINED_FUNCTION, enabled=enabled ) self._qinlines.append(w) + if not self._code_view.function.am_none: + reachable_functions = set(self._code_view.function.functions_reachable()) + for w in self._qinlines: + w.setHidden( + w.option.function == self._code_view.function or + w.option.function not in reachable_functions + ) + # expand all self._treewidget.expandAll() From 0bc6a257cce41089ff8951bc84fd3706586be560 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:30:15 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- angrmanagement/ui/widgets/qdecomp_options.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/angrmanagement/ui/widgets/qdecomp_options.py b/angrmanagement/ui/widgets/qdecomp_options.py index 70fa96796e..841c875046 100644 --- a/angrmanagement/ui/widgets/qdecomp_options.py +++ b/angrmanagement/ui/widgets/qdecomp_options.py @@ -70,12 +70,14 @@ def state(self): else: return bool(self.checkState(0) == Qt.CheckState.Checked) + class FunctionInlineOption: def __init__(self, func): self.NAME = func.name self.function = func self.DESCRIPTION = f"Inline the {self.NAME} function during decompilation." + class QDecompilationOptions(QWidget): """ The widget for selecting values for decompilation options. Will synchronize its status back to its parent (passed @@ -104,7 +106,7 @@ def __init__(self, code_view, instance: Instance) -> None: self._qoptions = [] self._qoptipasses = [] self._qpeephole_opts = [] - self._qinlines = [ ] + self._qinlines = [] self._init_widgets() @@ -264,10 +266,7 @@ def _reload_options(self, reset_values: bool = False) -> None: continue enabled = False if reset_values else function in vals_inlines w = QDecompilationOption( - inlining_category, - FunctionInlineOption(function), - OptionType.INLINED_FUNCTION, - enabled=enabled + inlining_category, FunctionInlineOption(function), OptionType.INLINED_FUNCTION, enabled=enabled ) self._qinlines.append(w) @@ -275,8 +274,7 @@ def _reload_options(self, reset_values: bool = False) -> None: reachable_functions = set(self._code_view.function.functions_reachable()) for w in self._qinlines: w.setHidden( - w.option.function == self._code_view.function or - w.option.function not in reachable_functions + w.option.function == self._code_view.function or w.option.function not in reachable_functions ) # expand all