/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction-linux.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" #include "libc/calls/struct/tms.h" #include "libc/calls/struct/utsname.h" #include "libc/calls/struct/winsize.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/vendor.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/pc.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/select.h" #include "libc/sock/sock.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/lock.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/pr.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/ss.h" #include "libc/sysv/consts/tcp.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" #include "libc/time/struct/timezone.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "third_party/mbedtls/endian.h" #include "tool/build/lib/bits.h" #include "tool/build/lib/case.h" #include "tool/build/lib/endian.h" #include "tool/build/lib/iovs.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/pml4t.h" #include "tool/build/lib/signal.h" #include "tool/build/lib/syscall.h" #include "tool/build/lib/throw.h" #include "tool/build/lib/xlat.h" #include "tool/build/lib/xlaterrno.h" #define TIOCGWINSZ_LINUX 0x5413 #define TCGETS_LINUX 0x5401 #define TCSETS_LINUX 0x5402 #define TCSETSW_LINUX 0x5403 #define TCSETSF_LINUX 0x5404 #define ARCH_SET_GS_LINUX 0x1001 #define ARCH_SET_FS_LINUX 0x1002 #define ARCH_GET_FS_LINUX 0x1003 #define ARCH_GET_GS_LINUX 0x1004 #define SOCK_CLOEXEC_LINUX 0x080000 #define O_CLOEXEC_LINUX 0x080000 #define SA_RESTORER 0x04000000 #define POLLIN_LINUX 0x01 #define POLLPRI_LINUX 0x02 #define POLLOUT_LINUX 0x04 #define POLLERR_LINUX 0x08 #define POLLHUP_LINUX 0x10 #define POLLNVAL_LINUX 0x20 #define TIOCGWINSZ_LINUX 0x5413 #define TCGETS_LINUX 0x5401 #define TCSETS_LINUX 0x5402 #define TCSETSW_LINUX 0x5403 #define TCSETSF_LINUX 0x5404 #define ISIG_LINUX 0b0000000000000001 #define ICANON_LINUX 0b0000000000000010 #define ECHO_LINUX 0b0000000000001000 #define OPOST_LINUX 0b0000000000000001 #define POINTER(x) ((void *)(intptr_t)(x)) #define UNPOINTER(x) ((int64_t)(intptr_t)(x)) #define SYSCALL(x, y) CASE(x, asm("# " #y); ax = y) #define XLAT(x, y) CASE(x, return y) #define PNN(x) ResolveAddress(m, x) #define P(x) ((x) ? PNN(x) : 0) #define ASSIGN(D, S) memcpy(&D, &S, MIN(sizeof(S), sizeof(D))) const struct MachineFdCb kMachineFdCbHost = { .close = close, .readv = readv, .poll = poll, .writev = writev, .ioctl = (void *)ioctl, }; static int GetFd(struct Machine *m, int fd) { if (!(0 <= fd && fd < m->fds.i)) return ebadf(); if (!m->fds.p[fd].cb) return ebadf(); return m->fds.p[fd].fd; } static int GetAfd(struct Machine *m, int fd) { if (fd == -100) return AT_FDCWD; return GetFd(m, fd); } static const char *GetSimulated(void) { if (IsGenuineCosmo()) { return " SIMULATED"; } else { return ""; } } static int AppendIovsReal(struct Machine *m, struct Iovs *ib, int64_t addr, size_t size) { void *real; size_t have; unsigned got; while (size) { if (!(real = FindReal(m, addr))) return efault(); have = 0x1000 - (addr & 0xfff); got = MIN(size, have); if (AppendIovs(ib, real, got) == -1) return -1; addr += got; size -= got; } return 0; } static int AppendIovsGuest(struct Machine *m, struct Iovs *iv, int64_t iovaddr, long iovlen) { int rc; size_t i, iovsize; struct iovec *guestiovs; if (!__builtin_mul_overflow(iovlen, sizeof(struct iovec), &iovsize) && (0 <= iovsize && iovsize <= 0x7ffff000)) { if ((guestiovs = malloc(iovsize))) { VirtualSendRead(m, guestiovs, iovaddr, iovsize); for (rc = i = 0; i < iovlen; ++i) { if (AppendIovsReal(m, iv, (intptr_t)guestiovs[i].iov_base, guestiovs[i].iov_len) == -1) { rc = -1; break; } } free(guestiovs); } else { rc = enomem(); } } else { rc = eoverflow(); } return rc; } static int OpPrctl(struct Machine *m, int op, int64_t a, int64_t b, int64_t c, int64_t d) { return einval(); } static int OpArchPrctl(struct Machine *m, int code, int64_t addr) { switch (code) { case ARCH_SET_GS: Write64(m->gs, addr); return 0; case ARCH_SET_FS: Write64(m->fs, addr); return 0; case ARCH_GET_GS: VirtualRecvWrite(m, addr, m->gs, 8); return 0; case ARCH_GET_FS: VirtualRecvWrite(m, addr, m->fs, 8); return 0; default: return einval(); } } static int OpMprotect(struct Machine *m, int64_t addr, uint64_t len, int prot) { return 0; } static int OpMadvise(struct Machine *m, int64_t addr, size_t length, int advice) { return enosys(); } static int64_t OpBrk(struct Machine *m, int64_t addr) { addr = ROUNDUP(addr, PAGESIZE); if (addr > m->brk) { if (ReserveVirtual(m, m->brk, addr - m->brk, PAGE_V | PAGE_RW | PAGE_U | PAGE_RSRV) != -1) { m->brk = addr; } } else if (addr < m->brk) { if (FreeVirtual(m, addr, m->brk - addr) != -1) { m->brk = addr; } } return m->brk; } static int OpMunmap(struct Machine *m, int64_t virt, uint64_t size) { VERBOSEF("MUNMAP%s %012lx %,ld", GetSimulated(), virt, size); return FreeVirtual(m, virt, size); } static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, int flags, int fd, int64_t offset) { void *tmp; uint64_t key; VERBOSEF("MMAP%s %012lx %,ld %#x %#x %d %#lx", GetSimulated(), virt, size, prot, flags, fd, offset); if (prot & PROT_READ) { key = PAGE_RSRV | PAGE_U | PAGE_V; if (prot & PROT_WRITE) key |= PAGE_RW; if (!(prot & PROT_EXEC)) key |= PAGE_XD; if (flags & 256 /* MAP_GROWSDOWN */) key |= PAGE_GROD; flags = XlatMapFlags(flags); if (fd != -1 && (fd = GetFd(m, fd)) == -1) return -1; if (!(flags & MAP_FIXED)) { if (!virt) { if ((virt = FindVirtual(m, m->brk, size)) == -1) return -1; m->brk = virt + size; } else { if ((virt = FindVirtual(m, virt, size)) == -1) return -1; } } if (ReserveVirtual(m, virt, size, key) != -1) { if (fd != -1 && !(flags & MAP_ANONYMOUS)) { /* TODO: lazy file mappings */ CHECK_NOTNULL((tmp = malloc(size))); CHECK_EQ(size, pread(fd, tmp, size, offset)); VirtualRecvWrite(m, virt, tmp, size); free(tmp); } } else { FreeVirtual(m, virt, size); return -1; } return virt; } else { return FreeVirtual(m, virt, size); } } static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) { return enosys(); #if 0 size_t i; void *page; virt = ROUNDDOWN(virt, 4096); flags = XlatMsyncFlags(flags); for (i = 0; i < size; i += 4096) { if (!(page = FindReal(m, virt + i))) return efault(); if (msync(page, 4096, flags) == -1) return -1; } return 0; #endif } static int OpClose(struct Machine *m, int fd) { int rc; struct FdClosed *closed; if (!(0 <= fd && fd < m->fds.i)) return ebadf(); if (!m->fds.p[fd].cb) return ebadf(); rc = m->fds.p[fd].cb->close(m->fds.p[fd].fd); MachineFdRemove(&m->fds, fd); return rc; } static int OpOpenat(struct Machine *m, int dirfd, int64_t pathaddr, int flags, int mode) { int fd, i; const char *path; flags = XlatOpenFlags(flags); if ((dirfd = GetAfd(m, dirfd)) == -1) return -1; if ((i = MachineFdAdd(&m->fds)) == -1) return -1; path = LoadStr(m, pathaddr); if ((fd = openat(dirfd, path, flags, mode)) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = fd; VERBOSEF("openat(%#x, %`'s, %#x, %#x) → %d [%d]", dirfd, path, flags, mode, i, fd); fd = i; } else { MachineFdRemove(&m->fds, i); VERBOSEF("openat(%#x, %`'s, %#x, %#x) failed", dirfd, path, flags, mode); } return fd; } static int OpPipe(struct Machine *m, int64_t pipefds_addr) { int i, j, pipefds[2]; if ((i = MachineFdAdd(&m->fds)) != -1) { if ((j = MachineFdAdd(&m->fds)) != -1) { if (pipe(pipefds) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = pipefds[0]; m->fds.p[j].cb = &kMachineFdCbHost; m->fds.p[j].fd = pipefds[1]; pipefds[0] = i; pipefds[1] = j; VirtualRecvWrite(m, pipefds_addr, pipefds, sizeof(pipefds)); return 0; } MachineFdRemove(&m->fds, j); } MachineFdRemove(&m->fds, i); } return -1; } static int OpDup(struct Machine *m, int fd) { int i; if ((fd = GetFd(m, fd)) != -1) { if ((i = MachineFdAdd(&m->fds)) != -1) { if ((fd = dup(fd)) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = fd; return i; } MachineFdRemove(&m->fds, i); } } return -1; } static int OpDup2(struct Machine *m, int fd, int newfd) { int i, rc; if ((fd = GetFd(m, fd)) == -1) return -1; if ((0 <= newfd && newfd < m->fds.i)) { if ((rc = dup2(fd, m->fds.p[newfd].fd)) != -1) { m->fds.p[newfd].cb = &kMachineFdCbHost; m->fds.p[newfd].fd = rc; rc = newfd; } } else if ((i = MachineFdAdd(&m->fds)) != -1) { if ((rc = dup(fd)) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = rc; rc = i; } } else { rc = -1; } return rc; } static int OpDup3(struct Machine *m, int fd, int newfd, int flags) { int i, rc; if (!(flags & ~O_CLOEXEC_LINUX)) { if ((fd = GetFd(m, fd)) == -1) return -1; if ((0 <= newfd && newfd < m->fds.i)) { if ((rc = dup2(fd, m->fds.p[newfd].fd)) != -1) { if (flags & O_CLOEXEC_LINUX) { fcntl(rc, F_SETFD, FD_CLOEXEC); } m->fds.p[newfd].cb = &kMachineFdCbHost; m->fds.p[newfd].fd = rc; rc = newfd; } } else if ((i = MachineFdAdd(&m->fds)) != -1) { if ((rc = dup(fd)) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = rc; rc = i; } } else { rc = -1; } return rc; } else { return einval(); } } static int OpSocket(struct Machine *m, int family, int type, int protocol) { int i, fd; if ((family = XlatSocketFamily(family)) == -1) return -1; if ((type = XlatSocketType(type)) == -1) return -1; if ((protocol = XlatSocketProtocol(protocol)) == -1) return -1; if ((i = MachineFdAdd(&m->fds)) == -1) return -1; if ((fd = socket(family, type, protocol)) != -1) { m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = fd; fd = i; } else { MachineFdRemove(&m->fds, i); } return fd; } static int OpAccept4(struct Machine *m, int fd, int64_t aa, int64_t asa, int flags) { int i, rc; uint32_t addrsize; uint8_t gaddrsize[4]; struct sockaddr_in addr; struct sockaddr_in_bits gaddr; if (!(flags & ~SOCK_CLOEXEC_LINUX)) { if ((fd = GetFd(m, fd)) == -1) return -1; if (aa) { VirtualSendRead(m, gaddrsize, asa, sizeof(gaddrsize)); if (Read32(gaddrsize) < sizeof(gaddr)) return einval(); } if ((i = rc = MachineFdAdd(&m->fds)) != -1) { addrsize = sizeof(addr); if ((rc = accept(fd, (struct sockaddr *)&addr, &addrsize)) != -1) { if (flags & SOCK_CLOEXEC_LINUX) { fcntl(fd, F_SETFD, FD_CLOEXEC); } if (aa) { Write32(gaddrsize, sizeof(gaddr)); XlatSockaddrToLinux(&gaddr, &addr); VirtualRecv(m, asa, gaddrsize, sizeof(gaddrsize)); VirtualRecvWrite(m, aa, &gaddr, sizeof(gaddr)); } m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = rc; rc = i; } else { MachineFdRemove(&m->fds, i); } } return rc; } else { return einval(); } } static int OpConnectBind(struct Machine *m, int fd, int64_t aa, uint32_t as, int impl(int, const void *, uint32_t)) { struct sockaddr_in addr; struct sockaddr_in_bits gaddr; if (as != sizeof(gaddr)) return einval(); if ((fd = GetFd(m, fd)) == -1) return -1; VirtualSendRead(m, &gaddr, aa, sizeof(gaddr)); XlatSockaddrToHost(&addr, &gaddr); return impl(fd, (const struct sockaddr *)&addr, sizeof(addr)); } static int OpBind(struct Machine *m, int fd, int64_t aa, uint32_t as) { return OpConnectBind(m, fd, aa, as, bind); } static int OpConnect(struct Machine *m, int fd, int64_t aa, uint32_t as) { return OpConnectBind(m, fd, aa, as, connect); } static int OpSetsockopt(struct Machine *m, int fd, int level, int optname, int64_t optvaladdr, uint32_t optvalsize) { int rc; void *optval; if ((level = XlatSocketLevel(level)) == -1) return -1; if ((optname = XlatSocketOptname(level, optname)) == -1) return -1; if ((fd = GetFd(m, fd)) == -1) return -1; if (!(optval = malloc(optvalsize))) return -1; VirtualSendRead(m, optval, optvaladdr, optvalsize); rc = setsockopt(fd, level, optname, optval, optvalsize); free(optval); return rc; } static int OpGetsockopt(struct Machine *m, int fd, int level, int optname, int64_t optvaladdr, int64_t optsizeaddr) { int rc; void *optval; uint32_t optsize; if ((level = XlatSocketLevel(level)) == -1) return -1; if ((optname = XlatSocketOptname(level, optname)) == -1) return -1; if ((fd = GetFd(m, fd)) == -1) return -1; if (!optvaladdr) { rc = getsockopt(fd, level, optname, 0, 0); } else { VirtualSendRead(m, &optsize, optsizeaddr, sizeof(optsize)); if (!(optval = malloc(optsize))) return -1; if ((rc = getsockopt(fd, level, optname, optval, &optsize)) != -1) { VirtualRecvWrite(m, optvaladdr, optval, optsize); VirtualRecvWrite(m, optsizeaddr, &optsize, sizeof(optsize)); } free(optval); } return rc; } static int OpSocketName(struct Machine *m, int fd, int64_t aa, int64_t asa, int SocketName(int, void *, socklen_t *)) { int rc; uint32_t addrsize; uint8_t gaddrsize[4]; struct sockaddr_in addr; struct sockaddr_in_bits gaddr; if ((fd = GetFd(m, fd)) == -1) return -1; VirtualSendRead(m, gaddrsize, asa, sizeof(gaddrsize)); if (Read32(gaddrsize) < sizeof(gaddr)) return einval(); addrsize = sizeof(addr); if ((rc = SocketName(fd, (struct sockaddr *)&addr, &addrsize)) != -1) { Write32(gaddrsize, sizeof(gaddr)); XlatSockaddrToLinux(&gaddr, &addr); VirtualRecv(m, asa, gaddrsize, sizeof(gaddrsize)); VirtualRecvWrite(m, aa, &gaddr, sizeof(gaddr)); } return rc; } static int OpGetsockname(struct Machine *m, int fd, int64_t aa, int64_t asa) { return OpSocketName(m, fd, aa, asa, getsockname); } static int OpGetpeername(struct Machine *m, int fd, int64_t aa, int64_t asa) { return OpSocketName(m, fd, aa, asa, getpeername); } static ssize_t OpRead(struct Machine *m, int fd, int64_t addr, size_t size) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { if ((rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { SetWriteAddr(m, addr, rc); } } } else { rc = ebadf(); } FreeIovs(&iv); return rc; } static int OpGetdents(struct Machine *m, int dirfd, int64_t addr, uint32_t size) { int rc; DIR *dir; struct dirent *ent; if (size < sizeof(struct dirent)) return einval(); if (0 <= dirfd && dirfd < m->fds.i) { if ((dir = fdopendir(m->fds.p[dirfd].fd))) { rc = 0; while (rc + sizeof(struct dirent) <= size) { if (!(ent = readdir(dir))) break; VirtualRecvWrite(m, addr + rc, ent, ent->d_reclen); rc += ent->d_reclen; } free(dir); } else { rc = -1; } } else { rc = ebadf(); } return rc; } static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size, int64_t offset) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((rc = GetFd(m, fd)) != -1) { fd = rc; if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { if ((rc = preadv(fd, iv.p, iv.i, offset)) != -1) { SetWriteAddr(m, addr, rc); } } } FreeIovs(&iv); return rc; } static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { if ((rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { SetReadAddr(m, addr, rc); } else { VERBOSEF("write(%d [%d], %012lx, %zu) failed: %s", fd, m->fds.p[fd].fd, addr, size, strerror(errno)); } } } else { VERBOSEF("write(%d, %012lx, %zu) bad fd", fd, addr, size); rc = ebadf(); } FreeIovs(&iv); return rc; } static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size, int64_t offset) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((rc = GetFd(m, fd)) != -1) { fd = rc; if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { if ((rc = pwritev(fd, iv.p, iv.i, offset)) != -1) { SetReadAddr(m, addr, rc); } } } FreeIovs(&iv); return rc; } static int IoctlTiocgwinsz(struct Machine *m, int fd, int64_t addr, int fn(int, int, ...)) { int rc; struct winsize ws; struct winsize_bits gws; if ((rc = fn(fd, TIOCGWINSZ, &ws)) != -1) { XlatWinsizeToLinux(&gws, &ws); VirtualRecvWrite(m, addr, &gws, sizeof(gws)); } return rc; } static int IoctlTcgets(struct Machine *m, int fd, int64_t addr, int fn(int, int, ...)) { int rc; struct termios tio; struct termios_bits gtio; if ((rc = fn(fd, TCGETS, &tio)) != -1) { XlatTermiosToLinux(>io, &tio); VirtualRecvWrite(m, addr, >io, sizeof(gtio)); } return rc; } static int IoctlTcsets(struct Machine *m, int fd, int64_t request, int64_t addr, int (*fn)(int, int, ...)) { struct termios tio; struct termios_bits gtio; VirtualSendRead(m, >io, addr, sizeof(gtio)); XlatLinuxToTermios(&tio, >io); return fn(fd, request, &tio); } static int OpIoctl(struct Machine *m, int fd, uint64_t request, int64_t addr) { int (*fn)(int, int, ...); if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); fn = m->fds.p[fd].cb->ioctl; fd = m->fds.p[fd].fd; switch (request) { case TIOCGWINSZ_LINUX: return IoctlTiocgwinsz(m, fd, addr, fn); case TCGETS_LINUX: return IoctlTcgets(m, fd, addr, fn); case TCSETS_LINUX: return IoctlTcsets(m, fd, TCSETS, addr, fn); case TCSETSW_LINUX: return IoctlTcsets(m, fd, TCSETSW, addr, fn); case TCSETSF_LINUX: return IoctlTcsets(m, fd, TCSETSF, addr, fn); default: return einval(); } } static ssize_t OpReadv(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i); } } else { rc = ebadf(); } FreeIovs(&iv); return rc; } static ssize_t OpWritev(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; struct Iovs iv; InitIovs(&iv); if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i); } } else { rc = ebadf(); } FreeIovs(&iv); return rc; } static int64_t OpLseek(struct Machine *m, int fd, int64_t offset, int whence) { if ((fd = GetFd(m, fd)) == -1) return -1; return lseek(fd, offset, whence); } static ssize_t OpFtruncate(struct Machine *m, int fd, int64_t size) { if ((fd = GetFd(m, fd)) == -1) return -1; return ftruncate(fd, size); } static int OpFaccessat(struct Machine *m, int dirfd, int64_t path, int mode, int flags) { flags = XlatAtf(flags); mode = XlatAccess(mode); if ((dirfd = GetAfd(m, dirfd)) == -1) return -1; return faccessat(dirfd, LoadStr(m, path), mode, flags); } static int OpFstatat(struct Machine *m, int dirfd, int64_t path, int64_t staddr, int flags) { int rc; struct stat st; struct stat_bits gst; flags = XlatAtf(flags); if ((dirfd = GetAfd(m, dirfd)) == -1) return -1; if ((rc = fstatat(dirfd, LoadStr(m, path), &st, flags)) != -1) { XlatStatToLinux(&gst, &st); VirtualRecvWrite(m, staddr, &gst, sizeof(gst)); } return rc; } static int OpFstat(struct Machine *m, int fd, int64_t staddr) { int rc; struct stat st; struct stat_bits gst; if ((fd = GetFd(m, fd)) == -1) return -1; if ((rc = fstat(fd, &st)) != -1) { XlatStatToLinux(&gst, &st); VirtualRecvWrite(m, staddr, &gst, sizeof(gst)); } return rc; } static int OpListen(struct Machine *m, int fd, int backlog) { if ((fd = GetFd(m, fd)) == -1) return -1; return listen(fd, backlog); } static int OpShutdown(struct Machine *m, int fd, int how) { if ((fd = GetFd(m, fd)) == -1) return -1; return shutdown(fd, how); } static int OpFsync(struct Machine *m, int fd) { if ((fd = GetFd(m, fd)) == -1) return -1; return fsync(fd); } static int OpFdatasync(struct Machine *m, int fd) { if ((fd = GetFd(m, fd)) == -1) return -1; return fdatasync(fd); } static int OpFchmod(struct Machine *m, int fd, uint32_t mode) { if ((fd = GetFd(m, fd)) == -1) return -1; return fchmod(fd, mode); } static int OpFcntl(struct Machine *m, int fd, int cmd, int arg) { if ((cmd = XlatFcntlCmd(cmd)) == -1) return -1; if ((arg = XlatFcntlArg(arg)) == -1) return -1; if ((fd = GetFd(m, fd)) == -1) return -1; return fcntl(fd, cmd, arg); } static int OpFadvise(struct Machine *m, int fd, uint64_t offset, uint64_t len, int advice) { if ((fd = GetFd(m, fd)) == -1) return -1; if ((advice = XlatAdvice(advice)) == -1) return -1; return fadvise(fd, offset, len, advice); } static int OpFlock(struct Machine *m, int fd, int lock) { if ((fd = GetFd(m, fd)) == -1) return -1; if ((lock = XlatLock(lock)) == -1) return -1; return flock(fd, lock); } static int OpChdir(struct Machine *m, int64_t path) { return chdir(LoadStr(m, path)); } static int OpMkdir(struct Machine *m, int64_t path, int mode) { return mkdir(LoadStr(m, path), mode); } static int OpMkdirat(struct Machine *m, int dirfd, int64_t path, int mode) { return mkdirat(GetAfd(m, dirfd), LoadStr(m, path), mode); } static int OpMknod(struct Machine *m, int64_t path, uint32_t mode, uint64_t dev) { return mknod(LoadStr(m, path), mode, dev); } static int OpRmdir(struct Machine *m, int64_t path) { return rmdir(LoadStr(m, path)); } static int OpUnlink(struct Machine *m, int64_t path) { return unlink(LoadStr(m, path)); } static int OpUnlinkat(struct Machine *m, int dirfd, int64_t path, int flags) { return unlinkat(GetAfd(m, dirfd), LoadStr(m, path), XlatAtf(flags)); } static int OpRename(struct Machine *m, int64_t src, int64_t dst) { return rename(LoadStr(m, src), LoadStr(m, dst)); } static int OpRenameat(struct Machine *m, int srcdirfd, int64_t src, int dstdirfd, int64_t dst) { return renameat(GetAfd(m, srcdirfd), LoadStr(m, src), GetAfd(m, dstdirfd), LoadStr(m, dst)); } static int OpTruncate(struct Machine *m, int64_t path, uint64_t length) { return truncate(LoadStr(m, path), length); } static int OpLink(struct Machine *m, int64_t existingpath, int64_t newpath) { return link(LoadStr(m, existingpath), LoadStr(m, newpath)); } static int OpSymlink(struct Machine *m, int64_t target, int64_t linkpath) { return symlink(LoadStr(m, target), LoadStr(m, linkpath)); } static int OpChmod(struct Machine *m, int64_t path, uint32_t mode) { return chmod(LoadStr(m, path), mode); } static int OpFork(struct Machine *m) { int pid; pid = fork(); if (!pid) m->isfork = true; return pid; } static int OpExecve(struct Machine *m, int64_t programaddr, int64_t argvaddr, int64_t envpaddr) { return enosys(); } wontreturn static void OpExit(struct Machine *m, int rc) { if (m->isfork) { _Exit(rc); } else { HaltMachine(m, rc | 0x100); } } _Noreturn static void OpExitGroup(struct Machine *m, int rc) { OpExit(m, rc); } static int OpWait4(struct Machine *m, int pid, int64_t opt_out_wstatus_addr, int options, int64_t opt_out_rusage_addr) { int rc; int32_t wstatus; struct rusage hrusage; struct rusage_bits grusage; if ((options = XlatWait(options)) == -1) return -1; if ((rc = wait4(pid, &wstatus, options, &hrusage)) != -1) { if (opt_out_wstatus_addr) { VirtualRecvWrite(m, opt_out_wstatus_addr, &wstatus, sizeof(wstatus)); } if (opt_out_rusage_addr) { XlatRusageToLinux(&grusage, &hrusage); VirtualRecvWrite(m, opt_out_rusage_addr, &grusage, sizeof(grusage)); } } return rc; } static int OpGetrusage(struct Machine *m, int resource, int64_t rusageaddr) { int rc; struct rusage hrusage; struct rusage_bits grusage; if ((resource = XlatRusage(resource)) == -1) return -1; if ((rc = getrusage(resource, &hrusage)) != -1) { XlatRusageToLinux(&grusage, &hrusage); VirtualRecvWrite(m, rusageaddr, &grusage, sizeof(grusage)); } return rc; } static int OpGetrlimit(struct Machine *m, int resource, int64_t rlimitaddr) { return enosys(); } static ssize_t OpReadlinkat(struct Machine *m, int dirfd, int64_t pathaddr, int64_t bufaddr, size_t size) { char *buf; ssize_t rc; const char *path; if ((dirfd = GetAfd(m, dirfd)) == -1) return -1; path = LoadStr(m, pathaddr); if (!(buf = malloc(size))) return enomem(); if ((rc = readlinkat(dirfd, path, buf, size)) != -1) { VirtualRecvWrite(m, bufaddr, buf, rc); } free(buf); return rc; } static int64_t OpGetcwd(struct Machine *m, int64_t bufaddr, size_t size) { size_t n; char *buf; int64_t res; size = MIN(size, PATH_MAX); if (!(buf = malloc(size))) return enomem(); if ((getcwd)(buf, size)) { n = strlen(buf); VirtualRecvWrite(m, bufaddr, buf, n); res = bufaddr; } else { res = -1; } free(buf); return res; } static int OpSigaction(struct Machine *m, int sig, int64_t act, int64_t old, uint64_t sigsetsize) { if ((sig = XlatSignal(sig) - 1) != -1 && (0 <= sig && sig < ARRAYLEN(m->sighand)) && sigsetsize == 8) { if (old) VirtualRecvWrite(m, old, &m->sighand[sig], sizeof(m->sighand[0])); if (act) VirtualSendRead(m, &m->sighand[sig], act, sizeof(m->sighand[0])); return 0; } else { return einval(); } } static int OpSigprocmask(struct Machine *m, int how, int64_t setaddr, int64_t oldsetaddr, uint64_t sigsetsize) { uint8_t set[8]; if ((how = XlatSig(how)) != -1 && sigsetsize == sizeof(set)) { if (oldsetaddr) { VirtualRecvWrite(m, oldsetaddr, m->sigmask, sizeof(set)); } if (setaddr) { VirtualSendRead(m, set, setaddr, sizeof(set)); if (how == SIG_BLOCK) { Write64(m->sigmask, Read64(m->sigmask) | Read64(set)); } else if (how == SIG_UNBLOCK) { Write64(m->sigmask, Read64(m->sigmask) & ~Read64(set)); } else { Write64(m->sigmask, Read64(set)); } } return 0; } else { return einval(); } } static int OpGetitimer(struct Machine *m, int which, int64_t curvaladdr) { int rc; struct itimerval it; struct itimerval_bits git; if ((rc = getitimer(which, &it)) != -1) { XlatItimervalToLinux(&git, &it); VirtualRecvWrite(m, curvaladdr, &git, sizeof(git)); } return rc; } static int OpSetitimer(struct Machine *m, int which, int64_t neuaddr, int64_t oldaddr) { int rc; struct itimerval neu, old; struct itimerval_bits git; VirtualSendRead(m, &git, neuaddr, sizeof(git)); XlatLinuxToItimerval(&neu, &git); if ((rc = setitimer(which, &neu, &old)) != -1) { if (oldaddr) { XlatItimervalToLinux(&git, &old); VirtualRecvWrite(m, oldaddr, &git, sizeof(git)); } } return rc; } static int OpNanosleep(struct Machine *m, int64_t req, int64_t rem) { int rc; struct timespec hreq, hrem; struct timespec_bits gtimespec; if (req) { VirtualSendRead(m, >imespec, req, sizeof(gtimespec)); hreq.tv_sec = Read64(gtimespec.tv_sec); hreq.tv_nsec = Read64(gtimespec.tv_nsec); } if ((rc = nanosleep(req ? &hreq : 0, rem ? &hrem : 0)) != -1) { if (rem) { Write64(gtimespec.tv_sec, hrem.tv_sec); Write64(gtimespec.tv_nsec, hrem.tv_nsec); VirtualRecvWrite(m, rem, >imespec, sizeof(gtimespec)); } } return rc; } static int OpSigsuspend(struct Machine *m, int64_t maskaddr) { sigset_t mask; uint8_t gmask[8]; VirtualSendRead(m, &gmask, maskaddr, 8); XlatLinuxToSigset(&mask, gmask); return sigsuspend(&mask); } static int OpClockGettime(struct Machine *m, int clockid, int64_t ts) { int rc; struct timespec htimespec; struct timespec_bits gtimespec; if ((rc = clock_gettime(XlatClock(clockid), ts ? &htimespec : 0)) != -1) { if (ts) { Write64(gtimespec.tv_sec, htimespec.tv_sec); Write64(gtimespec.tv_nsec, htimespec.tv_nsec); VirtualRecvWrite(m, ts, >imespec, sizeof(gtimespec)); } } return rc; } static int OpGettimeofday(struct Machine *m, int64_t tv, int64_t tz) { int rc; struct timeval htimeval; struct timezone htimezone; struct timeval_bits gtimeval; struct timezone_bits gtimezone; if ((rc = gettimeofday(&htimeval, tz ? &htimezone : 0)) != -1) { Write64(gtimeval.tv_sec, htimeval.tv_sec); Write64(gtimeval.tv_usec, htimeval.tv_usec); VirtualRecvWrite(m, tv, >imeval, sizeof(gtimeval)); if (tz) { Write32(gtimezone.tz_minuteswest, htimezone.tz_minuteswest); Write32(gtimezone.tz_dsttime, htimezone.tz_dsttime); VirtualRecvWrite(m, tz, >imezone, sizeof(gtimezone)); } } return rc; } static int OpUtimes(struct Machine *m, int64_t pathaddr, int64_t tvsaddr) { const char *path; struct timeval tvs[2]; struct timeval_bits gtvs[2]; path = LoadStr(m, pathaddr); if (tvsaddr) { VirtualSendRead(m, gtvs, tvsaddr, sizeof(gtvs)); tvs[0].tv_sec = Read64(gtvs[0].tv_sec); tvs[0].tv_usec = Read64(gtvs[0].tv_usec); tvs[1].tv_sec = Read64(gtvs[1].tv_sec); tvs[1].tv_usec = Read64(gtvs[1].tv_usec); return utimes(path, tvs); } else { return utimes(path, 0); } } static int OpPoll(struct Machine *m, int64_t fdsaddr, uint64_t nfds, int32_t timeout_ms) { uint64_t gfdssize; struct pollfd hfds[1]; int i, fd, rc, ev, count; struct timespec ts1, ts2; struct pollfd_bits *gfds; int64_t wait, elapsed, timeout; timeout = timeout_ms * 1000L; if (!__builtin_mul_overflow(nfds, sizeof(struct pollfd_bits), &gfdssize) && gfdssize <= 0x7ffff000) { if ((gfds = malloc(gfdssize))) { rc = 0; VirtualSendRead(m, gfds, fdsaddr, gfdssize); ts1 = _timespec_mono(); for (;;) { for (i = 0; i < nfds; ++i) { fd = Read32(gfds[i].fd); if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { hfds[0].fd = m->fds.p[fd].fd; ev = Read16(gfds[i].events); hfds[0].events = (((ev & POLLIN_LINUX) ? POLLIN : 0) | ((ev & POLLOUT_LINUX) ? POLLOUT : 0) | ((ev & POLLPRI_LINUX) ? POLLPRI : 0)); switch (m->fds.p[fd].cb->poll(hfds, 1, 0)) { case 0: Write16(gfds[i].revents, 0); break; case 1: ++rc; ev = 0; if (hfds[0].revents & POLLIN) ev |= POLLIN_LINUX; if (hfds[0].revents & POLLPRI) ev |= POLLPRI_LINUX; if (hfds[0].revents & POLLOUT) ev |= POLLOUT_LINUX; if (hfds[0].revents & POLLERR) ev |= POLLERR_LINUX; if (hfds[0].revents & POLLHUP) ev |= POLLHUP_LINUX; if (hfds[0].revents & POLLNVAL) ev |= POLLERR_LINUX; if (!ev) ev |= POLLERR_LINUX; Write16(gfds[i].revents, ev); break; case -1: ++rc; Write16(gfds[i].revents, POLLERR_LINUX); break; default: break; } } else { Write16(gfds[i].revents, POLLNVAL_LINUX); } } if (rc || !timeout) break; wait = __SIG_POLLING_INTERVAL_MS * 1000; if (timeout < 0) { if (usleep(wait)) { errno = EINTR; rc = -1; goto Finished; } } else { ts2 = _timespec_mono(); elapsed = _timespec_tomicros(_timespec_sub(ts2, ts1)); if (elapsed >= timeout) { break; } if (timeout - elapsed < wait) { wait = timeout - elapsed; } if (usleep(wait)) { errno = EINTR; rc = -1; goto Finished; } } } VirtualRecvWrite(m, fdsaddr, gfds, nfds * sizeof(*gfds)); } else { errno = ENOMEM; rc = -1; } Finished: free(gfds); return rc; } else { return einval(); } } static int OpGetPid(struct Machine *m) { return getpid(); } static int OpGetPpid(struct Machine *m) { return getppid(); } static void OpKill(struct Machine *m, int pid, int sig) { if (!pid || pid == getpid()) { DeliverSignal(m, sig, SI_USER); } else { Write64(m->ax, -3); // ESRCH } } static int OpGetUid(struct Machine *m) { return getuid(); } static int OpGetGid(struct Machine *m) { return getgid(); } static int OpGetTid(struct Machine *m) { return gettid(); } static int OpSchedYield(struct Machine *m) { return sched_yield(); } static int OpAlarm(struct Machine *m, unsigned seconds) { return alarm(seconds); } static int OpPause(struct Machine *m) { return pause(); } static int DoOpen(struct Machine *m, int64_t path, int flags, int mode) { return OpOpenat(m, -100, path, flags, mode); } static int DoCreat(struct Machine *m, int64_t file, int mode) { return DoOpen(m, file, 0x241, mode); } static int DoAccess(struct Machine *m, int64_t path, int mode) { return OpFaccessat(m, -100, path, mode, 0); } static int DoStat(struct Machine *m, int64_t path, int64_t st) { return OpFstatat(m, -100, path, st, 0); } static int DoLstat(struct Machine *m, int64_t path, int64_t st) { return OpFstatat(m, -100, path, st, 0x0400); } static int OpAccept(struct Machine *m, int fd, int64_t sa, int64_t sas) { return OpAccept4(m, fd, sa, sas, 0); } void OpSyscall(struct Machine *m, uint32_t rde) { uint64_t i, ax, di, si, dx, r0, r8, r9; if (m->redraw) { m->redraw(); } ax = Read64(m->ax); if (m->ismetal) { WARNF("metal syscall 0x%03x", ax); } di = Read64(m->di); si = Read64(m->si); dx = Read64(m->dx); r0 = Read64(m->r10); r8 = Read64(m->r8); r9 = Read64(m->r9); switch (ax & 0x1ff) { SYSCALL(0x000, OpRead(m, di, si, dx)); SYSCALL(0x001, OpWrite(m, di, si, dx)); SYSCALL(0x002, DoOpen(m, di, si, dx)); SYSCALL(0x003, OpClose(m, di)); SYSCALL(0x004, DoStat(m, di, si)); SYSCALL(0x005, OpFstat(m, di, si)); SYSCALL(0x006, DoLstat(m, di, si)); SYSCALL(0x007, OpPoll(m, di, si, dx)); SYSCALL(0x008, OpLseek(m, di, si, dx)); SYSCALL(0x009, OpMmap(m, di, si, dx, r0, r8, r9)); SYSCALL(0x01A, OpMsync(m, di, si, dx)); SYSCALL(0x00A, OpMprotect(m, di, si, dx)); SYSCALL(0x00B, OpMunmap(m, di, si)); SYSCALL(0x00C, OpBrk(m, di)); SYSCALL(0x00D, OpSigaction(m, di, si, dx, r0)); SYSCALL(0x00E, OpSigprocmask(m, di, si, dx, r0)); SYSCALL(0x010, OpIoctl(m, di, si, dx)); SYSCALL(0x011, OpPread(m, di, si, dx, r0)); SYSCALL(0x012, OpPwrite(m, di, si, dx, r0)); SYSCALL(0x013, OpReadv(m, di, si, dx)); SYSCALL(0x014, OpWritev(m, di, si, dx)); SYSCALL(0x015, DoAccess(m, di, si)); SYSCALL(0x016, OpPipe(m, di)); SYSCALL(0x017, select(di, P(si), P(dx), P(r0), P(r8))); SYSCALL(0x018, OpSchedYield(m)); SYSCALL(0x01C, OpMadvise(m, di, si, dx)); SYSCALL(0x020, OpDup(m, di)); SYSCALL(0x021, OpDup2(m, di, si)); SYSCALL(0x124, OpDup3(m, di, si, dx)); SYSCALL(0x022, OpPause(m)); SYSCALL(0x023, OpNanosleep(m, di, si)); SYSCALL(0x024, OpGetitimer(m, di, si)); SYSCALL(0x025, OpAlarm(m, di)); SYSCALL(0x026, OpSetitimer(m, di, si, dx)); SYSCALL(0x027, OpGetPid(m)); SYSCALL(0x028, sendfile(di, si, P(dx), r0)); SYSCALL(0x029, OpSocket(m, di, si, dx)); SYSCALL(0x02A, OpConnect(m, di, si, dx)); SYSCALL(0x02B, OpAccept(m, di, di, dx)); SYSCALL(0x02C, sendto(di, PNN(si), dx, r0, P(r8), r9)); SYSCALL(0x02D, recvfrom(di, P(si), dx, r0, P(r8), P(r9))); SYSCALL(0x030, OpShutdown(m, di, si)); SYSCALL(0x031, OpBind(m, di, si, dx)); SYSCALL(0x032, OpListen(m, di, si)); SYSCALL(0x033, OpGetsockname(m, di, si, dx)); SYSCALL(0x034, OpGetpeername(m, di, si, dx)); SYSCALL(0x036, OpSetsockopt(m, di, si, dx, r0, r8)); SYSCALL(0x037, OpGetsockopt(m, di, si, dx, r0, r8)); SYSCALL(0x039, OpFork(m)); SYSCALL(0x03B, OpExecve(m, di, si, dx)); SYSCALL(0x03D, OpWait4(m, di, si, dx, r0)); SYSCALL(0x03F, uname(P(di))); SYSCALL(0x048, OpFcntl(m, di, si, dx)); SYSCALL(0x049, OpFlock(m, di, si)); SYSCALL(0x04A, OpFsync(m, di)); SYSCALL(0x04B, OpFdatasync(m, di)); SYSCALL(0x04C, OpTruncate(m, di, si)); SYSCALL(0x04D, OpFtruncate(m, di, si)); SYSCALL(0x04F, OpGetcwd(m, di, si)); SYSCALL(0x050, OpChdir(m, di)); SYSCALL(0x052, OpRename(m, di, si)); SYSCALL(0x053, OpMkdir(m, di, si)); SYSCALL(0x054, OpRmdir(m, di)); SYSCALL(0x055, DoCreat(m, di, si)); SYSCALL(0x056, OpLink(m, di, si)); SYSCALL(0x057, OpUnlink(m, di)); SYSCALL(0x058, OpSymlink(m, di, si)); SYSCALL(0x05A, OpChmod(m, di, si)); SYSCALL(0x05B, OpFchmod(m, di, si)); SYSCALL(0x060, OpGettimeofday(m, di, si)); SYSCALL(0x061, OpGetrlimit(m, di, si)); SYSCALL(0x062, OpGetrusage(m, di, si)); SYSCALL(0x063, sysinfo(PNN(di))); SYSCALL(0x064, times(PNN(di))); SYSCALL(0x066, OpGetUid(m)); SYSCALL(0x068, OpGetGid(m)); SYSCALL(0x06E, OpGetPpid(m)); SYSCALL(0x075, setresuid(di, si, dx)); SYSCALL(0x077, setresgid(di, si, dx)); SYSCALL(0x082, OpSigsuspend(m, di)); SYSCALL(0x085, OpMknod(m, di, si, dx)); SYSCALL(0x08C, getpriority(di, si)); SYSCALL(0x08D, setpriority(di, si, dx)); SYSCALL(0x0A0, setrlimit(di, P(si))); SYSCALL(0x084, utime(PNN(di), PNN(si))); SYSCALL(0x0EB, OpUtimes(m, di, si)); SYSCALL(0x09D, OpPrctl(m, di, si, dx, r0, r8)); SYSCALL(0x09E, OpArchPrctl(m, di, si)); SYSCALL(0x0BA, OpGetTid(m)); SYSCALL(0x0CB, sched_setaffinity(di, si, P(dx))); SYSCALL(0x0D9, OpGetdents(m, di, si, dx)); SYSCALL(0x0DD, OpFadvise(m, di, si, dx, r0)); SYSCALL(0x0E4, OpClockGettime(m, di, si)); SYSCALL(0x101, OpOpenat(m, di, si, dx, r0)); SYSCALL(0x102, OpMkdirat(m, di, si, dx)); SYSCALL(0x104, fchownat(GetAfd(m, di), P(si), dx, r0, XlatAtf(r8))); SYSCALL(0x105, futimesat(GetAfd(m, di), P(si), P(dx))); SYSCALL(0x106, OpFstatat(m, di, si, dx, r0)); SYSCALL(0x107, OpUnlinkat(m, di, si, dx)); SYSCALL(0x108, OpRenameat(m, di, si, dx, r0)); SYSCALL(0x10B, OpReadlinkat(m, di, si, dx, r0)); SYSCALL(0x10D, OpFaccessat(m, di, si, dx, r0)); SYSCALL(0x113, splice(di, P(si), dx, P(r0), r8, XlatAtf(r9))); SYSCALL(0x115, sync_file_range(di, si, dx, XlatAtf(r0))); SYSCALL(0x118, utimensat(GetAfd(m, di), P(si), P(dx), XlatAtf(r0))); SYSCALL(0x120, OpAccept4(m, di, si, dx, r0)); SYSCALL(0x177, vmsplice(di, P(si), dx, r0)); case 0x3C: OpExit(m, di); case 0xE7: OpExitGroup(m, di); case 0x00F: OpRestore(m); return; case 0x03E: OpKill(m, di, si); return; default: WARNF("missing syscall 0x%03x", ax); ax = enosys(); break; } Write64(m->ax, ax != -1 ? ax : -(XlatErrno(errno) & 0xfff)); for (i = 0; i < m->freelist.i; ++i) free(m->freelist.p[i]); m->freelist.i = 0; }