[文档]classSwitchReusedEntryRewriter(OptimizationPass):""" For each switch-case construct (identified by jump tables), rewrite the entry into a goto block when we detect situations where an entry node is reused by edges in switch-case constructs that are not the current one. This code reuse is usually caused by compiler code deduplication. """ARCHES=NonePLATFORMS=NoneSTAGE=OptimizationPassStage.AFTER_AIL_GRAPH_CREATIONNAME="Rewrite switch-case entry nodes with multiple predecessors into goto statements."DESCRIPTION=__doc__.strip()
def_check(self):jumptables=self.kb.cfgs.get_most_accurate().jump_tablesswitch_jump_block_addrs={jumptable.addrforjumptableinjumptables.values()ifjumptable.typein{IndirectJumpType.Jumptable_AddressComputed,IndirectJumpType.Jumptable_AddressLoadedFromMemory}}jump_node_addrs=self._func.block_addrs_set.intersection(switch_jump_block_addrs)ifnotjump_node_addrs:returnFalse,None# ensure each jump table entry node has only one predecessorreused_entries:dict[Block,set[Block]]={}forjumptableinjumptables.values():forentry_addrinsorted(set(jumptable.jumptable_entries)):entry_nodes=self._get_blocks(entry_addr)forentry_nodeinentry_nodes:preds=list(self._graph.predecessors(entry_node))iflen(preds)>1:non_current_jumptable_preds=[predforpredinpredsifpred.addr!=jumptable.addr]ifany(p.addrinswitch_jump_block_addrsforpinnon_current_jumptable_preds):reused_entries[entry_node]={predforpredinpredsifpred.addrinswitch_jump_block_addrs}ifnotreused_entries:returnFalse,Nonecache={"reused_entries":reused_entries}returnTrue,cachedef_analyze(self,cache=None):reused_entries:dict[Block,set[Block]]=cache["reused_entries"]out_graph=Noneforentry_node,pred_nodesinreused_entries.items():# we assign the entry node to the predecessor with the lowest addresssorted_pred_nodes=sorted(pred_nodes,key=lambdax:(x.addr,x.idx))forhead_nodeinsorted_pred_nodes[1:]:# create the new goto nodegoto_stmt=Jump(None,Const(None,None,entry_node.addr,self.project.arch.bits,ins_addr=entry_node.addr),target_idx=entry_node.idx,ins_addr=entry_node.addr,)goto_node=Block(entry_node.addr,0,statements=[goto_stmt],idx=next(self.node_idx),)ifout_graphisNone:out_graph=self._graphout_graph.remove_edge(head_node,entry_node)out_graph.add_edge(head_node,goto_node)# we are virtualizing these edges, so we don't need to add the edge from goto_node to the entry_nodeself.out_graph=out_graph