<commit_before>import StringIO
import errno
import fcntl
import logging
import os
import random
import struct
import ctypes
import socket

# Remove in favor of binary.py
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
from elftools.elf.descriptions import describe_symbol_type

from ..core.cpu.abstractcpu import Interruption, Syscall, ConcretizeArgument
from ..core.cpu.cpufactory import CpuFactory
from ..core.cpu.binja import BinjaCpu
from ..core.memory import SMemory32, SMemory64, Memory32, Memory64
from ..core.smtlib import Operators, ConstraintSet, SolverException, solver
from ..core.cpu.arm import *
from ..core.executor import TerminateState
from ..platforms.platform import Platform, SyscallNotImplemented
from ..utils.helpers import issymbolic, is_binja_disassembler
from . import linux_syscalls

logger = logging.getLogger(__name__)


class RestartSyscall(Exception):
    pass


class Deadlock(Exception):
    pass


class EnvironmentError(RuntimeError):
    pass


class FdError(Exception):
    def __init__(self, message='', err=errno.EBADF):
        self.err = err
        super(FdError, self).__init__(message)


def perms_from_elf(elf_flags):
    return ['   ', '  x', ' w ', ' wx', 'r  ', 'r x', 'rw ', 'rwx'][elf_flags & 7]


def perms_from_protflags(prot_flags):
    return ['   ', 'r  ', ' w ', 'rw ', '  x', 'r x', ' wx', 'rwx'][prot_flags & 7]


def mode_from_flags(file_flags):
    return {os.O_RDWR: 'r+', os.O_RDONLY: 'r', os.O_WRONLY: 'w'}[file_flags & 7]


class File(object):
    def __init__(self, path, flags):
        # TODO: assert file is seekable otherwise we should save what was
        # read/write to the state
        mode = mode_from_flags(flags)
        self.file = file(path, mode)

    def __getstate__(self):
        state = {}
        state['name'] = self.name
        state['mode'] = self.mode
        try:
            state['pos'] = self.tell()
        except IOError:
            # This is to handle special files like /dev/tty
            state['pos'] = None
        return state

    def __setstate__(self, state):
        name = state['name']
        mode = state['mode']
        pos = state['pos']
        self.file = file(name, mode)
        if pos is not None:
            self.seek(pos)

    @property
    def name(self):
        return self.file.name

    @property
    def mode(self):
        return self.file.mode

    def stat(self):
        return os.fstat(self.fileno())

    def ioctl(self, request, argp):
        # argp ignored..
        return fcntl.fcntl(self, request)

    def tell(self, *args):
        return self.file.tell(*args)

    def seek(self, *args):
        return self.file.seek(*args)

    def write(self, buf):
        for c in buf:
            self.file.write(c)

    def read(self, *args):
        return self.file.read(*args)

    def close(self, *args):
        return self.file.close(*args)

    def fileno(self, *args):
        return self.file.fileno(*args)

    def is_full(self):
        return False

    def sync(self):
        '''
        Flush buffered data. Currently not implemented.
        '''
        return


class Directory(File):
    def __init__(self, path, flags):
        assert(os.path.isdir(path))

        self.fd = os.open(path, flags)
        self.path = path
        self.flags = flags

    def __getstate__(self):
        state = {}
        state['path'] = self.path
        state['flags'] = self.flags
        return state

    def __setstate__(self, state):
        self.path = state['path']
        self.flags = state['flags']
        self.fd = os.open(self.path, self.flags)

    @property
    def name(self):
        return self.path

    @property
    def mode(self):
        return mode_from_flags(self.flags)

    def tell(self, *args):
        return 0

    def seek(self, *args):
        return 0

    def write(self, buf):
        raise FdError("Is a directory", errno.EBADF)

    def read(self, *args):
        raise FdError("Is a directory", errno.EISDIR)

    def close(self, *args):
        return os.close(self.fd)

    def fileno(self, *args):
        return self.fd


class SymbolicFile(File):
    '''
    Represents a symbolic file.
    '''

    def __init__(self, constraints, path="sfile", mode='rw', max_size=100,
                 wildcard='+'):
        '''
        Builds a symbolic file

        :param constraints: the SMT constraints
        :param str path: the pathname of the symbolic file
        :param str mode: the access permissions of the symbolic file
        :param max_size: Maximum amount of bytes of the symbolic file
        :param str wildcard: Wildcard to be used in symbolic file
        '''
        super(SymbolicFile, self).__init__(path, mode)

        # read the concrete data using the parent the read() form the File class
        data = self.file.read()

        self._constraints = constraints
        self.pos = 0
        self.max_size = min(len(data), max_size)

        # build the constraints array
        size = len(data)
        self.array = constraints.new_array(name=self.name, index_max=size)

        symbols_cnt = 0
        for i in range(size):
            if data[i] != wildcard:
                self.array[i] = data[i]
            else:
                symbols_cnt += 1

        if symbols_cnt > max_size:
            logger.warning(("Found more wilcards in the file than free ",
                            "symbolic values allowed (%d > %d)"),
                           symbols_cnt,
                           max_size)
        else:
            logger.debug("Found %d free symbolic values on file %s",
                         symbols_cnt,
                         self.name)

    def __getstate__(self):
        state = {}
        state['array'] = self.array
        state['pos'] = self.pos
        state['max_size'] = self.max_size
        return state

    def __setstate__(self, state):
        self.pos = state['pos']
        self.max_size = state['max_size']
        self.array = state['array']

    def tell(self):
        '''
        Returns the read/write file offset
        :rtype: int
        :return: the read/write file offset.
        '''
        return self.pos

    def seek(self, offset, whence=os.SEEK_SET):
        '''
        Repositions the file C{offset} according to C{whence}.
        Returns the resulting offset or -1 in case of error.
        :rtype: int
        :return: the file offset.
        '''
        assert isinstance(offset, (int, long))
        assert whence in (os.SEEK_SET, os.SEEK_CUR, os.SEEK_END)

        new_position = 0
        if whence == os.SEEK_SET:
            new_position = offset
        elif whence == os.SEEK_CUR:
            new_position = self.pos + offset
        elif whence == os.SEEK_END:
            new_position = self.max_size + offset

        if new_position < 0:
            return -1

        self.pos = new_position

        return self.pos

    def read(self, count):
        '''
        Reads up to C{count} bytes from the file.
        :rtype: list
        :return: the list of symbolic bytes read
        '''
        if self.pos > self.max_size:
            return []
        else:
            size = min(count, self.max_size - self.pos)
            ret = [self.array[i] for i in xrange(self.pos, self.pos + size)]
            self.pos += size
            return ret

    def write(self, data):
        '''
        Writes the symbolic bytes in C{data} onto the file.
        '''
        size = min(len(data), self.max_size - self.pos)
        for i in xrange(self.pos, self.pos + size):
            self.array[i] = data[i - self.pos]


class SocketDesc(object):
    '''
    Represents a socket descriptor (i.e. value returned by socket(2)
    '''

    def __init__(self, domain=None, socket_type=None, protocol=None):
        self.domain = domain
        self.socket_type = socket_type
        self.protocol = protocol


class Socket(object):
    def stat(self):
        from collections import namedtuple
        stat_result = namedtuple('stat_result', ['st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid',
                                                 'st_size', 'st_atime', 'st_mtime', 'st_ctime', 'st_blksize', 'st_blocks', 'st_rdev'])
        return stat_result(8592, 11, 9, 1, 1000, 5, 0, 1378673920, 1378673920, 1378653796, 0x400, 0x8808, 0)

    @staticmethod
    def pair():
        a = Socket()
        b = Socket()
        a.connect(b)
        return a, b

    def __init__(self):
        self.buffer = []  # queue os bytes
        self.peer = None

    def __repr__(self):
        return "SOCKET(%x, %r, %x)" % (hash(self), self.buffer, hash(self.peer))

    def is_connected(self):
        return self.peer is not None

    def is_empty(self):
        return not self.buffer

    def is_full(self):
        return len(self.buffer) > 2 * 1024

    def connect(self, peer):
        assert not self.is_connected()
        assert not peer.is_connected()
        self.peer = peer
        if peer.peer is None:
            peer.peer = self

    def read(self, size):
        return self.receive(size)

    def receive(self, size):
        rx_bytes = min(size, len(self.buffer))
        ret = []
        for i in xrange(rx_bytes):
            ret.append(self.buffer.pop())
        return ret

    def write(self, buf):
        assert self.is_connected()
        return self.peer._transmit(buf)

    def _transmit(self, buf):
        for c in buf:
            self.buffer.insert(0, c)
        return len(buf)

    def sync(self):
        raise FdError("Invalid sync() operation on Socket", errno.EINVAL)

    def seek(self, *args):
        raise FdError("Invalid lseek() operation on Socket", errno.EINVAL)


