angr.analyses.decompiler.optimization_passes.duplication_reverter.ail_merge_graph

class angr.analyses.decompiler.optimization_passes.duplication_reverter.ail_merge_graph.AILBlockSplit

Bases: object

This class represents a block that has been split into three parts, which is best explained in the AILMergeGraph class example. See that class for more information.

The up_split, is all statements above the matched Longest Common Sequence (LCS), the match_split is the LCS, and the down_split is all statements below the matched LCS. This class should only be used in the context of the AILMergeGraph class.

__init__(original=None, up_split=None, match_split=None, down_split=None)
Parameters:
  • original

  • up_split – The block split above the matched LCS

  • match_split – The block containing only the matched LCS

  • down_split – The block split below the matched LCS

classmethod from_block_lcs(original_block, idx, len_)
Parameters:

original_block (Block)

static split_ail_block(block, split_idx, split_len)
Return type:

tuple[Block | None, Block | None, Block | None]

class angr.analyses.decompiler.optimization_passes.duplication_reverter.ail_merge_graph.AILMergeGraph

Bases: object

This class represents the merged results of two AIL graphs that have been found to be similar. We can reference these graphs as G1 and G2. The two graphs are of the following form, where any node other than D can be empty:

  A
 /           B   C
\  /
 D
/          E   F

The D node can be a subgraph, but in both G1 and G2, this D-subgraph are exact duplicates of each other, except their top and bottom statements. This class is the result of merging those two D subgraphs.

To explain that last part about statements differing at the ends, see this example:

D1:
-----
a = 10;
puts(a);
puts("bye");
-----

D2:
-----
a = 11;
puts(a);
puts("cya");
-----

In this case, the merged D would contain just puts(a). The statements above it, referred to as up_split in the code, and the statements below it, referred to as down_split in the code, would be moved out of the block and bounded by the conditions that lead to those statements. This creates a graph even in the case of a single block being the original D.

Lastly, since this class will deal a lot with splitting blocks into pieces, we keep a mapping of how the original blocks turned into the new ones and vice versa.

__init__(graph=None, original_graph=None, conditional_block=None, original_blocks=None, original_split_blocks=None)
create_conditionless_graph(starting_blocks, graph_lcs)
Return type:

dict[Block, Block] | None

Parameters:

starting_blocks (list[Block])

add_edges_to_condition(conditional_block, true_target, merge_end_pairs)
create_mapping_to_merge_graph(updated_blocks, start_blocks)
Parameters:
merged_is_split_type(merge_block, split_type)
Parameters:
static clone_graph_replace_splits(graph, split_map)
Parameters: