(self)
Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
        each function.

        Although Function objects are crated during the CFG recovery, they are neither sound nor accurate. With a
        pre-constructed CFG, this method rebuilds all functions bearing the following rules:

            - A block may only belong to one function.
            - Small functions lying inside the startpoint and the endpoint of another function will be merged with the
              other function
            - Tail call optimizations are detected.
            - PLT stubs are aligned by 16.

        :return: None
Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
        each function.
Revisit the entire control flow graph create Function instances accordingly and correctly put blocks into each function .
def make_functions(self):
        """
        Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
        each function.

        Although Function objects are crated during the CFG recovery, they are neither sound nor accurate. With a
        pre-constructed CFG, this method rebuilds all functions bearing the following rules:

            - A block may only belong to one function.
            - Small functions lying inside the startpoint and the endpoint of another function will be merged with the
              other function
            - Tail call optimizations are detected.
            - PLT stubs are aligned by 16.

        :return: None
        """

        # TODO: Is it required that PLT stubs are always aligned by 16? If so, on what architectures and platforms is it
        # TODO:  enforced?

        tmp_functions = self.kb.functions.copy()

        for function in tmp_functions.values():
            function.mark_nonreturning_calls_endpoints()

        # Clear old functions dict
        self.kb.functions.clear()

        blockaddr_to_function = { }
        traversed_cfg_nodes = set()

        function_nodes = set()

        # Find nodes for beginnings of all functions
        for _, dst, data in self.graph.edges(data=True):
            jumpkind = data.get('jumpkind', "")
            if jumpkind == 'Ijk_Call' or jumpkind.startswith('Ijk_Sys'):
                function_nodes.add(dst)

        entry_node = self.model.get_any_node(self._binary.entry)
        if entry_node is not None:
            function_nodes.add(entry_node)

        # aggressively remove and merge functions
        # For any function, if there is a call to it, it won't be removed
        called_function_addrs = { n.addr for n in function_nodes }
        # Any function addresses that appear as symbols won't be removed
        predetermined_function_addrs = called_function_addrs | self._function_addresses_from_symbols

        removed_functions_a = self._process_irrational_functions(tmp_functions,
                                                                 predetermined_function_addrs,
                                                                 blockaddr_to_function
                                                                 )
        removed_functions_b, adjusted_cfgnodes = self._process_irrational_function_starts(tmp_functions,
                                                                                          predetermined_function_addrs,
                                                                                          blockaddr_to_function
                                                                                          )
        self._process_jump_table_targeted_functions(tmp_functions,
                                                    predetermined_function_addrs,
                                                    blockaddr_to_function,
                                                    )
        removed_functions = removed_functions_a | removed_functions_b

        # Remove all nodes that are adjusted
        function_nodes.difference_update(adjusted_cfgnodes)
        for n in self.graph.nodes():
            if n.addr in tmp_functions or n.addr in removed_functions:
                function_nodes.add(n)

        # traverse the graph starting from each node, not following call edges
        # it's important that we traverse all functions in order so that we have a greater chance to come across
        # rational functions before its irrational counterparts (e.g. due to failed jump table resolution)

        min_stage_2_progress = 50.0
        max_stage_2_progress = 90.0
        nodes_count = len(function_nodes)
        for i, fn in enumerate(sorted(function_nodes, key=lambda n: n.addr)):

            if self._low_priority:
                self._release_gil(i, 20)

            if self._show_progressbar or self._progress_callback:
                progress = min_stage_2_progress + (max_stage_2_progress - min_stage_2_progress) * (i * 1. / nodes_count)
                self._update_progress(progress)

            self._graph_bfs_custom(self.graph, [ fn ], self._graph_traversal_handler, blockaddr_to_function,
                                   tmp_functions, traversed_cfg_nodes
                                   )

        # Don't forget those small function chunks that are not called by anything.
        # There might be references to them from data, or simply references that we cannot find via static analysis

        secondary_function_nodes = set()
        # add all function chunks ("functions" that are not called from anywhere)
        for func_addr in tmp_functions:
            node = self.model.get_any_node(func_addr)
            if node is None:
                continue
            if node.addr not in blockaddr_to_function:
                secondary_function_nodes.add(node)

        missing_cfg_nodes = set(self.graph.nodes()) - traversed_cfg_nodes
        missing_cfg_nodes = { node for node in missing_cfg_nodes if node.function_address is not None }
        if missing_cfg_nodes:
            l.debug('%d CFGNodes are missing in the first traversal.', len(missing_cfg_nodes))
            secondary_function_nodes |=  missing_cfg_nodes

        min_stage_3_progress = 90.0
        max_stage_3_progress = 99.9

        nodes_count = len(secondary_function_nodes)
        for i, fn in enumerate(sorted(secondary_function_nodes, key=lambda n: n.addr)):

            if self._show_progressbar or self._progress_callback:
                progress = min_stage_3_progress + (max_stage_3_progress - min_stage_3_progress) * (i * 1. / nodes_count)
                self._update_progress(progress)

            self._graph_bfs_custom(self.graph, [fn], self._graph_traversal_handler, blockaddr_to_function,
                                   tmp_functions
                                   )

        to_remove = set()

        # Remove all stubs after PLT entries
        if not is_arm_arch(self.project.arch):
            to_remove |= self._remove_dummy_plt_stubs(self.kb.functions)

        # remove empty functions
        for func in self.kb.functions.values():
            if func.startpoint is None:
                to_remove.add(func.addr)

        for addr in to_remove:
            del self.kb.functions[addr]

        # Update CFGNode.function_address
        for node in self._nodes.values():
            if node.addr in blockaddr_to_function:
                node.function_address = blockaddr_to_function[node.addr].addr

        # Update function.info
        for func in self.kb.functions.values():
            if func.addr in tmp_functions:
                func.info = tmp_functions[func.addr].info
