(self, g: networkx.DiGraph, src_addr, dst_addr, src_function, all_edges,
                                   known_functions, blockaddr_to_function)
return False
If source and destination belong to the same function, and the following criteria apply:
        - source node has only one default exit
        - destination is not one of the known functions
        - destination does not belong to another function, or destination belongs to the same function that
          source belongs to
        - at the end of the block, the SP offset is 0
        - for all other edges that are pointing to the destination node, their source nodes must only have one default
          exit, too

        :return:    True if it is a tail-call optimization. False otherwise.
        :rtype:     bool
If source and destination belong to the same function, and the following criteria apply:
        - source node has only one default exit
        - destination is not one of the known functions
        - destination does not belong to another function, or destination belongs to the same function that
          source belongs to
        - at the end of the block, the SP offset is 0
        - for all other edges that are pointing to the destination node, their source nodes must only have one default
          exit, too
If source and destination belong to the same function and the following criteria apply : - source node has only one default exit - destination is not one of the known functions - destination does not belong to another function or destination belongs to the same function that source belongs to - at the end of the block the SP offset is 0 - for all other edges that are pointing to the destination node their source nodes must only have one default exit too
def _is_tail_call_optimization(self, g: networkx.DiGraph, src_addr, dst_addr, src_function, all_edges,
                                   known_functions, blockaddr_to_function):
        """
        If source and destination belong to the same function, and the following criteria apply:
        - source node has only one default exit
        - destination is not one of the known functions
        - destination does not belong to another function, or destination belongs to the same function that
          source belongs to
        - at the end of the block, the SP offset is 0
        - for all other edges that are pointing to the destination node, their source nodes must only have one default
          exit, too

        :return:    True if it is a tail-call optimization. False otherwise.
        :rtype:     bool
        """
        def _has_more_than_one_exit(node_):
            # Do not consider FakeRets as counting as multiple exits here.
            out_edges = list(filter(lambda x: g.get_edge_data(*x)['jumpkind'] != 'Ijk_FakeRet', g.out_edges(node_)))
            return len(out_edges) > 1

        if len(all_edges) == 1 and dst_addr != src_addr:
            the_edge = next(iter(all_edges))
            _, dst, data = the_edge
            if data.get('stmt_idx', None) != DEFAULT_STATEMENT:
                return False

            dst_in_edges = g.in_edges(dst, data=True)
            if len(dst_in_edges) > 1:
                # there are other edges going to the destination node. check all edges to make sure all source nodes
                # only have one default exit
                if any(data.get('stmt_idx', None) != DEFAULT_STATEMENT for _, _, data in dst_in_edges):
                    # some nodes are jumping to the destination node via non-default edges. skip.
                    return False
                if any(_has_more_than_one_exit(src_) for src_, _, _ in dst_in_edges):
                    # at least one source node has more than just the default exit. skip.
                    return False

            candidate = False
            if dst_addr in known_functions:
                # dst_addr cannot be the same as src_function.addr. Pass
                pass
            elif dst_addr in blockaddr_to_function:
                # it seems that we already know where this function should belong to. Pass.
                dst_func = blockaddr_to_function[dst_addr]
                if dst_func is src_function:
                    # they belong to the same function right now, but they'd better not
                    candidate = True
                    # treat it as a tail-call optimization
            else:
                # we don't know where it belongs to
                # treat it as a tail-call optimization
                candidate = True

            if candidate:
                regs = {self.project.arch.sp_offset}
                if hasattr(self.project.arch, 'bp_offset') and self.project.arch.bp_offset is not None:
                    regs.add(self.project.arch.bp_offset)
                sptracker = self.project.analyses.StackPointerTracker(src_function, regs, track_memory=self._sp_tracking_track_memory)
                sp_delta = sptracker.offset_after_block(src_addr, self.project.arch.sp_offset)
                if sp_delta == 0:
                    return True

        return False
def _is_tail_call_optimization ( self , g : networkx . DiGraph , src_addr , dst_addr , src_function , all_edges , known_functions , blockaddr_to_function ) : def _has_more_than_one_exit ( node_ ) : # Do not consider FakeRets as counting as multiple exits here. out_edges = list ( filter ( lambda x : g . get_edge_data ( * x ) [ 'jumpkind' ] != 'Ijk_FakeRet' , g . out_edges ( node_ ) ) ) return len ( out_edges ) > 1 if len ( all_edges ) == 1 and dst_addr != src_addr : the_edge = next ( iter ( all_edges ) ) _ , dst , data = the_edge if data . get ( 'stmt_idx' , None ) != DEFAULT_STATEMENT : return False dst_in_edges = g . in_edges ( dst , data = True ) if len ( dst_in_edges ) > 1 : # there are other edges going to the destination node. check all edges to make sure all source nodes # only have one default exit if any ( data . get ( 'stmt_idx' , None ) != DEFAULT_STATEMENT for _ , _ , data in dst_in_edges ) : # some nodes are jumping to the destination node via non-default edges. skip. return False if any ( _has_more_than_one_exit ( src_ ) for src_ , _ , _ in dst_in_edges ) : # at least one source node has more than just the default exit. skip. return False candidate = False if dst_addr in known_functions : # dst_addr cannot be the same as src_function.addr. Pass pass elif dst_addr in blockaddr_to_function : # it seems that we already know where this function should belong to. Pass. dst_func = blockaddr_to_function [ dst_addr ] if dst_func is src_function : # they belong to the same function right now, but they'd better not candidate = True # treat it as a tail-call optimization else : # we don't know where it belongs to # treat it as a tail-call optimization candidate = True if candidate : regs = { self . project . arch . sp_offset } if hasattr ( self . project . arch , 'bp_offset' ) and self . project . arch . bp_offset is not None : regs . add ( self . project . arch . bp_offset ) sptracker = self . project . analyses . StackPointerTracker ( src_function , regs , track_memory = self . _sp_tracking_track_memory ) sp_delta = sptracker . offset_after_block ( src_addr , self . project . arch . sp_offset ) if sp_delta == 0 : return True return False