Exemplo n.º 1
0
 async def op(sem: RAM) -> t.Tuple[Pointer[Stack], WrittenPointer[Stack]]:
     stack_value = loader.make_trampoline_stack(Trampoline(
         loader.futex_helper_func, [
             int(futex_pointer.near + ffi.offsetof('struct futex_node', 'futex')),
             futex_pointer.value.futex]))
     stack_buf = await sem.malloc(Stack, 4096)
     stack = await stack_buf.write_to_end(stack_value, alignment=16)
     return stack
Exemplo n.º 2
0
 def to_bytes(self) -> bytes:
     struct = ffi.new(
         'struct robust_list_head*', {
             'list':
             (ffi.cast('struct robust_list*', int(self.first.near)), ),
             'futex_offset': ffi.offsetof('struct futex_node', 'futex'),
             'list_op_pending': ffi.NULL,
         })
     return bytes(ffi.buffer(struct))
Exemplo n.º 3
0
 async def clone(
     self,
     flags: CLONE,
     # these two pointers must be adjacent; the end of the first is the start of the
     # second. the first is the allocation for stack growth, the second is the data
     # we've written on the stack that will be popped off for arguments.
     child_stack: t.Tuple[Pointer[Stack], WrittenPointer[Stack]],
     ptid: t.Optional[Pointer],
     ctid: t.Optional[Pointer[FutexNode]],
     # this points to anything, it depends on the thread implementation
     newtls: t.Optional[Pointer]
 ) -> ThreadProcess:
     clone_parent = bool(flags & CLONE.PARENT)
     if clone_parent:
         if self.parent_task is None:
             raise Exception(
                 "using CLONE.PARENT, but we don't know our parent task")
         # TODO also check that the parent_task hasn't shut down... not sure how to do that
         owning_task = self.parent_task
     else:
         owning_task = self
     with contextlib.ExitStack() as stack:
         stack_alloc, stack_data = child_stack
         if (int(stack_data.near) % 16) != 0:
             raise Exception(
                 "child stack must have 16-byte alignment, so says Intel")
         stack_alloc_end = stack_alloc.near + stack_alloc.size()
         if stack_alloc_end != stack_data.near:
             raise Exception("the end of the stack allocation pointer",
                             stack_alloc_end,
                             "and the beginning of the stack data pointer",
                             stack_data.near, "must be the same")
         stack.enter_context(stack_alloc.borrow(self))
         stack.enter_context(stack_data.borrow(self))
         ptid_n = self._borrow_optional(stack, ptid)
         ctid_n = self._borrow_optional(stack, ctid)
         newtls_n = self._borrow_optional(stack, newtls)
         process = await _clone(
             self.sysif, flags, stack_data.near, ptid_n, ctid_n +
             ffi.offsetof('struct futex_node', 'futex') if ctid_n else None,
             newtls_n)
     # TODO the safety of this depends on no-one borrowing/freeing the stack in borrow __aexit__
     # should try to do this a bit more robustly...
     merged_stack = stack_alloc.merge(stack_data)
     return ThreadProcess(owning_task, process, merged_stack,
                          stack_data.value, ctid, newtls)
Exemplo n.º 4
0
async def launch_futex_monitor(
        loader: NativeLoader, monitor: ChildPidMonitor,
        futex_pointer: WrittenPointer[FutexNode]) -> AsyncChildPid:
    """Launch a process to wait on a futex; then we monitor the process to monitor the futex

    This process calls futex(futex_pointer, FUTEX_WAIT, futex_pointer.value) and
    then exits, so this process will exit if and when the futex has FUTEX_WAKE
    called on it.

    Sadly, this is the best we can do with integrating futexes into our event
    loop. There used to be a way to get a file descriptor to represent a futex,
    but it was removed because it was racy.

    Something better would be really great - especially because it would allow
    incorporating pprocesss locks and other shared memory concurrency mechanisms
    based on futexes, into a normal event loop.

    """
    stack_value = loader.make_trampoline_stack(
        Trampoline(loader.futex_helper_func, [
            int(futex_pointer.near +
                ffi.offsetof('struct futex_node', 'futex')),
            futex_pointer.value.futex
        ]))
    stack_buf = await monitor.cloning_task.malloc(Stack, 4096)
    stack = await stack_buf.write_to_end(stack_value, alignment=16)
    futex_pid = await monitor.clone(CLONE.VM | CLONE.FILES, stack)
    # wait for futex helper to SIGSTOP itself,
    # which indicates the trampoline is done and we can deallocate the stack.
    state = await futex_pid.waitpid(W.EXITED | W.STOPPED)
    if state.state(W.EXITED):
        raise Exception(
            "process internal futex-waiting task died unexpectedly", state)
    # resume the futex_process so it can start waiting on the futex
    await futex_pid.kill(SIG.CONT)
    # TODO uh we need to actually call something to free the stack
    return futex_pid
Exemplo n.º 5
0
    "GetdentsFileDescriptor",
]


class DT(enum.IntEnum):
    BLK = lib.DT_BLK  # This is a block device.
    CHR = lib.DT_CHR  # This is a character device.
    DIR = lib.DT_DIR  # This is a directory.
    FIFO = lib.DT_FIFO  # This is a named pipe (FIFO).
    LNK = lib.DT_LNK  # This is a symbolic link.
    REG = lib.DT_REG  # This is a regular file.
    SOCK = lib.DT_SOCK  # This is a UNIX domain socket.
    UNKNOWN = lib.DT_UNKNOWN  # The file type is unknown.


_d_name_offset = ffi.offsetof('struct linux_dirent64', 'd_name')


@dataclass
class Dirent:
    inode: int
    offset: int  # the offset to seek to to see the next dirent
    type: DT
    name: str

    def __str__(self) -> str:
        return f"Dirent({self.type}, {self.name})"

    def to_bytes(self) -> bytes:
        def record(reclen: int) -> bytes:
            record = ffi.new(