docs: def _determinebase_address(self):
          """
          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.
          """
  
          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_options.remove(o.COW_STATES)
          initial_state.options = initial_options
          # Sadly, not all calls to functions are explicitly made by call
          # instruction - they could be a jmp or b, or something else. So we
          # should record all exits from a single function, and then add
          # necessary calling edges in our call map during the post-processing
          # phase.
          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:
              # Performance boost :-)
              # Scan for existing function prologues
              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):
              # We got some indirect jumps!
              # Gotta execute each basic block and see where it wants to jump to
              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.")
              # TODO: 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)
  
          # Post-processing: Map those calls that are not made by call/blr
          # instructions to their targets in our map
          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.")