'The basic idea is simple: start from a specific point, try to construct
functions as much as we can, and maintain a function distribution graph
and a call graph simultaneously. Repeat searching until we come to the
end that there is no new function to be found.
A function should start with:
# some addresses that a call exit leads to, or
# certain instructions. They are recoreded in SimArch.
For a better performance, instead of blindly scanning the entire process
space, we first try to search for instruction patterns that a function
may start with, and start scanning at those positions. Then we try to
decode anything that is left.'

def _determinebase_address(self):

    traced_address = set()
    self.functions = set()
    self.call_map = networkx.DiGraph()
    self.cfg = networkx.DiGraph()
    initial_state = self.project.factory.blank_state(mode='fastpath')
    initial_options = ((initial_state.options - {o.TRACK_CONSTRAINTS}) - o.refs)
    initial_options |= {o.SUPER_FASTPATH}
    initial_state.options = initial_options
    function_exits = defaultdict(set)
    dump_file_prefix = self.project.filename
    if (self._pickle_intermediate_results and os.path.exists((dump_file_prefix + '_indirect_jumps.angr'))):
        l.debug('Loading    existing    intermediate    results.')
        self._indirect_jumps = pickle.load(open((dump_file_prefix + '_indirect_jumps.angr'), 'rb'))
        self.cfg = pickle.load(open((dump_file_prefix + '_coercecfg.angr'), 'rb'))
        self._unassured_functions = pickle.load(open((dump_file_prefix + '_unassured_functions.angr'), 'rb'))
    else:
        self._scan_function_prologues(traced_address, function_exits, initial_state)
        if self._pickle_intermediate_results:
            l.debug('Dumping    intermediate    results.')
            pickle.dump(self._indirect_jumps, open((dump_file_prefix + '_indirect_jumps.angr'), 'wb'), (-1))
            pickle.dump(self.cfg, open((dump_file_prefix + '_coercecfg.angr'), 'wb'), (-1))
            pickle.dump(self._unassured_functions, open((dump_file_prefix + '_unassured_functions.angr'), 'wb'), (-1))
    if len(self._indirect_jumps):
        function_starts = self._process_indirect_jumps()
        self.base_address = self._solve_forbase_address(function_starts, self._unassured_functions)
        l.info('Base    address    should    be    0x%x', self.base_address)
    else:
        l.debug('No    indirect    jumps    are    found.    We    switch    to    the    slowpath    mode.')
        while True:
            next_addr = self._get_next_code_addr(initial_state)
            percentage = ((self._seg_list.occupied_size * 100.0) / self._valid_memory_region_size)
            l.info('Analyzing    %xh,    progress    %0.04f%%', next_addr, percentage)
            if (next_addr is None):
                break
            self.call_map.add_node(next_addr)
            self._scan_code(traced_address, function_exits, initial_state, next_addr)
    for (src, s) in function_exits.items():
        if (src in self.call_map):
            for target in s:
                if (target in self.call_map):
                    self.call_map.add_edge(src, target)
    nodes = sorted(self.call_map.nodes())
    for i in range((len(nodes) - 1)):
        if (nodes[i] >= (nodes[(i + 1)] - 4)):
            for dst in self.call_map.successors(nodes[(i + 1)]):
                self.call_map.add_edge(nodes[i], dst)
            for src in self.call_map.predecessors(nodes[(i + 1)]):
                self.call_map.add_edge(src, nodes[i])
            self.call_map.remove_node(nodes[(i + 1)])
    l.debug('Construction    finished.')