def make_functions ( self ) : # TODO: Is it required that PLT stubs are always aligned by 16? If so, on what architectures and platforms is it # TODO:  enforced? tmp_functions = self . kb . functions . copy ( ) for function in tmp_functions . values ( ) : function . mark_nonreturning_calls_endpoints ( ) # Clear old functions dict self . kb . functions . clear ( ) blockaddr_to_function = { } traversed_cfg_nodes = set ( ) function_nodes = set ( ) # Find nodes for beginnings of all functions for _ , dst , data in self . graph . edges ( data = True ) : jumpkind = data . get ( 'jumpkind' , "" ) if jumpkind == 'Ijk_Call' or jumpkind . startswith ( 'Ijk_Sys' ) : function_nodes . add ( dst ) entry_node = self . model . get_any_node ( self . _binary . entry ) if entry_node is not None : function_nodes . add ( entry_node ) # aggressively remove and merge functions # For any function, if there is a call to it, it won't be removed called_function_addrs = { n . addr for n in function_nodes } # Any function addresses that appear as symbols won't be removed predetermined_function_addrs = called_function_addrs | self . _function_addresses_from_symbols removed_functions_a = self . _process_irrational_functions ( tmp_functions , predetermined_function_addrs , blockaddr_to_function ) removed_functions_b , adjusted_cfgnodes = self . _process_irrational_function_starts ( tmp_functions , predetermined_function_addrs , blockaddr_to_function ) self . _process_jump_table_targeted_functions ( tmp_functions , predetermined_function_addrs , blockaddr_to_function , ) removed_functions = removed_functions_a | removed_functions_b # Remove all nodes that are adjusted function_nodes . difference_update ( adjusted_cfgnodes ) for n in self . graph . nodes ( ) : if n . addr in tmp_functions or n . addr in removed_functions : function_nodes . add ( n ) # traverse the graph starting from each node, not following call edges # it's important that we traverse all functions in order so that we have a greater chance to come across # rational functions before its irrational counterparts (e.g. due to failed jump table resolution) min_stage_2_progress = 50.0 max_stage_2_progress = 90.0 nodes_count = len ( function_nodes ) for i , fn in enumerate ( sorted ( function_nodes , key = lambda n : n . addr ) ) : if self . _low_priority : self . _release_gil ( i , 20 ) if self . _show_progressbar or self . _progress_callback : progress = min_stage_2_progress + ( max_stage_2_progress - min_stage_2_progress ) * ( i * 1. / nodes_count ) self . _update_progress ( progress ) self . _graph_bfs_custom ( self . graph , [ fn ] , self . _graph_traversal_handler , blockaddr_to_function , tmp_functions , traversed_cfg_nodes ) # Don't forget those small function chunks that are not called by anything. # There might be references to them from data, or simply references that we cannot find via static analysis secondary_function_nodes = set ( ) # add all function chunks ("functions" that are not called from anywhere) for func_addr in tmp_functions : node = self . model . get_any_node ( func_addr ) if node is None : continue if node . addr not in blockaddr_to_function : secondary_function_nodes . add ( node ) missing_cfg_nodes = set ( self . graph . nodes ( ) ) - traversed_cfg_nodes missing_cfg_nodes = { node for node in missing_cfg_nodes if node . function_address is not None } if missing_cfg_nodes : l . debug ( '%d CFGNodes are missing in the first traversal.' , len ( missing_cfg_nodes ) ) secondary_function_nodes |= missing_cfg_nodes min_stage_3_progress = 90.0 max_stage_3_progress = 99.9 nodes_count = len ( secondary_function_nodes ) for i , fn in enumerate ( sorted ( secondary_function_nodes , key = lambda n : n . addr ) ) : if self . _show_progressbar or self . _progress_callback : progress = min_stage_3_progress + ( max_stage_3_progress - min_stage_3_progress ) * ( i * 1. / nodes_count ) self . _update_progress ( progress ) self . _graph_bfs_custom ( self . graph , [ fn ] , self . _graph_traversal_handler , blockaddr_to_function , tmp_functions ) to_remove = set ( ) # Remove all stubs after PLT entries if not is_arm_arch ( self . project . arch ) : to_remove |= self . _remove_dummy_plt_stubs ( self . kb . functions ) # remove empty functions for func in self . kb . functions . values ( ) : if func . startpoint is None : to_remove . add ( func . addr ) for addr in to_remove : del self . kb . functions [ addr ] # Update CFGNode.function_address for node in self . _nodes . values ( ) : if node . addr in blockaddr_to_function : node . function_address = blockaddr_to_function [ node . addr ] . addr # Update function.info for func in self . kb . functions . values ( ) : if func . addr in tmp_functions : func . info = tmp_functions [ func . addr ] . info