class Linux(Platform):
    '''
    A simple Linux Operating System Platform.
    This class emulates the most common Linux system calls
    '''

    # from /usr/include/asm-generic/resource.h
    RLIMIT_NOFILE = 7  # /* max number of open files */
    FCNTL_FDCWD = -100  # /* Special value used to indicate openat should use the cwd */

    def __init__(self, program, argv=None, envp=None, disasm='capstone', **kwargs):
        '''
        Builds a Linux OS platform
        :param string program: The path to ELF binary
        :param string disasm: Disassembler to be used
        :param list argv: The argv array; not including binary.
        :param list envp: The ENV variables.
        :ivar files: List of active file descriptors
        :type files: list[Socket] or list[File]
        '''
        super(Linux, self).__init__(path=program, **kwargs)

        self.program = program
        self.clocks = 0
        self.files = []
        self.syscall_trace = []
        # Many programs to support SLinux
        self.programs = program
        self.disasm = disasm

        # dict of [int -> (int, int)] where tuple is (soft, hard) limits
        self._rlimits = {
            self.RLIMIT_NOFILE: (256, 1024)
        }

        if program is not None:
            self.elf = ELFFile(file(program))
            # FIXME (theo) self.arch is actually mode as initialized in the CPUs,
            # make things consistent and perhaps utilize a global mapping for this
            self.arch = {'x86': 'i386', 'x64': 'amd64', 'ARM': 'armv7'}[self.elf.get_machine_arch()]

            self._init_cpu(self.arch)
            self._init_std_fds()
            self._execve(program, argv, envp)

    @classmethod
    def empty_platform(cls, arch):
        '''
        Create a platform without an ELF loaded.

        :param str arch: The architecture of the new platform
        :rtype: Linux
        '''
        platform = cls(None)
        platform._init_cpu(arch)
        platform._init_std_fds()
        return platform

    def _init_std_fds(self):
        # open standard files stdin, stdout, stderr
        logger.debug("Opening file descriptors (0,1,2) (STDIN, STDOUT, STDERR)")
        self.input = Socket()
        self.output = Socket()
        self.stderr = Socket()

        stdin = Socket()
        stdout = Socket()
        stderr = Socket()
        # A transmit to stdin,stdout or stderr will be directed to out
        stdin.peer = self.output
        stdout.peer = self.output
        stderr.peer = self.stderr
        # A receive from stdin will get data from input
        self.input.peer = stdin
        # A receive on stdout or stderr will return no data (rx_bytes: 0)

        assert self._open(stdin) == 0
        assert self._open(stdout) == 1
        assert self._open(stderr) == 2

    def _init_cpu(self, arch):
        # create memory and CPU
        cpu = self._mk_proc(arch)
        self.procs = [cpu]
        self._current = 0
        self._function_abi = CpuFactory.get_function_abi(cpu, 'linux', arch)
        self._syscall_abi = CpuFactory.get_syscall_abi(cpu, 'linux', arch)

    def _find_symbol(self, name):
        symbol_tables = (s for s in self.elf.iter_sections()
                         if isinstance(s, SymbolTableSection))

        for section in symbol_tables:
            if section['sh_entsize'] == 0:
                continue

            for symbol in section.iter_symbols():
                if describe_symbol_type(symbol['st_info']['type']) == 'FUNC':
                    if symbol.name == name:
                        return symbol['st_value']

        return None

    def _execve(self, program, argv, envp):
        '''
        Load `program` and establish program state, such as stack and arguments.

        :param program str: The ELF binary to load
        :param argv list: argv array
        :param envp list: envp array
        '''
        argv = [] if argv is None else argv
        envp = [] if envp is None else envp

        logger.debug("Loading %s as a %s elf", program, self.arch)

        self.load(program)
        self._arch_specific_init()

        self._stack_top = self.current.STACK
        self.setup_stack([program] + argv, envp)

        nprocs = len(self.procs)
        nfiles = len(self.files)
        assert nprocs > 0
        self.running = range(nprocs)

        # Each process can wait for one timeout
        self.timers = [None] * nprocs
        # each fd has a waitlist
        self.rwait = [set() for _ in xrange(nfiles)]
        self.twait = [set() for _ in xrange(nfiles)]

        # Install event forwarders
        for proc in self.procs:
            self.forward_events_from(proc)

    def _mk_proc(self, arch):
        mem = Memory32() if arch in {'i386', 'armv7'} else Memory64()
        cpu = CpuFactory.get_cpu(mem, arch)
        return cpu

    @property
    def current(self):
        return self.procs[self._current]

    def __getstate__(self):
        state = super(Linux, self).__getstate__()
        state['clocks'] = self.clocks
        state['input'] = self.input.buffer
        state['output'] = self.output.buffer

        # Store the type of file descriptor and the respective contents
        state_files = []
        for fd in self.files:
            if isinstance(fd, Socket):
                state_files.append(('Socket', fd.buffer))
            else:
                state_files.append(('File', fd))
        state['files'] = state_files
        state['rlimits'] = self._rlimits

        state['procs'] = self.procs
        state['current'] = self._current
        state['running'] = self.running
        state['rwait'] = self.rwait
        state['twait'] = self.twait
        state['timers'] = self.timers
        state['syscall_trace'] = self.syscall_trace
        state['base'] = self.base
        state['elf_bss'] = self.elf_bss
        state['end_code'] = self.end_code
        state['end_data'] = self.end_data
        state['elf_brk'] = self.elf_brk
        state['auxv'] = self.auxv
        state['program'] = self.program
        state['functionabi'] = self._function_abi
        state['syscallabi'] = self._syscall_abi
        state['uname_machine'] = self._uname_machine

        if hasattr(self, '_arm_tls_memory'):
            state['_arm_tls_memory'] = self._arm_tls_memory
        return state

    def __setstate__(self, state):
        """
        :todo: some asserts
        :todo: fix deps? (last line)
        """
        super(Linux, self).__setstate__(state)

        self.input = Socket()
        self.input.buffer = state['input']
        self.output = Socket()
        self.output.buffer = state['output']

        # fetch each file descriptor (Socket or File())
        self.files = []
        for ty, buf in state['files']:
            if ty == 'Socket':
                f = Socket()
                f.buffer = buf
                self.files.append(f)
            else:
                self.files.append(buf)

        self.files[0].peer = self.output
        self.files[1].peer = self.output
        self.files[2].peer = self.output
        self.input.peer = self.files[0]
        self._rlimits = state['rlimits']

        self.procs = state['procs']
        self._current = state['current']
        self.running = state['running']
        self.rwait = state['rwait']
        self.twait = state['twait']
        self.timers = state['timers']
        self.clocks = state['clocks']

        self.syscall_trace = state['syscall_trace']
        self.base = state['base']
        self.elf_bss = state['elf_bss']
        self.end_code = state['end_code']
        self.end_data = state['end_data']
        self.elf_brk = state['elf_brk']
        self.auxv = state['auxv']
        self.program = state['program']
        self._function_abi = state['functionabi']
        self._syscall_abi = state['syscallabi']
        self._uname_machine = state['uname_machine']
        if '_arm_tls_memory' in state:
            self._arm_tls_memory = state['_arm_tls_memory']

        # Install event forwarders
        for proc in self.procs:
            self.forward_events_from(proc)

    def _init_arm_kernel_helpers(self):
        '''
        ARM kernel helpers

        https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
        '''

        page_data = bytearray('\xf1\xde\xfd\xe7' * 1024)

        # Extracted from a RPi2
        preamble = (
            'ff0300ea' +
            '650400ea' +
            'f0ff9fe5' +
            '430400ea' +
            '220400ea' +
            '810400ea' +
            '000400ea' +
            '870400ea'
        ).decode('hex')

        # XXX(yan): The following implementations of cmpxchg and cmpxchg64 were
        # handwritten to not use any exclusive instructions (e.g. ldrexd) or
        # locking. For actual implementations, refer to
        # arch/arm64/kernel/kuser32.S in the Linux source code.
        __kuser_cmpxchg64 = (
            '30002de9' +  # push    {r4, r5}
            '08c09de5' +  # ldr     ip, [sp, #8]
            '30009ce8' +  # ldm     ip, {r4, r5}
            '010055e1' +  # cmp     r5, r1
            '00005401' +  # cmpeq   r4, r0
            '0100a013' +  # movne   r0, #1
            '0000a003' +  # moveq   r0, #0
            '0c008c08' +  # stmeq   ip, {r2, r3}
            '3000bde8' +  # pop     {r4, r5}
            '1eff2fe1'   # bx      lr
        ).decode('hex')

        __kuser_dmb = (
            '5bf07ff5' +  # dmb ish
            '1eff2fe1'   # bx lr
        ).decode('hex')

        __kuser_cmpxchg = (
            '003092e5' +  # ldr     r3, [r2]
            '000053e1' +  # cmp     r3, r0
            '0000a003' +  # moveq   r0, #0
            '00108205' +  # streq   r1, [r2]
            '0100a013' +  # movne   r0, #1
            '1eff2fe1'   # bx      lr
        ).decode('hex')

        # Map a TLS segment
        self._arm_tls_memory = self.current.memory.mmap(None, 4, 'rw ')

        __kuser_get_tls = (
            '04009FE5' +  # ldr r0, [pc, #4]
            '010090e8' +  # ldm r0, {r0}
            '1eff2fe1'   # bx lr
        ).decode('hex') + struct.pack('<I', self._arm_tls_memory)

        tls_area = '\x00' * 12

        version = struct.pack('<I', 5)

        def update(address, code):
            page_data[address:address + len(code)] = code

        # Offsets from Documentation/arm/kernel_user_helpers.txt in Linux
        update(0x000, preamble)
        update(0xf60, __kuser_cmpxchg64)
        update(0xfa0, __kuser_dmb)
        update(0xfc0, __kuser_cmpxchg)
        update(0xfe0, __kuser_get_tls)
        update(0xff0, tls_area)
        update(0xffc, version)

        self.current.memory.mmap(0xffff0000, len(page_data), 'r x', page_data)

    def load_vdso(self, bits):
        # load vdso #TODO or #IGNORE
        vdso_top = {32: 0x7fff0000, 64: 0x7fff00007fff0000}[bits]
        vdso_size = len(file('vdso%2d.dump' % bits).read())
        vdso_addr = self.memory.mmapFile(self.memory._floor(vdso_top - vdso_size),
                                         vdso_size,
                                         'r x',
                                         {32: 'vdso32.dump', 64: 'vdso64.dump'}[bits],
                                         0)
        return vdso_addr

    def setup_stack(self, argv, envp):
        '''
        :param Cpu cpu: The cpu instance
        :param argv: list of parameters for the program to execute.
        :param envp: list of environment variables for the program to execute.

        http://www.phrack.org/issues.html?issue=58&id=5#article
         position            content                     size (bytes) + comment
         ----------------------------------------------------------------------
         stack pointer ->  [ argc = number of args ]     4
                         [ argv[0] (pointer) ]         4   (program name)
                         [ argv[1] (pointer) ]         4
                         [ argv[..] (pointer) ]        4 * x
                         [ argv[n - 1] (pointer) ]     4
                         [ argv[n] (pointer) ]         4   (= NULL)

                         [ envp[0] (pointer) ]         4
                         [ envp[1] (pointer) ]         4
                         [ envp[..] (pointer) ]        4
                         [ envp[term] (pointer) ]      4   (= NULL)

                         [ auxv[0] (Elf32_auxv_t) ]    8
                         [ auxv[1] (Elf32_auxv_t) ]    8
                         [ auxv[..] (Elf32_auxv_t) ]   8
                         [ auxv[term] (Elf32_auxv_t) ] 8   (= AT_NULL vector)

                         [ padding ]                   0 - 16

                         [ argument ASCIIZ strings ]   >= 0
                         [ environment ASCIIZ str. ]   >= 0

         (0xbffffffc)      [ end marker ]                4   (= NULL)

         (0xc0000000)      < top of stack >              0   (virtual)
         ----------------------------------------------------------------------
        '''
        cpu = self.current

        # In case setup_stack() is called again, we make sure we're growing the
        # stack from the original top
        cpu.STACK = self._stack_top

        auxv = self.auxv
        logger.debug("Setting argv, envp and auxv.")
        logger.debug("\tArguments: %s", repr(argv))
        if envp:
            logger.debug("\tEnvironment:")
            for e in envp:
                logger.debug("\t\t%s", repr(e))

        logger.debug("\tAuxv:")
        for name, val in auxv.items():
            logger.debug("\t\t%s: %s", name, hex(val))

        # We save the argument and environment pointers
        argvlst = []
        envplst = []

        # end envp marker empty string
        for evar in envp:
            cpu.push_bytes('\x00')
            envplst.append(cpu.push_bytes(evar))

        for arg in argv:
            cpu.push_bytes('\x00')
            argvlst.append(cpu.push_bytes(arg))

        # Put all auxv strings into the string stack area.
        # And replace the value be its pointer

        for name, value in auxv.items():
            if hasattr(value, '__len__'):
                cpu.push_bytes(value)
                auxv[name] = cpu.STACK

        # The "secure execution" mode of secure_getenv() is controlled by the
        # AT_SECURE flag contained in the auxiliary vector passed from the
        # kernel to user space.
        auxvnames = {
            'AT_IGNORE': 1,  # Entry should be ignored
            'AT_EXECFD': 2,  # File descriptor of program
            'AT_PHDR': 3,  # Program headers for program
            'AT_PHENT': 4,  # Size of program header entry
            'AT_PHNUM': 5,  # Number of program headers
            'AT_PAGESZ': 6,  # System page size
            'AT_BASE': 7,  # Base address of interpreter
            'AT_FLAGS': 8,  # Flags
            'AT_ENTRY': 9,  # Entry point of program
            'AT_NOTELF': 10,  # Program is not ELF
            'AT_UID': 11,  # Real uid
            'AT_EUID': 12,  # Effective uid
            'AT_GID': 13,  # Real gid
            'AT_EGID': 14,  # Effective gid
            'AT_CLKTCK': 17,  # Frequency of times()
            'AT_PLATFORM': 15,  # String identifying platform.
            'AT_HWCAP': 16,  # Machine-dependent hints about processor capabilities.
            'AT_FPUCW': 18,  # Used FPU control word.
            'AT_SECURE': 23,  # Boolean, was exec setuid-like?
            'AT_BASE_PLATFORM': 24,  # String identifying real platforms.
            'AT_RANDOM': 25,  # Address of 16 random bytes.
            'AT_EXECFN': 31,  # Filename of executable.
            'AT_SYSINFO': 32,  # Pointer to the global system page used for system calls and other nice things.
            'AT_SYSINFO_EHDR': 33,  # Pointer to the global system page used for system calls and other nice things.
        }
        # AT_NULL
        cpu.push_int(0)
        cpu.push_int(0)
        for name, val in auxv.items():
            cpu.push_int(val)
            cpu.push_int(auxvnames[name])

        # NULL ENVP
        cpu.push_int(0)
        for var in reversed(envplst):              # ENVP n
            cpu.push_int(var)
        envp = cpu.STACK

        # NULL ARGV
        cpu.push_int(0)
        for arg in reversed(argvlst):              # Argv n
            cpu.push_int(arg)
        argv = cpu.STACK

        # ARGC
        cpu.push_int(len(argvlst))

    def set_entry(self, entryPC):
        elf_entry = entryPC
        if self.elf.header.e_type == 'ET_DYN':
            elf_entry += self.load_addr
        self.current.PC = elf_entry
        logger.debug("Entry point updated: %016x", elf_entry)

    def load(self, filename):
        '''
        Loads and an ELF program in memory and prepares the initial CPU state.
        Creates the stack and loads the environment variables and the arguments in it.

        :param filename: pathname of the file to be executed. (used for auxv)
        :raises error:
            - 'Not matching cpu': if the program is compiled for a different architecture
            - 'Not matching memory': if the program is compiled for a different address size
        :todo: define va_randomize and read_implies_exec personality
        '''
        # load elf See binfmt_elf.c
        # read the ELF object file
        cpu = self.current
        elf = self.elf
        arch = self.arch
        addressbitsize = {'x86': 32, 'x64': 64, 'ARM': 32}[elf.get_machine_arch()]
        logger.debug("Loading %s as a %s elf", filename, arch)

        assert elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']

        # Get interpreter elf
        interpreter = None
        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_INTERP':
                continue
            interpreter_filename = elf_segment.data()[:-1]
            logger.info('Interpreter filename: %s', interpreter_filename)
            try:
                interpreter = ELFFile(open(interpreter_filename))
            except IOError:
                logger.warning('Interpreter not loaded: %s', interpreter_filename)
            break
        if interpreter is not None:
            assert interpreter.get_machine_arch() == elf.get_machine_arch()
            assert interpreter.header.e_type in ['ET_DYN', 'ET_EXEC']

        # Stack Executability
        executable_stack = False
        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_GNU_STACK':
                continue
            if elf_segment.header.p_flags & 0x01:
                executable_stack = True
            else:
                executable_stack = False
            break

        base = 0
        elf_bss = 0
        end_code = 0
        end_data = 0
        elf_brk = 0
        self.load_addr = 0

        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_LOAD':
                continue

            align = 0x1000  # elf_segment.header.p_align

            ELF_PAGEOFFSET = elf_segment.header.p_vaddr & (align - 1)

            flags = elf_segment.header.p_flags
            memsz = elf_segment.header.p_memsz + ELF_PAGEOFFSET
            offset = elf_segment.header.p_offset - ELF_PAGEOFFSET
            filesz = elf_segment.header.p_filesz + ELF_PAGEOFFSET
            vaddr = elf_segment.header.p_vaddr - ELF_PAGEOFFSET
            memsz = cpu.memory._ceil(memsz)
            if base == 0 and elf.header.e_type == 'ET_DYN':
                assert vaddr == 0
                if addressbitsize == 32:
                    base = 0x56555000
                else:
                    base = 0x555555554000

            perms = perms_from_elf(flags)
            hint = base + vaddr
            if hint == 0:
                hint = None

            logger.debug("Loading elf offset: %08x addr:%08x %08x %s" % (offset, base + vaddr, base + vaddr + memsz, perms))
            base = cpu.memory.mmapFile(hint, memsz, perms, elf_segment.stream.name, offset) - vaddr

            if self.load_addr == 0:
                self.load_addr = base + vaddr

            k = base + vaddr + filesz
            if k > elf_bss:
                elf_bss = k
            if (flags & 4) and end_code < k:  # PF_X
                end_code = k
            if end_data < k:
                end_data = k
            k = base + vaddr + memsz
            if k > elf_brk:
                elf_brk = k

        elf_entry = elf.header.e_entry
        if elf.header.e_type == 'ET_DYN':
            elf_entry += self.load_addr
        entry = elf_entry
        real_elf_brk = elf_brk

        # We need to explicitly clear bss, as fractional pages will have data from the file
        bytes_to_clear = elf_brk - elf_bss
        if bytes_to_clear > 0:
            logger.debug("Zeroing main elf fractional pages. From bss(%x) to brk(%x), %d bytes.",
                         elf_bss, elf_brk, bytes_to_clear)
            cpu.write_bytes(elf_bss, '\x00' * bytes_to_clear, force=True)

        stack_size = 0x21000

        if addressbitsize == 32:
            stack_top = 0xc0000000
        else:
            stack_top = 0x800000000000
        stack_base = stack_top - stack_size
        stack = cpu.memory.mmap(stack_base, stack_size, 'rwx', name='stack') + stack_size
        assert stack_top == stack

        reserved = cpu.memory.mmap(base + vaddr + memsz, 0x1000000, '   ')
        interpreter_base = 0
        if interpreter is not None:
            base = 0
            elf_bss = 0
            end_code = 0
            end_data = 0
            elf_brk = 0
            entry = interpreter.header.e_entry
            for elf_segment in interpreter.iter_segments():
                if elf_segment.header.p_type != 'PT_LOAD':
                    continue
                align = 0x1000  # elf_segment.header.p_align
                vaddr = elf_segment.header.p_vaddr
                filesz = elf_segment.header.p_filesz
                flags = elf_segment.header.p_flags
                offset = elf_segment.header.p_offset
                memsz = elf_segment.header.p_memsz

                ELF_PAGEOFFSET = (vaddr & (align - 1))
                memsz = memsz + ELF_PAGEOFFSET
                offset = offset - ELF_PAGEOFFSET
                filesz = filesz + ELF_PAGEOFFSET
                vaddr = vaddr - ELF_PAGEOFFSET
                memsz = cpu.memory._ceil(memsz)

                if base == 0 and interpreter.header.e_type == 'ET_DYN':
                    assert vaddr == 0
                    total_size = self._interp_total_size(interpreter)
                    base = stack_base - total_size

                if base == 0:
                    assert vaddr == 0
                perms = perms_from_elf(flags)
                hint = base + vaddr
                if hint == 0:
                    hint = None

                base = cpu.memory.mmapFile(hint, memsz, perms, elf_segment.stream.name, offset)
                base -= vaddr
                logger.debug(
                    "Loading interpreter offset: %08x addr:%08x %08x %s%s%s" %
                    (offset,
                     base +
                     vaddr,
                     base +
                     vaddr +
                     memsz,
                     (flags & 1 and 'r' or ' '),
                        (flags & 2 and 'w' or ' '),
                        (flags & 4 and 'x' or ' ')))

                k = base + vaddr + filesz
                if k > elf_bss:
                    elf_bss = k
                if (flags & 4) and end_code < k:  # PF_X
                    end_code = k
                if end_data < k:
                    end_data = k
                k = base + vaddr + memsz
                if k > elf_brk:
                    elf_brk = k

            if interpreter.header.e_type == 'ET_DYN':
                entry += base
            interpreter_base = base

            bytes_to_clear = elf_brk - elf_bss
            if bytes_to_clear > 0:
                logger.debug("Zeroing interpreter elf fractional pages. From bss(%x) to brk(%x), %d bytes.",
                             elf_bss, elf_brk, bytes_to_clear)
                cpu.write_bytes(elf_bss, '\x00' * bytes_to_clear, force=True)

        # free reserved brk space
        cpu.memory.munmap(reserved, 0x1000000)

        # load vdso
        #vdso_addr = load_vdso(addressbitsize)

        cpu.STACK = stack
        cpu.PC = entry

        logger.debug("Entry point: %016x", entry)
        logger.debug("Stack start: %016x", stack)
        logger.debug("Brk: %016x", real_elf_brk)
        logger.debug("Mappings:")
        for m in str(cpu.memory).split('\n'):
            logger.debug("  %s", m)
        self.base = base
        self.elf_bss = elf_bss
        self.end_code = end_code
        self.end_data = end_data
        self.elf_brk = real_elf_brk

        at_random = cpu.push_bytes('A' * 16)
        at_execfn = cpu.push_bytes(filename + '\x00')

        self.auxv = {
            'AT_PHDR': self.load_addr + elf.header.e_phoff,  # Program headers for program
            'AT_PHENT': elf.header.e_phentsize,       # Size of program header entry
            'AT_PHNUM': elf.header.e_phnum,           # Number of program headers
            'AT_PAGESZ': cpu.memory.page_size,         # System page size
            'AT_BASE': interpreter_base,             # Base address of interpreter
            'AT_FLAGS': elf.header.e_flags,           # Flags
            'AT_ENTRY': elf_entry,                    # Entry point of program
            'AT_UID': 1000,                         # Real uid
            'AT_EUID': 1000,                         # Effective uid
            'AT_GID': 1000,                         # Real gid
            'AT_EGID': 1000,                         # Effective gid
            'AT_CLKTCK': 100,                          # Frequency of times()
            'AT_HWCAP': 0,                            # Machine-dependent hints about processor capabilities.
            'AT_RANDOM': at_random,                    # Address of 16 random bytes.
            'AT_EXECFN': at_execfn,                    # Filename of executable.
        }

    def _to_signed_dword(self, dword):
        arch_width = self.current.address_bit_size
        if arch_width == 32:
            sdword = ctypes.c_int32(dword).value
        elif arch_width == 64:
            sdword = ctypes.c_int64(dword).value
        else:
            raise EnvironmentError("Corrupted internal CPU state (arch width is {})".format(arch_width))
        return sdword

    def _open(self, f):
        '''
        Adds a file descriptor to the current file descriptor list

        :rtype: int
        :param f: the file descriptor to add.
        :return: the index of the file descriptor in the file descr. list
        '''
        if None in self.files:
            fd = self.files.index(None)
            self.files[fd] = f
        else:
            fd = len(self.files)
            self.files.append(f)
        return fd

    def _close(self, fd):
        '''
        Removes a file descriptor from the file descriptor list
        :rtype: int
        :param fd: the file descriptor to close.
        :return: C{0} on success.
        '''
        try:
            self.files[fd].close()
            self.files[fd] = None
        except IndexError:
            raise FdError("Bad file descriptor ({})".format(fd))

    def _dup(self, fd):
        '''
        Duplicates a file descriptor
        :rtype: int
        :param fd: the file descriptor to duplicate.
        :return: C{0} on success.
        '''
        return self._open(self.files[fd])

    def _is_fd_open(self, fd):
        '''
        Determines if the fd is within range and in the file descr. list
        :param fd: the file descriptor to check.
        '''
        return fd >= 0 and fd < len(self.files) and self.files[fd] is not None

    def _get_fd(self, fd):
        if not self._is_fd_open(fd):
            raise FdError
        else:
            return self.files[fd]

    def _transform_write_data(self, data):
        '''
        Implement in subclass to transform data written by write(2)/writev(2)

        Nop by default.
        :param list data: Anything being written to a file descriptor
        :rtype list
        :return: Transformed data
        '''
        return data

    def _exit(self, message):
        procid = self.procs.index(self.current)
        self.sched()
        self.running.remove(procid)
        if len(self.running) == 0:
            raise TerminateState(message, testcase=True)

    def sys_umask(self, mask):
        '''
        umask - Set file creation mode mask
        :param int mask: New mask
        '''
        logger.debug("umask(%o)", mask)
        return os.umask(mask)

    def sys_chdir(self, path):
        '''
        chdir - Change current working directory
        :param int path: Pointer to path
        '''
        path_str = self.current.read_string(path)
        logger.debug("chdir(%s)", path_str)
        try:
            os.chdir(path_str)
            return 0
        except OSError as e:
            return e.errno

    def sys_getcwd(self, buf, size):
        '''
        getcwd - Get the current working directory
        :param int buf: Pointer to dest array
        :param size: size in bytes of the array pointed to by the buf
        :return: buf (Success), or 0
        '''

        try:
            current_dir = os.getcwd()
            length = len(current_dir) + 1

            if size > 0 and size < length:
                logger.info("GETCWD: size is greater than 0, but is smaller than the length"
                            "of the path + 1. Returning ERANGE")
                return -errno.ERANGE

            if not self.current.memory.access_ok(slice(buf, buf + length), 'w'):
                logger.info("GETCWD: buf within invalid memory. Returning EFAULT")
                return -errno.EFAULT

            self.current.write_string(buf, current_dir)
            logger.debug("getcwd(0x%08x, %u) -> <%s> (Size %d)", buf, size, current_dir, length)
            return length

        except OSError as e:
            return -e.errno

    def sys_lseek(self, fd, offset, whence):
        '''
        lseek - reposition read/write file offset

        The lseek() function repositions the file offset of the open file description associated
        with the file descriptor fd to the argument offset according to the directive whence


        :param fd: a valid file descriptor
        :param offset: the offset in bytes
        :param whence: SEEK_SET: The file offset is set to offset bytes.
                       SEEK_CUR: The file offset is set to its current location plus offset bytes.
                       SEEK_END: The file offset is set to the size of the file plus offset bytes.

        :return: 0 (Success), or EBADF (fd is not a valid file descriptor or is not open)

        '''
        signed_offset = self._to_signed_dword(offset)
        try:
            self._get_fd(fd).seek(signed_offset, whence)
        except FdError as e:
            logger.info(("LSEEK: Not valid file descriptor on lseek."
                         "Fd not seekable. Returning EBADF"))
            return -e.err

        return 0

    def sys_read(self, fd, buf, count):
        data = ''
        if count != 0:
            # TODO check count bytes from buf
            if buf not in self.current.memory:  # or not  self.current.memory.isValid(buf+count):
                logger.info("READ: buf points to invalid address. Returning EFAULT")
                return -errno.EFAULT

            try:
                # Read the data and put in tin memory
                data = self._get_fd(fd).read(count)
            except FdError as e:
                logger.info(("READ: Not valid file descriptor on read."
                             " Returning EBADF"))
                return -e.err
            self.syscall_trace.append(("_read", fd, data))
            self.current.write_bytes(buf, data)

        return len(data)

    def sys_write(self, fd, buf, count):
        ''' write - send bytes through a file descriptor
          The write system call writes up to count bytes from the buffer pointed
          to by buf to the file descriptor fd. If count is zero, write returns 0
          and optionally sets *tx_bytes to zero.

          :param fd            a valid file descriptor
          :param buf           a memory buffer
          :param count         number of bytes to send
          :return: 0          Success
                    EBADF      fd is not a valid file descriptor or is not open.
                    EFAULT     buf or tx_bytes points to an invalid address.
        '''
        data = []
        cpu = self.current
        if count != 0:
            try:
                write_fd = self._get_fd(fd)
            except FdError as e:
                logger.error("WRITE: Not valid file descriptor (%d). Returning -%d", fd, e.err)
                return -e.err

            # TODO check count bytes from buf
            if buf not in cpu.memory or buf + count not in cpu.memory:
                logger.debug("WRITE: buf points to invalid address. Returning EFAULT")
                return -errno.EFAULT

            if fd > 2 and write_fd.is_full():
                cpu.PC -= cpu.instruction.size
                self.wait([], [fd], None)
                raise RestartSyscall()

            data = cpu.read_bytes(buf, count)
            data = self._transform_write_data(data)
            write_fd.write(data)

            for line in ''.join([str(x) for x in data]).split('\n'):
                logger.debug("WRITE(%d, 0x%08x, %d) -> <%.48r>",
                             fd,
                             buf,
                             count,
                             line)
            self.syscall_trace.append(("_write", fd, data))
            self.signal_transmit(fd)

        return len(data)

    def sys_access(self, buf, mode):
        '''
        Checks real user's permissions for a file
        :rtype: int

        :param buf: a buffer containing the pathname to the file to check its permissions.
        :param mode: the access permissions to check.
        :return:
            -  C{0} if the calling process can access the file in the desired mode.
            - C{-1} if the calling process can not access the file in the desired mode.
        '''
        filename = ""
        for i in xrange(0, 255):
            c = Operators.CHR(self.current.read_int(buf + i, 8))
            if c == '\x00':
                break
            filename += c

        if os.access(filename, mode):
            return 0
        else:
            return -1

    def sys_newuname(self, old_utsname):
        '''
        Writes system information in the variable C{old_utsname}.
        :rtype: int
        :param old_utsname: the buffer to write the system info.
        :return: C{0} on success
        '''
        from datetime import datetime<commit_msg>Partially implement fork(2) (#892)<commit_after>import StringIO
import errno
import fcntl
import logging
import os
import random
import struct
import ctypes
import socket

# Remove in favor of binary.py
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
from elftools.elf.descriptions import describe_symbol_type

from ..core.cpu.abstractcpu import Interruption, Syscall, ConcretizeArgument
from ..core.cpu.cpufactory import CpuFactory
from ..core.cpu.binja import BinjaCpu
from ..core.memory import SMemory32, SMemory64, Memory32, Memory64
from ..core.smtlib import Operators, ConstraintSet, SolverException, solver
from ..core.cpu.arm import *
from ..core.executor import TerminateState
from ..platforms.platform import Platform, SyscallNotImplemented
from ..utils.helpers import issymbolic, is_binja_disassembler
from . import linux_syscalls

logger = logging.getLogger(__name__)


class RestartSyscall(Exception):
    pass


class Deadlock(Exception):
    pass


class EnvironmentError(RuntimeError):
    pass


class FdError(Exception):
    def __init__(self, message='', err=errno.EBADF):
        self.err = err
        super(FdError, self).__init__(message)


def perms_from_elf(elf_flags):
    return ['   ', '  x', ' w ', ' wx', 'r  ', 'r x', 'rw ', 'rwx'][elf_flags & 7]


def perms_from_protflags(prot_flags):
    return ['   ', 'r  ', ' w ', 'rw ', '  x', 'r x', ' wx', 'rwx'][prot_flags & 7]


def mode_from_flags(file_flags):
    return {os.O_RDWR: 'r+', os.O_RDONLY: 'r', os.O_WRONLY: 'w'}[file_flags & 7]


class File(object):
    def __init__(self, path, flags):
        # TODO: assert file is seekable otherwise we should save what was
        # read/write to the state
        mode = mode_from_flags(flags)
        self.file = file(path, mode)

    def __getstate__(self):
        state = {}
        state['name'] = self.name
        state['mode'] = self.mode
        try:
            state['pos'] = self.tell()
        except IOError:
            # This is to handle special files like /dev/tty
            state['pos'] = None
        return state

    def __setstate__(self, state):
        name = state['name']
        mode = state['mode']
        pos = state['pos']
        self.file = file(name, mode)
        if pos is not None:
            self.seek(pos)

    @property
    def name(self):
        return self.file.name

    @property
    def mode(self):
        return self.file.mode

    def stat(self):
        return os.fstat(self.fileno())

    def ioctl(self, request, argp):
        # argp ignored..
        return fcntl.fcntl(self, request)

    def tell(self, *args):
        return self.file.tell(*args)

    def seek(self, *args):
        return self.file.seek(*args)

    def write(self, buf):
        for c in buf:
            self.file.write(c)

    def read(self, *args):
        return self.file.read(*args)

    def close(self, *args):
        return self.file.close(*args)

    def fileno(self, *args):
        return self.file.fileno(*args)

    def is_full(self):
        return False

    def sync(self):
        '''
        Flush buffered data. Currently not implemented.
        '''
        return


class Directory(File):
    def __init__(self, path, flags):
        assert(os.path.isdir(path))

        self.fd = os.open(path, flags)
        self.path = path
        self.flags = flags

    def __getstate__(self):
        state = {}
        state['path'] = self.path
        state['flags'] = self.flags
        return state

    def __setstate__(self, state):
        self.path = state['path']
        self.flags = state['flags']
        self.fd = os.open(self.path, self.flags)

    @property
    def name(self):
        return self.path

    @property
    def mode(self):
        return mode_from_flags(self.flags)

    def tell(self, *args):
        return 0

    def seek(self, *args):
        return 0

    def write(self, buf):
        raise FdError("Is a directory", errno.EBADF)

    def read(self, *args):
        raise FdError("Is a directory", errno.EISDIR)

    def close(self, *args):
        return os.close(self.fd)

    def fileno(self, *args):
        return self.fd


class SymbolicFile(File):
    '''
    Represents a symbolic file.
    '''

    def __init__(self, constraints, path="sfile", mode='rw', max_size=100,
                 wildcard='+'):
        '''
        Builds a symbolic file

        :param constraints: the SMT constraints
        :param str path: the pathname of the symbolic file
        :param str mode: the access permissions of the symbolic file
        :param max_size: Maximum amount of bytes of the symbolic file
        :param str wildcard: Wildcard to be used in symbolic file
        '''
        super(SymbolicFile, self).__init__(path, mode)

        # read the concrete data using the parent the read() form the File class
        data = self.file.read()

        self._constraints = constraints
        self.pos = 0
        self.max_size = min(len(data), max_size)

        # build the constraints array
        size = len(data)
        self.array = constraints.new_array(name=self.name, index_max=size)

        symbols_cnt = 0
        for i in range(size):
            if data[i] != wildcard:
                self.array[i] = data[i]
            else:
                symbols_cnt += 1

        if symbols_cnt > max_size:
            logger.warning(("Found more wilcards in the file than free ",
                            "symbolic values allowed (%d > %d)"),
                           symbols_cnt,
                           max_size)
        else:
            logger.debug("Found %d free symbolic values on file %s",
                         symbols_cnt,
                         self.name)

    def __getstate__(self):
        state = {}
        state['array'] = self.array
        state['pos'] = self.pos
        state['max_size'] = self.max_size
        return state

    def __setstate__(self, state):
        self.pos = state['pos']
        self.max_size = state['max_size']
        self.array = state['array']

    def tell(self):
        '''
        Returns the read/write file offset
        :rtype: int
        :return: the read/write file offset.
        '''
        return self.pos

    def seek(self, offset, whence=os.SEEK_SET):
        '''
        Repositions the file C{offset} according to C{whence}.
        Returns the resulting offset or -1 in case of error.
        :rtype: int
        :return: the file offset.
        '''
        assert isinstance(offset, (int, long))
        assert whence in (os.SEEK_SET, os.SEEK_CUR, os.SEEK_END)

        new_position = 0
        if whence == os.SEEK_SET:
            new_position = offset
        elif whence == os.SEEK_CUR:
            new_position = self.pos + offset
        elif whence == os.SEEK_END:
            new_position = self.max_size + offset

        if new_position < 0:
            return -1

        self.pos = new_position

        return self.pos

    def read(self, count):
        '''
        Reads up to C{count} bytes from the file.
        :rtype: list
        :return: the list of symbolic bytes read
        '''
        if self.pos > self.max_size:
            return []
        else:
            size = min(count, self.max_size - self.pos)
            ret = [self.array[i] for i in xrange(self.pos, self.pos + size)]
            self.pos += size
            return ret

    def write(self, data):
        '''
        Writes the symbolic bytes in C{data} onto the file.
        '''
        size = min(len(data), self.max_size - self.pos)
        for i in xrange(self.pos, self.pos + size):
            self.array[i] = data[i - self.pos]


class SocketDesc(object):
    '''
    Represents a socket descriptor (i.e. value returned by socket(2)
    '''

    def __init__(self, domain=None, socket_type=None, protocol=None):
        self.domain = domain
        self.socket_type = socket_type
        self.protocol = protocol


class Socket(object):
    def stat(self):
        from collections import namedtuple
        stat_result = namedtuple('stat_result', ['st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid',
                                                 'st_size', 'st_atime', 'st_mtime', 'st_ctime', 'st_blksize', 'st_blocks', 'st_rdev'])
        return stat_result(8592, 11, 9, 1, 1000, 5, 0, 1378673920, 1378673920, 1378653796, 0x400, 0x8808, 0)

    @staticmethod
    def pair():
        a = Socket()
        b = Socket()
        a.connect(b)
        return a, b

    def __init__(self):
        self.buffer = []  # queue os bytes
        self.peer = None

    def __repr__(self):
        return "SOCKET(%x, %r, %x)" % (hash(self), self.buffer, hash(self.peer))

    def is_connected(self):
        return self.peer is not None

    def is_empty(self):
        return not self.buffer

    def is_full(self):
        return len(self.buffer) > 2 * 1024

    def connect(self, peer):
        assert not self.is_connected()
        assert not peer.is_connected()
        self.peer = peer
        if peer.peer is None:
            peer.peer = self

    def read(self, size):
        return self.receive(size)

    def receive(self, size):
        rx_bytes = min(size, len(self.buffer))
        ret = []
        for i in xrange(rx_bytes):
            ret.append(self.buffer.pop())
        return ret

    def write(self, buf):
        assert self.is_connected()
        return self.peer._transmit(buf)

    def _transmit(self, buf):
        for c in buf:
            self.buffer.insert(0, c)
        return len(buf)

    def sync(self):
        raise FdError("Invalid sync() operation on Socket", errno.EINVAL)

    def seek(self, *args):
        raise FdError("Invalid lseek() operation on Socket", errno.EINVAL)


class Linux(Platform):
    '''
    A simple Linux Operating System Platform.
    This class emulates the most common Linux system calls
    '''

    # from /usr/include/asm-generic/resource.h
    RLIMIT_NOFILE = 7  # /* max number of open files */
    FCNTL_FDCWD = -100  # /* Special value used to indicate openat should use the cwd */

    def __init__(self, program, argv=None, envp=None, disasm='capstone', **kwargs):
        '''
        Builds a Linux OS platform
        :param string program: The path to ELF binary
        :param string disasm: Disassembler to be used
        :param list argv: The argv array; not including binary.
        :param list envp: The ENV variables.
        :ivar files: List of active file descriptors
        :type files: list[Socket] or list[File]
        '''
        super(Linux, self).__init__(path=program, **kwargs)

        self.program = program
        self.clocks = 0
        self.files = []
        self.syscall_trace = []
        # Many programs to support SLinux
        self.programs = program
        self.disasm = disasm

        # dict of [int -> (int, int)] where tuple is (soft, hard) limits
        self._rlimits = {
            self.RLIMIT_NOFILE: (256, 1024)
        }

        if program is not None:
            self.elf = ELFFile(file(program))
            # FIXME (theo) self.arch is actually mode as initialized in the CPUs,
            # make things consistent and perhaps utilize a global mapping for this
            self.arch = {'x86': 'i386', 'x64': 'amd64', 'ARM': 'armv7'}[self.elf.get_machine_arch()]

            self._init_cpu(self.arch)
            self._init_std_fds()
            self._execve(program, argv, envp)

    @classmethod
    def empty_platform(cls, arch):
        '''
        Create a platform without an ELF loaded.

        :param str arch: The architecture of the new platform
        :rtype: Linux
        '''
        platform = cls(None)
        platform._init_cpu(arch)
        platform._init_std_fds()
        return platform

    def _init_std_fds(self):
        # open standard files stdin, stdout, stderr
        logger.debug("Opening file descriptors (0,1,2) (STDIN, STDOUT, STDERR)")
        self.input = Socket()
        self.output = Socket()
        self.stderr = Socket()

        stdin = Socket()
        stdout = Socket()
        stderr = Socket()
        # A transmit to stdin,stdout or stderr will be directed to out
        stdin.peer = self.output
        stdout.peer = self.output
        stderr.peer = self.stderr
        # A receive from stdin will get data from input
        self.input.peer = stdin
        # A receive on stdout or stderr will return no data (rx_bytes: 0)

        assert self._open(stdin) == 0
        assert self._open(stdout) == 1
        assert self._open(stderr) == 2

    def _init_cpu(self, arch):
        # create memory and CPU
        cpu = self._mk_proc(arch)
        self.procs = [cpu]
        self._current = 0
        self._function_abi = CpuFactory.get_function_abi(cpu, 'linux', arch)
        self._syscall_abi = CpuFactory.get_syscall_abi(cpu, 'linux', arch)

    def _find_symbol(self, name):
        symbol_tables = (s for s in self.elf.iter_sections()
                         if isinstance(s, SymbolTableSection))

        for section in symbol_tables:
            if section['sh_entsize'] == 0:
                continue

            for symbol in section.iter_symbols():
                if describe_symbol_type(symbol['st_info']['type']) == 'FUNC':
                    if symbol.name == name:
                        return symbol['st_value']

        return None

    def _execve(self, program, argv, envp):
        '''
        Load `program` and establish program state, such as stack and arguments.

        :param program str: The ELF binary to load
        :param argv list: argv array
        :param envp list: envp array
        '''
        argv = [] if argv is None else argv
        envp = [] if envp is None else envp

        logger.debug("Loading %s as a %s elf", program, self.arch)

        self.load(program)
        self._arch_specific_init()

        self._stack_top = self.current.STACK
        self.setup_stack([program] + argv, envp)

        nprocs = len(self.procs)
        nfiles = len(self.files)
        assert nprocs > 0
        self.running = range(nprocs)

        # Each process can wait for one timeout
        self.timers = [None] * nprocs
        # each fd has a waitlist
        self.rwait = [set() for _ in xrange(nfiles)]
        self.twait = [set() for _ in xrange(nfiles)]

        # Install event forwarders
        for proc in self.procs:
            self.forward_events_from(proc)

    def _mk_proc(self, arch):
        mem = Memory32() if arch in {'i386', 'armv7'} else Memory64()
        cpu = CpuFactory.get_cpu(mem, arch)
        return cpu

    @property
    def current(self):
        return self.procs[self._current]

    def __getstate__(self):
        state = super(Linux, self).__getstate__()
        state['clocks'] = self.clocks
        state['input'] = self.input.buffer
        state['output'] = self.output.buffer

        # Store the type of file descriptor and the respective contents
        state_files = []
        for fd in self.files:
            if isinstance(fd, Socket):
                state_files.append(('Socket', fd.buffer))
            else:
                state_files.append(('File', fd))
        state['files'] = state_files
        state['rlimits'] = self._rlimits

        state['procs'] = self.procs
        state['current'] = self._current
        state['running'] = self.running
        state['rwait'] = self.rwait
        state['twait'] = self.twait
        state['timers'] = self.timers
        state['syscall_trace'] = self.syscall_trace
        state['base'] = self.base
        state['elf_bss'] = self.elf_bss
        state['end_code'] = self.end_code
        state['end_data'] = self.end_data
        state['elf_brk'] = self.elf_brk
        state['auxv'] = self.auxv
        state['program'] = self.program
        state['functionabi'] = self._function_abi
        state['syscallabi'] = self._syscall_abi
        state['uname_machine'] = self._uname_machine

        if hasattr(self, '_arm_tls_memory'):
            state['_arm_tls_memory'] = self._arm_tls_memory
        return state

    def __setstate__(self, state):
        """
        :todo: some asserts
        :todo: fix deps? (last line)
        """
        super(Linux, self).__setstate__(state)

        self.input = Socket()
        self.input.buffer = state['input']
        self.output = Socket()
        self.output.buffer = state['output']

        # fetch each file descriptor (Socket or File())
        self.files = []
        for ty, buf in state['files']:
            if ty == 'Socket':
                f = Socket()
                f.buffer = buf
                self.files.append(f)
            else:
                self.files.append(buf)

        self.files[0].peer = self.output
        self.files[1].peer = self.output
        self.files[2].peer = self.output
        self.input.peer = self.files[0]
        self._rlimits = state['rlimits']

        self.procs = state['procs']
        self._current = state['current']
        self.running = state['running']
        self.rwait = state['rwait']
        self.twait = state['twait']
        self.timers = state['timers']
        self.clocks = state['clocks']

        self.syscall_trace = state['syscall_trace']
        self.base = state['base']
        self.elf_bss = state['elf_bss']
        self.end_code = state['end_code']
        self.end_data = state['end_data']
        self.elf_brk = state['elf_brk']
        self.auxv = state['auxv']
        self.program = state['program']
        self._function_abi = state['functionabi']
        self._syscall_abi = state['syscallabi']
        self._uname_machine = state['uname_machine']
        if '_arm_tls_memory' in state:
            self._arm_tls_memory = state['_arm_tls_memory']

        # Install event forwarders
        for proc in self.procs:
            self.forward_events_from(proc)

    def _init_arm_kernel_helpers(self):
        '''
        ARM kernel helpers

        https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
        '''

        page_data = bytearray('\xf1\xde\xfd\xe7' * 1024)

        # Extracted from a RPi2
        preamble = (
            'ff0300ea' +
            '650400ea' +
            'f0ff9fe5' +
            '430400ea' +
            '220400ea' +
            '810400ea' +
            '000400ea' +
            '870400ea'
        ).decode('hex')

        # XXX(yan): The following implementations of cmpxchg and cmpxchg64 were
        # handwritten to not use any exclusive instructions (e.g. ldrexd) or
        # locking. For actual implementations, refer to
        # arch/arm64/kernel/kuser32.S in the Linux source code.
        __kuser_cmpxchg64 = (
            '30002de9' +  # push    {r4, r5}
            '08c09de5' +  # ldr     ip, [sp, #8]
            '30009ce8' +  # ldm     ip, {r4, r5}
            '010055e1' +  # cmp     r5, r1
            '00005401' +  # cmpeq   r4, r0
            '0100a013' +  # movne   r0, #1
            '0000a003' +  # moveq   r0, #0
            '0c008c08' +  # stmeq   ip, {r2, r3}
            '3000bde8' +  # pop     {r4, r5}
            '1eff2fe1'   # bx      lr
        ).decode('hex')

        __kuser_dmb = (
            '5bf07ff5' +  # dmb ish
            '1eff2fe1'   # bx lr
        ).decode('hex')

        __kuser_cmpxchg = (
            '003092e5' +  # ldr     r3, [r2]
            '000053e1' +  # cmp     r3, r0
            '0000a003' +  # moveq   r0, #0
            '00108205' +  # streq   r1, [r2]
            '0100a013' +  # movne   r0, #1
            '1eff2fe1'   # bx      lr
        ).decode('hex')

        # Map a TLS segment
        self._arm_tls_memory = self.current.memory.mmap(None, 4, 'rw ')

        __kuser_get_tls = (
            '04009FE5' +  # ldr r0, [pc, #4]
            '010090e8' +  # ldm r0, {r0}
            '1eff2fe1'   # bx lr
        ).decode('hex') + struct.pack('<I', self._arm_tls_memory)

        tls_area = '\x00' * 12

        version = struct.pack('<I', 5)

        def update(address, code):
            page_data[address:address + len(code)] = code

        # Offsets from Documentation/arm/kernel_user_helpers.txt in Linux
        update(0x000, preamble)
        update(0xf60, __kuser_cmpxchg64)
        update(0xfa0, __kuser_dmb)
        update(0xfc0, __kuser_cmpxchg)
        update(0xfe0, __kuser_get_tls)
        update(0xff0, tls_area)
        update(0xffc, version)

        self.current.memory.mmap(0xffff0000, len(page_data), 'r x', page_data)

    def load_vdso(self, bits):
        # load vdso #TODO or #IGNORE
        vdso_top = {32: 0x7fff0000, 64: 0x7fff00007fff0000}[bits]
        vdso_size = len(file('vdso%2d.dump' % bits).read())
        vdso_addr = self.memory.mmapFile(self.memory._floor(vdso_top - vdso_size),
                                         vdso_size,
                                         'r x',
                                         {32: 'vdso32.dump', 64: 'vdso64.dump'}[bits],
                                         0)
        return vdso_addr

    def setup_stack(self, argv, envp):
        '''
        :param Cpu cpu: The cpu instance
        :param argv: list of parameters for the program to execute.
        :param envp: list of environment variables for the program to execute.

        http://www.phrack.org/issues.html?issue=58&id=5#article
         position            content                     size (bytes) + comment
         ----------------------------------------------------------------------
         stack pointer ->  [ argc = number of args ]     4
                         [ argv[0] (pointer) ]         4   (program name)
                         [ argv[1] (pointer) ]         4
                         [ argv[..] (pointer) ]        4 * x
                         [ argv[n - 1] (pointer) ]     4
                         [ argv[n] (pointer) ]         4   (= NULL)

                         [ envp[0] (pointer) ]         4
                         [ envp[1] (pointer) ]         4
                         [ envp[..] (pointer) ]        4
                         [ envp[term] (pointer) ]      4   (= NULL)

                         [ auxv[0] (Elf32_auxv_t) ]    8
                         [ auxv[1] (Elf32_auxv_t) ]    8
                         [ auxv[..] (Elf32_auxv_t) ]   8
                         [ auxv[term] (Elf32_auxv_t) ] 8   (= AT_NULL vector)

                         [ padding ]                   0 - 16

                         [ argument ASCIIZ strings ]   >= 0
                         [ environment ASCIIZ str. ]   >= 0

         (0xbffffffc)      [ end marker ]                4   (= NULL)

         (0xc0000000)      < top of stack >              0   (virtual)
         ----------------------------------------------------------------------
        '''
        cpu = self.current

        # In case setup_stack() is called again, we make sure we're growing the
        # stack from the original top
        cpu.STACK = self._stack_top

        auxv = self.auxv
        logger.debug("Setting argv, envp and auxv.")
        logger.debug("\tArguments: %s", repr(argv))
        if envp:
            logger.debug("\tEnvironment:")
            for e in envp:
                logger.debug("\t\t%s", repr(e))

        logger.debug("\tAuxv:")
        for name, val in auxv.items():
            logger.debug("\t\t%s: %s", name, hex(val))

        # We save the argument and environment pointers
        argvlst = []
        envplst = []

        # end envp marker empty string
        for evar in envp:
            cpu.push_bytes('\x00')
            envplst.append(cpu.push_bytes(evar))

        for arg in argv:
            cpu.push_bytes('\x00')
            argvlst.append(cpu.push_bytes(arg))

        # Put all auxv strings into the string stack area.
        # And replace the value be its pointer

        for name, value in auxv.items():
            if hasattr(value, '__len__'):
                cpu.push_bytes(value)
                auxv[name] = cpu.STACK

        # The "secure execution" mode of secure_getenv() is controlled by the
        # AT_SECURE flag contained in the auxiliary vector passed from the
        # kernel to user space.
        auxvnames = {
            'AT_IGNORE': 1,  # Entry should be ignored
            'AT_EXECFD': 2,  # File descriptor of program
            'AT_PHDR': 3,  # Program headers for program
            'AT_PHENT': 4,  # Size of program header entry
            'AT_PHNUM': 5,  # Number of program headers
            'AT_PAGESZ': 6,  # System page size
            'AT_BASE': 7,  # Base address of interpreter
            'AT_FLAGS': 8,  # Flags
            'AT_ENTRY': 9,  # Entry point of program
            'AT_NOTELF': 10,  # Program is not ELF
            'AT_UID': 11,  # Real uid
            'AT_EUID': 12,  # Effective uid
            'AT_GID': 13,  # Real gid
            'AT_EGID': 14,  # Effective gid
            'AT_CLKTCK': 17,  # Frequency of times()
            'AT_PLATFORM': 15,  # String identifying platform.
            'AT_HWCAP': 16,  # Machine-dependent hints about processor capabilities.
            'AT_FPUCW': 18,  # Used FPU control word.
            'AT_SECURE': 23,  # Boolean, was exec setuid-like?
            'AT_BASE_PLATFORM': 24,  # String identifying real platforms.
            'AT_RANDOM': 25,  # Address of 16 random bytes.
            'AT_EXECFN': 31,  # Filename of executable.
            'AT_SYSINFO': 32,  # Pointer to the global system page used for system calls and other nice things.
            'AT_SYSINFO_EHDR': 33,  # Pointer to the global system page used for system calls and other nice things.
        }
        # AT_NULL
        cpu.push_int(0)
        cpu.push_int(0)
        for name, val in auxv.items():
            cpu.push_int(val)
            cpu.push_int(auxvnames[name])

        # NULL ENVP
        cpu.push_int(0)
        for var in reversed(envplst):              # ENVP n
            cpu.push_int(var)
        envp = cpu.STACK

        # NULL ARGV
        cpu.push_int(0)
        for arg in reversed(argvlst):              # Argv n
            cpu.push_int(arg)
        argv = cpu.STACK

        # ARGC
        cpu.push_int(len(argvlst))

    def set_entry(self, entryPC):
        elf_entry = entryPC
        if self.elf.header.e_type == 'ET_DYN':
            elf_entry += self.load_addr
        self.current.PC = elf_entry
        logger.debug("Entry point updated: %016x", elf_entry)

    def load(self, filename):
        '''
        Loads and an ELF program in memory and prepares the initial CPU state.
        Creates the stack and loads the environment variables and the arguments in it.

        :param filename: pathname of the file to be executed. (used for auxv)
        :raises error:
            - 'Not matching cpu': if the program is compiled for a different architecture
            - 'Not matching memory': if the program is compiled for a different address size
        :todo: define va_randomize and read_implies_exec personality
        '''
        # load elf See binfmt_elf.c
        # read the ELF object file
        cpu = self.current
        elf = self.elf
        arch = self.arch
        addressbitsize = {'x86': 32, 'x64': 64, 'ARM': 32}[elf.get_machine_arch()]
        logger.debug("Loading %s as a %s elf", filename, arch)

        assert elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']

        # Get interpreter elf
        interpreter = None
        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_INTERP':
                continue
            interpreter_filename = elf_segment.data()[:-1]
            logger.info('Interpreter filename: %s', interpreter_filename)
            try:
                interpreter = ELFFile(open(interpreter_filename))
            except IOError:
                logger.warning('Interpreter not loaded: %s', interpreter_filename)
            break
        if interpreter is not None:
            assert interpreter.get_machine_arch() == elf.get_machine_arch()
            assert interpreter.header.e_type in ['ET_DYN', 'ET_EXEC']

        # Stack Executability
        executable_stack = False
        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_GNU_STACK':
                continue
            if elf_segment.header.p_flags & 0x01:
                executable_stack = True
            else:
                executable_stack = False
            break

        base = 0
        elf_bss = 0
        end_code = 0
        end_data = 0
        elf_brk = 0
        self.load_addr = 0

        for elf_segment in elf.iter_segments():
            if elf_segment.header.p_type != 'PT_LOAD':
                continue

            align = 0x1000  # elf_segment.header.p_align

            ELF_PAGEOFFSET = elf_segment.header.p_vaddr & (align - 1)

            flags = elf_segment.header.p_flags
            memsz = elf_segment.header.p_memsz + ELF_PAGEOFFSET
            offset = elf_segment.header.p_offset - ELF_PAGEOFFSET
            filesz = elf_segment.header.p_filesz + ELF_PAGEOFFSET
            vaddr = elf_segment.header.p_vaddr - ELF_PAGEOFFSET
            memsz = cpu.memory._ceil(memsz)
            if base == 0 and elf.header.e_type == 'ET_DYN':
                assert vaddr == 0
                if addressbitsize == 32:
                    base = 0x56555000
                else:
                    base = 0x555555554000

            perms = perms_from_elf(flags)
            hint = base + vaddr
            if hint == 0:
                hint = None

            logger.debug("Loading elf offset: %08x addr:%08x %08x %s" % (offset, base + vaddr, base + vaddr + memsz, perms))
            base = cpu.memory.mmapFile(hint, memsz, perms, elf_segment.stream.name, offset) - vaddr

            if self.load_addr == 0:
                self.load_addr = base + vaddr

            k = base + vaddr + filesz
            if k > elf_bss:
                elf_bss = k
            if (flags & 4) and end_code < k:  # PF_X
                end_code = k
            if end_data < k:
                end_data = k
            k = base + vaddr + memsz
            if k > elf_brk:
                elf_brk = k

        elf_entry = elf.header.e_entry
        if elf.header.e_type == 'ET_DYN':
            elf_entry += self.load_addr
        entry = elf_entry
        real_elf_brk = elf_brk

        # We need to explicitly clear bss, as fractional pages will have data from the file
        bytes_to_clear = elf_brk - elf_bss
        if bytes_to_clear > 0:
            logger.debug("Zeroing main elf fractional pages. From bss(%x) to brk(%x), %d bytes.",
                         elf_bss, elf_brk, bytes_to_clear)
            cpu.write_bytes(elf_bss, '\x00' * bytes_to_clear, force=True)

        stack_size = 0x21000

        if addressbitsize == 32:
            stack_top = 0xc0000000
        else:
            stack_top = 0x800000000000
        stack_base = stack_top - stack_size
        stack = cpu.memory.mmap(stack_base, stack_size, 'rwx', name='stack') + stack_size
        assert stack_top == stack

        reserved = cpu.memory.mmap(base + vaddr + memsz, 0x1000000, '   ')
        interpreter_base = 0
        if interpreter is not None:
            base = 0
            elf_bss = 0
            end_code = 0
            end_data = 0
            elf_brk = 0
            entry = interpreter.header.e_entry
            for elf_segment in interpreter.iter_segments():
                if elf_segment.header.p_type != 'PT_LOAD':
                    continue
                align = 0x1000  # elf_segment.header.p_align
                vaddr = elf_segment.header.p_vaddr
                filesz = elf_segment.header.p_filesz
                flags = elf_segment.header.p_flags
                offset = elf_segment.header.p_offset
                memsz = elf_segment.header.p_memsz

                ELF_PAGEOFFSET = (vaddr & (align - 1))
                memsz = memsz + ELF_PAGEOFFSET
                offset = offset - ELF_PAGEOFFSET
                filesz = filesz + ELF_PAGEOFFSET
                vaddr = vaddr - ELF_PAGEOFFSET
                memsz = cpu.memory._ceil(memsz)

                if base == 0 and interpreter.header.e_type == 'ET_DYN':
                    assert vaddr == 0
                    total_size = self._interp_total_size(interpreter)
                    base = stack_base - total_size

                if base == 0:
                    assert vaddr == 0
                perms = perms_from_elf(flags)
                hint = base + vaddr
                if hint == 0:
                    hint = None

                base = cpu.memory.mmapFile(hint, memsz, perms, elf_segment.stream.name, offset)
                base -= vaddr
                logger.debug(
                    "Loading interpreter offset: %08x addr:%08x %08x %s%s%s" %
                    (offset,
                     base +
                     vaddr,
                     base +
                     vaddr +
                     memsz,
                     (flags & 1 and 'r' or ' '),
                        (flags & 2 and 'w' or ' '),
                        (flags & 4 and 'x' or ' ')))

                k = base + vaddr + filesz
                if k > elf_bss:
                    elf_bss = k
                if (flags & 4) and end_code < k:  # PF_X
                    end_code = k
                if end_data < k:
                    end_data = k
                k = base + vaddr + memsz
                if k > elf_brk:
                    elf_brk = k

            if interpreter.header.e_type == 'ET_DYN':
                entry += base
            interpreter_base = base

            bytes_to_clear = elf_brk - elf_bss
            if bytes_to_clear > 0:
                logger.debug("Zeroing interpreter elf fractional pages. From bss(%x) to brk(%x), %d bytes.",
                             elf_bss, elf_brk, bytes_to_clear)
                cpu.write_bytes(elf_bss, '\x00' * bytes_to_clear, force=True)

        # free reserved brk space
        cpu.memory.munmap(reserved, 0x1000000)

        # load vdso
        #vdso_addr = load_vdso(addressbitsize)

        cpu.STACK = stack
        cpu.PC = entry

        logger.debug("Entry point: %016x", entry)
        logger.debug("Stack start: %016x", stack)
        logger.debug("Brk: %016x", real_elf_brk)
        logger.debug("Mappings:")
        for m in str(cpu.memory).split('\n'):
            logger.debug("  %s", m)
        self.base = base
        self.elf_bss = elf_bss
        self.end_code = end_code
        self.end_data = end_data
        self.elf_brk = real_elf_brk

        at_random = cpu.push_bytes('A' * 16)
        at_execfn = cpu.push_bytes(filename + '\x00')

        self.auxv = {
            'AT_PHDR': self.load_addr + elf.header.e_phoff,  # Program headers for program
            'AT_PHENT': elf.header.e_phentsize,       # Size of program header entry
            'AT_PHNUM': elf.header.e_phnum,           # Number of program headers
            'AT_PAGESZ': cpu.memory.page_size,         # System page size
            'AT_BASE': interpreter_base,             # Base address of interpreter
            'AT_FLAGS': elf.header.e_flags,           # Flags
            'AT_ENTRY': elf_entry,                    # Entry point of program
            'AT_UID': 1000,                         # Real uid
            'AT_EUID': 1000,                         # Effective uid
            'AT_GID': 1000,                         # Real gid
            'AT_EGID': 1000,                         # Effective gid
            'AT_CLKTCK': 100,                          # Frequency of times()
            'AT_HWCAP': 0,                            # Machine-dependent hints about processor capabilities.
            'AT_RANDOM': at_random,                    # Address of 16 random bytes.
            'AT_EXECFN': at_execfn,                    # Filename of executable.
        }

    def _to_signed_dword(self, dword):
        arch_width = self.current.address_bit_size
        if arch_width == 32:
            sdword = ctypes.c_int32(dword).value
        elif arch_width == 64:
            sdword = ctypes.c_int64(dword).value
        else:
            raise EnvironmentError("Corrupted internal CPU state (arch width is {})".format(arch_width))
        return sdword

    def _open(self, f):
        '''
        Adds a file descriptor to the current file descriptor list

        :rtype: int
        :param f: the file descriptor to add.
        :return: the index of the file descriptor in the file descr. list
        '''
        if None in self.files:
            fd = self.files.index(None)
            self.files[fd] = f
        else:
            fd = len(self.files)
            self.files.append(f)
        return fd

    def _close(self, fd):
        '''
        Removes a file descriptor from the file descriptor list
        :rtype: int
        :param fd: the file descriptor to close.
        :return: C{0} on success.
        '''
        try:
            self.files[fd].close()
            self.files[fd] = None
        except IndexError:
            raise FdError("Bad file descriptor ({})".format(fd))

    def _dup(self, fd):
        '''
        Duplicates a file descriptor
        :rtype: int
        :param fd: the file descriptor to duplicate.
        :return: C{0} on success.
        '''
        return self._open(self.files[fd])

    def _is_fd_open(self, fd):
        '''
        Determines if the fd is within range and in the file descr. list
        :param fd: the file descriptor to check.
        '''
        return fd >= 0 and fd < len(self.files) and self.files[fd] is not None

    def _get_fd(self, fd):
        if not self._is_fd_open(fd):
            raise FdError
        else:
            return self.files[fd]

    def _transform_write_data(self, data):
        '''
        Implement in subclass to transform data written by write(2)/writev(2)

        Nop by default.
        :param list data: Anything being written to a file descriptor
        :rtype list
        :return: Transformed data
        '''
        return data

    def _exit(self, message):
        procid = self.procs.index(self.current)
        self.sched()
        self.running.remove(procid)
        if len(self.running) == 0:
            raise TerminateState(message, testcase=True)

    def sys_umask(self, mask):
        '''
        umask - Set file creation mode mask
        :param int mask: New mask
        '''
        logger.debug("umask(%o)", mask)
        return os.umask(mask)

    def sys_chdir(self, path):
        '''
        chdir - Change current working directory
        :param int path: Pointer to path
        '''
        path_str = self.current.read_string(path)
        logger.debug("chdir(%s)", path_str)
        try:
            os.chdir(path_str)
            return 0
        except OSError as e:
            return e.errno

    def sys_getcwd(self, buf, size):
        '''
        getcwd - Get the current working directory
        :param int buf: Pointer to dest array
        :param size: size in bytes of the array pointed to by the buf
        :return: buf (Success), or 0
        '''

        try:
            current_dir = os.getcwd()
            length = len(current_dir) + 1

            if size > 0 and size < length:
                logger.info("GETCWD: size is greater than 0, but is smaller than the length"
                            "of the path + 1. Returning ERANGE")
                return -errno.ERANGE

            if not self.current.memory.access_ok(slice(buf, buf + length), 'w'):
                logger.info("GETCWD: buf within invalid memory. Returning EFAULT")
                return -errno.EFAULT

            self.current.write_string(buf, current_dir)
            logger.debug("getcwd(0x%08x, %u) -> <%s> (Size %d)", buf, size, current_dir, length)
            return length

        except OSError as e:
            return -e.errno

    def sys_lseek(self, fd, offset, whence):
        '''
        lseek - reposition read/write file offset

        The lseek() function repositions the file offset of the open file description associated
        with the file descriptor fd to the argument offset according to the directive whence


        :param fd: a valid file descriptor
        :param offset: the offset in bytes
        :param whence: SEEK_SET: The file offset is set to offset bytes.
                       SEEK_CUR: The file offset is set to its current location plus offset bytes.
                       SEEK_END: The file offset is set to the size of the file plus offset bytes.

        :return: 0 (Success), or EBADF (fd is not a valid file descriptor or is not open)

        '''
        signed_offset = self._to_signed_dword(offset)
        try:
            self._get_fd(fd).seek(signed_offset, whence)
        except FdError as e:
            logger.info(("LSEEK: Not valid file descriptor on lseek."
                         "Fd not seekable. Returning EBADF"))
            return -e.err

        return 0

    def sys_read(self, fd, buf, count):
        data = ''
        if count != 0:
            # TODO check count bytes from buf
            if buf not in self.current.memory:  # or not  self.current.memory.isValid(buf+count):
                logger.info("READ: buf points to invalid address. Returning EFAULT")
                return -errno.EFAULT

            try:
                # Read the data and put in tin memory
                data = self._get_fd(fd).read(count)
            except FdError as e:
                logger.info(("READ: Not valid file descriptor on read."
                             " Returning EBADF"))
                return -e.err
            self.syscall_trace.append(("_read", fd, data))
            self.current.write_bytes(buf, data)

        return len(data)

    def sys_write(self, fd, buf, count):
        ''' write - send bytes through a file descriptor
          The write system call writes up to count bytes from the buffer pointed
          to by buf to the file descriptor fd. If count is zero, write returns 0
          and optionally sets *tx_bytes to zero.

          :param fd            a valid file descriptor
          :param buf           a memory buffer
          :param count         number of bytes to send
          :return: 0          Success
                    EBADF      fd is not a valid file descriptor or is not open.
                    EFAULT     buf or tx_bytes points to an invalid address.
        '''
        data = []
        cpu = self.current
        if count != 0:
            try:
                write_fd = self._get_fd(fd)
            except FdError as e:
                logger.error("WRITE: Not valid file descriptor (%d). Returning -%d", fd, e.err)
                return -e.err

            # TODO check count bytes from buf
            if buf not in cpu.memory or buf + count not in cpu.memory:
                logger.debug("WRITE: buf points to invalid address. Returning EFAULT")
                return -errno.EFAULT

            if fd > 2 and write_fd.is_full():
                cpu.PC -= cpu.instruction.size
                self.wait([], [fd], None)
                raise RestartSyscall()

            data = cpu.read_bytes(buf, count)
            data = self._transform_write_data(data)
            write_fd.write(data)

            for line in ''.join([str(x) for x in data]).split('\n'):
                logger.debug("WRITE(%d, 0x%08x, %d) -> <%.48r>",
                             fd,
                             buf,
                             count,
                             line)
            self.syscall_trace.append(("_write", fd, data))
            self.signal_transmit(fd)

        return len(data)

    def sys_fork(self):
        '''
        We don't support forking, but do return a valid error code to client binary.
        '''
        return -errno.ENOSYS

    def sys_access(self, buf, mode):
        '''
        Checks real user's permissions for a file
        :rtype: int

        :param buf: a buffer containing the pathname to the file to check its permissions.
        :param mode: the access permissions to check.
        :return:
            -  C{0} if the calling process can access the file in the desired mode.
            - C{-1} if the calling process can not access the file in the desired mode.
        '''
        filename = ""
        for i in xrange(0, 255):
            c = Operators.CHR(self.current.read_int(buf + i, 8))
            if c == '\x00':
                break
            filename += c

        if os.access(filename, mode):
            return 0
        else:
            return -1

    def sys_newuname(self, old_utsname):
        '''
        Writes system information in the variable C{old_utsname}.
        :rtype: int
        :param old_utsname: the buffer to write the system info.
        :return: C{0} on success
        '''
        from datetime import datetime
{'fraction_non_alphanumeric': 0.1015577923092374, 'fraction_numerical': 0.01611355368580802, 'mean_word_length': 1.655101919886229, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 49, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6932278', 'n_tokens_mistral': 27617, 'n_tokens_neox': 25828, 'n_words': 7412}