Create the SimSuccessors instance for a block.

        :param int addr:                  Address of the block.
        :param CFGJob job:                The CFG job instance with an input state inside.
        :param int current_function_addr: Address of the current function.
        :return:                          A SimSuccessors instance
        :rtype:                           SimSuccessors

def _get_simsuccessors(self, addr, job, current_function_addr=None):
        """
        Create the SimSuccessors instance for a block.

        :param int addr:                  Address of the block.
        :param CFGJob job:                The CFG job instance with an input state inside.
        :param int current_function_addr: Address of the current function.
        :return:                          A SimSuccessors instance
        :rtype:                           SimSuccessors
        """

        exception_info = None
        state = job.state
        saved_state = job.state  # We don't have to make a copy here

        # respect the basic block size from base graph
        block_size = None
        if self._base_graph is not None:
            for n in self._base_graph.nodes():
                if n.addr == addr:
                    block_size = n.size
                    break

        try:
            sim_successors = None

            if not self._keep_state:
                if self.project.is_hooked(addr):
                    old_proc = self.project._sim_procedures[addr]
                    is_continuation = old_proc.is_continuation
                elif self.project.simos.is_syscall_addr(addr):
                    old_proc = self.project.simos.syscall_from_addr(addr)
                    is_continuation = False  # syscalls don't support continuation
                else:
                    old_proc = None
                    is_continuation = None

                if old_proc is not None and \
                        not is_continuation and \
                        not old_proc.ADDS_EXITS and \
                        not old_proc.NO_RET:
                    # DON'T CREATE USELESS SIMPROCEDURES if we don't care about the accuracy of states
                    # When generating CFG, a SimProcedure will not be created as it is but be created as a
                    # ReturnUnconstrained stub if it satisfies the following conditions:
                    # - It doesn't add any new exits.
                    # - It returns as normal.
                    # In this way, we can speed up the CFG generation by quite a lot as we avoid simulating
                    # those functions like read() and puts(), which has no impact on the overall control flow at all.
                    #
                    # Special notes about SimProcedure continuation: Any SimProcedure instance that is a continuation
                    # will add new exits, otherwise the original SimProcedure wouldn't have been executed anyway. Hence
                    # it's reasonable for us to always simulate a SimProcedure with continuation.

                    old_name = None

                    if old_proc.is_syscall:
                        new_stub = SIM_PROCEDURES["stubs"]["syscall"]
                        ret_to = state.regs.ip_at_syscall
                    else:
                        # normal SimProcedures
                        new_stub = SIM_PROCEDURES["stubs"]["ReturnUnconstrained"]
                        ret_to = None

                    old_name = old_proc.display_name

                    # instantiate the stub
                    new_stub_inst = new_stub(display_name=old_name)

                    sim_successors = self.project.engines.procedure_engine.process(
                        state,
                        new_stub_inst,
                        force_addr=addr,
                        ret_to=ret_to,
                    )

            if sim_successors is None:
                jumpkind = state.history.jumpkind
                jumpkind = 'Ijk_Boring' if jumpkind is None else jumpkind
                sim_successors = self.project.factory.successors(
                        state,
                        jumpkind=jumpkind,
                        size=block_size,
                        opt_level=self._iropt_level)

        except (SimFastPathError, SimSolverModeError) as ex:

            if saved_state.mode == 'fastpath':
                # Got a SimFastPathError or SimSolverModeError in FastPath mode.
                # We wanna switch to symbolic mode for current IRSB.
                l.debug('Switch to symbolic mode for address %#x', addr)
                # Make a copy of the current 'fastpath' state

                l.debug('Symbolic jumps at basic block %#x.', addr)

                new_state = None
                if addr != current_function_addr:
                    new_state = self._get_symbolic_function_initial_state(current_function_addr)

                if new_state is None:
                    new_state = state.copy()
                    new_state.set_mode('symbolic')
                new_state.options.add(o.DO_RET_EMULATION)
                # Remove bad constraints
                # FIXME: This is so hackish...
                new_state.solver._solver.constraints = [c for c in new_state.solver.constraints if
                                                    c.op != 'BoolV' or c.args[0] is not False]
                new_state.solver._solver._result = None
                # Swap them
                saved_state, job.state = job.state, new_state
                sim_successors, exception_info, _ = self._get_simsuccessors(addr, job)

            else:
                exception_info = sys.exc_info()
                # Got a SimSolverModeError in symbolic mode. We are screwed.
                # Skip this IRSB
                l.debug("Caught a SimIRSBError %s. Don't panic, this is usually expected.", ex)
                inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
                sim_successors = SimEngineProcedure().process(state, inst)

        except SimIRSBError:
            exception_info = sys.exc_info()
            # It's a tragedy that we came across some instructions that VEX
            # does not support. I'll create a terminating stub there
            l.debug("Caught a SimIRSBError during CFG recovery. Creating a PathTerminator.", exc_info=True)
            inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
            sim_successors = SimEngineProcedure().process(state, inst)

        except claripy.ClaripyError:
            exception_info = sys.exc_info()
            l.debug("Caught a ClaripyError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
            # Generate a PathTerminator to terminate the current path
            inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
            sim_successors = SimEngineProcedure().process(state, inst)

        except SimError:
            exception_info = sys.exc_info()
            l.debug("Caught a SimError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
            # Generate a PathTerminator to terminate the current path
            inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
            sim_successors = SimEngineProcedure().process(state, inst)

        except AngrExitError as ex:
            exception_info = sys.exc_info()
            l.debug("Caught a AngrExitError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
            # Generate a PathTerminator to terminate the current path
            inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
            sim_successors = SimEngineProcedure().process(state, inst)

        except AngrError:
            exception_info = sys.exc_info()
            section = self.project.loader.main_object.find_section_containing(addr)
            if section is None:
                sec_name = 'No section'
            else:
                sec_name = section.name
            # AngrError shouldn't really happen though
            l.debug("Caught an AngrError during CFG recovery at %#x (%s)",
                    addr, sec_name, exc_info=True)
            # We might be on a wrong branch, and is likely to encounter the
            # "No bytes in memory xxx" exception
            # Just ignore it
            sim_successors = None

        return sim_successors, exception_info, saved_state