text: import logging
  
  from ..calling_conventions import SYSCALL_CC
  from ..errors import AngrUnsupportedSyscallError
  from ..procedures import SIM_PROCEDURES as P
  from .simos import SimOS
  
  _l = logging.getLogger('angr.simos.userland')
  
  
  class SimUserland(SimOS):
      """
      This is a base class for any SimOS that wants to support syscalls.
  
      It uses the CLE kernel object to provide addresses for syscalls. Syscalls will be emulated as a jump to one of these
      addresses, where a SimProcedure from the syscall library provided at construction time will be executed.
      """
      def __init__(self, project, syscall_library=None, syscall_addr_alignment=4, **kwargs):
          super(SimUserland, self).__init__(project, **kwargs)
          self.syscall_library = syscall_library.copy()
          self.syscall_addr_alignment = syscall_addr_alignment
          self.kernel_base = None
          self.unknown_syscall_number = None
          self.syscall_abis = {}
          # syscall_abis is a dict of tuples {name: (base_number, min_number, max_number)}
          # min_number and max_number are just cached from SimSyscallLibrary.{min,max}imum_sysall_number
          # base_number is used to map the syscalls into the syscall address space - it's a "base address"
          # but a number. to convert from syscall number to address it's (number - min_num + base_num) * alignment + kernel_base
  
      def configure_project(self, abi_list=None): # pylint: disable=arguments-differ
          if abi_list is None:
              abi_list = list(self.syscall_library.syscall_number_mapping)
              assert len(abi_list) == 1, "More than one ABI is available for this target - you need to specify which ones are valid"
          super(SimUserland, self).configure_project()
          self.kernel_base = self.project.loader.kernel_object.mapped_base
  
          base_no = 0
          for abi in abi_list:
              assert abi in self.syscall_library.syscall_number_mapping
              min_no = self.syscall_library.minimum_syscall_number(abi)
              max_no = self.syscall_library.maximum_syscall_number(abi)
              self.syscall_abis[abi] = (base_no, min_no, max_no)
              base_no += max_no - min_no + 1 # since max is the actual max and not the array length
  
          self.unknown_syscall_number = base_no
  
      def syscall(self, state, allow_unsupported=True):
          """
          Given a state, return the procedure corresponding to the current syscall.
          This procedure will have .syscall_number, .display_name, and .addr set.
  
          :param state:               The state to get the syscall number from
          :param allow_unsupported:   Whether to return a "dummy" sycall instead of raising an unsupported exception
          """
          abi = self.syscall_abi(state)
  
          if state.os_name in SYSCALL_CC[state.arch.name]:
              cc = SYSCALL_CC[state.arch.name][state.os_name](state.arch)
          else:
              # Use the default syscall calling convention - it may bring problems
              _l.warning("No syscall calling convention available for %s/%s", state.arch.name, state.os_name)
              cc = SYSCALL_CC[state.arch.name]['default'](state.arch)
  
          sym_num = cc.syscall_num(state)
          possible = state.solver.eval_upto(sym_num, 2)
  
          if len(possible) == 0:
              raise AngrUnsupportedSyscallError("The program state is not satisfiable")
          elif len(possible) == 1:
              num = possible[0]
          elif allow_unsupported:
              num = self.unknown_syscall_number
          else:
              raise AngrUnsupportedSyscallError("Got a symbolic syscall number")
  
          proc = self.syscall_from_number(num, allow_unsupported=allow_unsupported, abi=abi)
          proc.cc = cc
          return proc
  
      def syscall_abi(self, state): # pylint: disable=unused-argument,no-self-use
          """
          Optionally, override this function to determine which abi is being used for the state's current syscall.
          """
          return None
  
      def is_syscall_addr(self, addr):
          """
          Return whether or not the given address corresponds to a syscall implementation.
          """
          if self.kernel_base is None or addr < self.kernel_base:
              return False
  
          addr -= self.kernel_base
  
          if addr % self.syscall_addr_alignment != 0:
              return False
  
          addr /= self.syscall_addr_alignment
          return addr <= self.unknown_syscall_number
  
      def syscall_from_addr(self, addr, allow_unsupported=True):
          """
          Get a syscall SimProcedure from an address.
  
          :param addr: The address to convert to a syscall SimProcedure
          :param allow_unsupported: Whether to return a dummy procedure for an unsupported syscall instead of raising an
                                    exception.
          :return: The SimProcedure for the syscall, or None if the address is not a syscall address.
          """
          if not self.is_syscall_addr(addr):
              return None
  
          number = (addr - self.kernel_base) / self.syscall_addr_alignment
          for abi in self.syscall_abis:
              baseno, minno, maxno = self.syscall_abis[abi]
              if baseno <= number <= baseno + maxno - minno:
                  number += minno
                  number -= baseno
                  break
          else:
              abi = None
          return self.syscall_from_number(number, allow_unsupported=allow_unsupported, abi=abi)
  
      def syscall_from_number(self, number, allow_unsupported=True, abi=None):
          """
          Get a syscall SimProcedure from its number.
  
          :param number:              The syscall number
          :param allow_unsupported:   Whether to return a "stub" syscall for unsupported numbers instead of throwing an error
          :param abi:                 The name of the abi to use. If None, will assume that the abis have disjoint
                                      numbering schemes and pick the right one.
          :return: The SimProcedure for the syscall
          """
          abilist = self.syscall_abis if abi is None else [abi]
  
          if self.syscall_library is None:
              if not allow_unsupported:
                  raise AngrUnsupportedSyscallError("%s does not have a library of syscalls implemented" % self.name)
              proc = P['stubs']['syscall']()
          elif not allow_unsupported and not self.syscall_library.has_implementation(number, self.arch, abilist):
              raise AngrUnsupportedSyscallError("No implementation for syscall %d" % number)
          else:
              proc = self.syscall_library.get(number, self.arch, abilist)
  
          if proc.abi is not None:
              baseno, minno, _ = self.syscall_abis[proc.abi]
              mapno = number - minno + baseno
          else:
              mapno = self.unknown_syscall_number
  
          proc.addr = mapno * self.syscall_addr_alignment + self.kernel_base
          return proc
  