'For unresolveable indirect jumps, angr marks those jump targets as individual functions. For example, usually
the following pattern is seen:
sub_0x400010:
push ebp
mov esp, ebp
cmp eax, 10
ja end
mov eax, jumptable[eax]
jmp eax
sub_0x400080:
# do something here
jmp end
end (0x400e00):
pop ebp
ret
In the example above, `process_irrational_functions` will remove function 0x400080, and merge it with function
0x400010.
:param angr.knowledge.FunctionManager functions: all functions that angr recovers, including those ones that are
misidentified as functions.
:param dict blockaddr_to_function: A mapping between block addresses and Function instances.
:return: a list of addresses of all removed functions
:rtype: list'

def _process_irrational_functions(self, functions, predetermined_function_addrs, blockaddr_to_function):

    functions_to_remove = {}
    functions_can_be_removed = (set(functions.keys()) - set(predetermined_function_addrs))
    for (func_addr, function) in functions.iteritems():
        if (func_addr in functions_to_remove):
            continue
        has_unresolved_jumps = False
        max_unresolved_jump_addr = 0
        for block_addr in function.block_addrs_set:
            if ((block_addr in self.indirect_jumps) and (self.indirect_jumps[block_addr].jumpkind == 'Ijk_Boring') and (not self.indirect_jumps[block_addr].resolved_targets)):
                has_unresolved_jumps = True
                max_unresolved_jump_addr = max(max_unresolved_jump_addr, block_addr)
        if (not has_unresolved_jumps):
            continue
        if (function.startpoint is None):
            continue
        startpoint_addr = function.startpoint.addr
        if (not function.endpoints):
            continue
        endpoint_addr = max([a.addr for a in function.endpoints])
        the_endpoint = next((a for a in function.endpoints if (a.addr == endpoint_addr)))
        endpoint_addr += the_endpoint.size
        if (startpoint_addr >= endpoint_addr):
            continue
        if (max_unresolved_jump_addr >= endpoint_addr):
            continue
        last_addr = endpoint_addr
        tmp_state = self.project.factory.blank_state(mode='fastpath')
        while True:
            try:
                tmp_state.ip = last_addr
                b = self.project.factory.successors(tmp_state, jumpkind='Ijk_Boring')
                if (len(b.successors) != 1):
                    break
                if (b.successors[0].history.jumpkind != 'Ijk_Boring'):
                    break
                if b.successors[0].ip.symbolic:
                    break
                suc_addr = b.successors[0].ip._model_concrete
                if (max(startpoint_addr, (the_endpoint.addr - 64)) <= suc_addr < (the_endpoint.addr + the_endpoint.size)):
                    endpoint_addr = (b.addr + b.artifacts['irsb_size'])
                else:
                    break
                last_addr = (b.addr + b.artifacts['irsb_size'])
            except (SimTranslationError, SimMemoryError, SimIRSBError, SimEngineError):
                break
        should_merge = True
        functions_to_merge = set()
        for f_addr in functions_can_be_removed:
            f = functions[f_addr]
            if (f_addr == func_addr):
                continue
            if ((max_unresolved_jump_addr < f_addr < endpoint_addr) and all([(max_unresolved_jump_addr < b_addr < endpoint_addr) for b_addr in f.block_addrs])):
                if (f_addr in functions_to_remove):
                    should_merge = False
                    break
                if (f_addr in predetermined_function_addrs):
                    should_merge = False
                    break
                functions_to_merge.add(f_addr)
        if (not should_merge):
            continue
        for f_addr in functions_to_merge:
            functions_to_remove[f_addr] = func_addr
            continue
    for (to_remove, merge_with) in functions_to_remove.iteritems():
        func_merge_with = self._addr_to_function(merge_with, blockaddr_to_function, functions)
        for block_addr in functions[to_remove].block_addrs:
            blockaddr_to_function[block_addr] = func_merge_with
        del functions[to_remove]
    return functions_to_remove.keys()
