cosmopolitan/tool/build/lib/syscall.c
Justine Tunney 416fd86676 Make improvements
- Emulator can now test the αcτµαlly pδrταblε εxεcµταblε bootloader

- Whipped up a webserver named redbean. It services 150k requests per
  second on a single core. Bundling assets inside zip enables extremely
  fast serving for two reasons. The first is that zip central directory
  lookups go faster than stat() system calls. The second is that both
  zip and gzip content-encoding use DEFLATE, therefore, compressed
  responses can be served via the sendfile() system call which does an
  in-kernel copy directly from the zip executable structure. Also note
  that red bean zip executables can be deployed easily to all platforms,
  since these native executables work on Linux, Mac, BSD, and Windows.

- Address sanitizer now works very well
2020-09-14 00:02:34 -07:00

1085 lines
32 KiB
C

/*-*- 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 │
│ │
│ This program is free software; you can redistribute it and/or modify │
│ it under the terms of the GNU General Public License as published by │
│ the Free Software Foundation; version 2 of the License. │
│ │
│ This program is distributed in the hope that it will be useful, but │
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
│ General Public License for more details. │
│ │
│ You should have received a copy of the GNU General Public License │
│ along with this program; if not, write to the Free Software │
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
│ 02110-1301 USA │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigaction-linux.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/winsize.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.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/map.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/sa.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/tcp.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/struct/timezone.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/machine.h"
#include "tool/build/lib/memory.h"
#include "tool/build/lib/pml4t.h"
#include "tool/build/lib/syscall.h"
#include "tool/build/lib/throw.h"
#include "tool/build/lib/xlaterrno.h"
#define AT_FDCWD_LINUX -100
#define TIOCGWINSZ_LINUX 0x5413
#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,
.read = read,
.write = write,
.ioctl = ioctl,
};
static int XlatSignal(int sig) {
switch (sig) {
XLAT(1, SIGHUP);
XLAT(2, SIGINT);
XLAT(3, SIGQUIT);
XLAT(4, SIGILL);
XLAT(5, SIGTRAP);
XLAT(6, SIGABRT);
XLAT(7, SIGBUS);
XLAT(8, SIGFPE);
XLAT(9, SIGKILL);
XLAT(10, SIGUSR1);
XLAT(11, SIGSEGV);
XLAT(13, SIGPIPE);
XLAT(14, SIGALRM);
XLAT(15, SIGTERM);
XLAT(21, SIGTTIN);
XLAT(22, SIGTTOU);
XLAT(24, SIGXCPU);
XLAT(25, SIGXFSZ);
XLAT(26, SIGVTALRM);
XLAT(27, SIGPROF);
XLAT(28, SIGWINCH);
XLAT(17, SIGCHLD);
XLAT(18, SIGCONT);
XLAT(29, SIGIO);
XLAT(19, SIGSTOP);
XLAT(31, SIGSYS);
XLAT(20, SIGTSTP);
XLAT(23, SIGURG);
XLAT(12, SIGUSR2);
XLAT(0x2000, SIGSTKSZ);
XLAT(30, SIGPWR);
XLAT(0x10, SIGSTKFLT);
default:
return einval();
}
}
static int XlatSig(int x) {
switch (x) {
XLAT(0, SIG_BLOCK);
XLAT(1, SIG_UNBLOCK);
XLAT(2, SIG_SETMASK);
default:
return einval();
}
}
static int XlatSocketFamily(int x) {
switch (x) {
XLAT(0, AF_INET);
XLAT(2, AF_INET);
default:
return epfnosupport();
}
}
static int XlatSocketType(int x) {
switch (x) {
XLAT(1, SOCK_STREAM);
XLAT(2, SOCK_DGRAM);
default:
return einval();
}
}
static int XlatSocketProtocol(int x) {
switch (x) {
XLAT(6, IPPROTO_TCP);
XLAT(17, IPPROTO_UDP);
default:
return einval();
}
}
static unsigned XlatSocketFlags(int flags) {
unsigned res = 0;
if (flags & 0x080000) res |= SOCK_CLOEXEC;
if (flags & 0x000800) res |= SOCK_NONBLOCK;
return res;
}
static int XlatSocketLevel(int x) {
switch (x) {
XLAT(0, SOL_IP);
XLAT(1, SOL_SOCKET);
XLAT(6, SOL_TCP);
XLAT(17, SOL_UDP);
default:
return einval();
}
}
static int XlatSocketOptname(int x) {
switch (x) {
XLAT(2, SO_REUSEADDR);
XLAT(15, SO_REUSEPORT);
XLAT(9, SO_KEEPALIVE);
XLAT(5, SO_DONTROUTE);
XLAT(7, SO_SNDBUF);
XLAT(8, SO_RCVBUF);
XLAT(13, SO_LINGER);
default:
return einval();
}
}
static int XlatMapFlags(int x) {
unsigned res = 0;
if (x & 1) res |= MAP_SHARED;
if (x & 2) res |= MAP_PRIVATE;
if (x & 16) res |= MAP_FIXED;
if (x & 32) res |= MAP_ANONYMOUS;
return res;
}
static int XlatAccess(int x) {
unsigned res = F_OK;
if (x & 1) res |= X_OK;
if (x & 2) res |= W_OK;
if (x & 4) res |= R_OK;
return res;
}
static int XlatSigaction(int x) {
unsigned res = 0;
if (x & 0x00000001) res |= SA_NOCLDSTOP;
if (x & 0x00000002) res |= SA_NOCLDWAIT;
if (x & 0x00000004) res |= SA_SIGINFO;
if (x & 0x04000000) res |= SA_RESTORER;
if (x & 0x08000000) res |= SA_ONSTACK;
if (x & 0x10000000) res |= SA_RESTART;
if (x & 0x40000000) res |= SA_NODEFER;
if (x & 0x40000000) res |= SA_NOMASK;
if (x & 0x80000000) res |= SA_RESETHAND;
if (x & 0x80000000) res |= SA_ONESHOT;
return res;
}
static int XlatSo(int x) {
switch (x) {
XLAT(-1, SO_EXCLUSIVEADDRUSE);
XLAT(1, SO_DEBUG);
XLAT(2, SO_REUSEADDR);
XLAT(3, SO_TYPE);
XLAT(4, SO_ERROR);
XLAT(5, SO_DONTROUTE);
XLAT(6, SO_BROADCAST);
XLAT(7, SO_SNDBUF);
XLAT(8, SO_RCVBUF);
XLAT(9, SO_KEEPALIVE);
XLAT(10, SO_OOBINLINE);
XLAT(13, SO_LINGER);
XLAT(15, SO_REUSEPORT);
XLAT(17, SO_PEERCRED);
XLAT(18, SO_RCVLOWAT);
XLAT(19, SO_SNDLOWAT);
XLAT(20, SO_RCVTIMEO);
XLAT(21, SO_SNDTIMEO);
XLAT(29, SO_TIMESTAMP);
XLAT(30, SO_ACCEPTCONN);
XLAT(38, SO_PROTOCOL);
XLAT(39, SO_DOMAIN);
XLAT(47, SO_MAX_PACING_RATE);
default:
return x;
}
}
static int XlatClock(int x) {
switch (x) {
XLAT(0, CLOCK_REALTIME);
XLAT(4, CLOCK_MONOTONIC);
default:
return x;
}
}
static int XlatTcp(int x) {
switch (x) {
XLAT(1, TCP_NODELAY);
XLAT(2, TCP_MAXSEG);
XLAT(23, TCP_FASTOPEN);
XLAT(4, TCP_KEEPIDLE);
XLAT(5, TCP_KEEPINTVL);
XLAT(6, TCP_KEEPCNT);
default:
return x;
}
}
static int XlatFd(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 XlatAfd(struct Machine *m, int fd) {
if (fd == AT_FDCWD_LINUX) return AT_FDCWD;
return XlatFd(m, fd);
}
static int XlatAtf(int x) {
unsigned res = 0;
if (x & 0x0100) res |= AT_SYMLINK_NOFOLLOW;
if (x & 0x0200) res |= AT_REMOVEDIR;
if (x & 0x0400) res |= AT_SYMLINK_FOLLOW;
if (x & 0x1000) res |= AT_EMPTY_PATH;
return res;
}
static int XlatMsyncFlags(int x) {
unsigned res = 0;
if (x & 1) res |= MS_ASYNC;
if (x & 2) res |= MS_INVALIDATE;
if (x & 4) res |= MS_SYNC;
return res;
}
static unsigned XlatOpenFlags(unsigned flags) {
unsigned res = 0;
if ((flags & 3) == 0) res = O_RDONLY;
if ((flags & 3) == 1) res = O_WRONLY;
if ((flags & 3) == 3) res = O_RDWR;
if (flags & 0x80000) res |= O_CLOEXEC;
if (flags & 0x400) res |= O_APPEND;
if (flags & 0x40) res |= O_CREAT;
if (flags & 0x80) res |= O_EXCL;
if (flags & 0x200) res |= O_TRUNC;
if (flags & 0x0800) res |= O_NDELAY;
if (flags & 0x4000) res |= O_DIRECT;
if (flags & 0x0800) res |= O_NONBLOCK;
if (flags & 0x1000) res |= O_DSYNC;
if (flags & 0x101000) res |= O_RSYNC;
if (flags & 0x040000) res |= O_NOATIME;
return res;
}
static int XlatFcntlCmd(int cmd) {
switch (cmd) {
XLAT(1, F_GETFD);
XLAT(2, F_SETFD);
XLAT(3, F_GETFL);
XLAT(4, F_SETFL);
default:
return einval();
}
}
static int XlatFcntlArg(int arg) {
switch (arg) {
XLAT(0, 0);
XLAT(1, FD_CLOEXEC);
default:
return einval();
}
}
static struct sigaction *CoerceSigactionToCosmo(
struct sigaction *dst, const struct sigaction$linux *src) {
if (!src) return NULL;
memset(dst, 0, sizeof(*dst));
ASSIGN(dst->sa_handler, src->sa_handler);
ASSIGN(dst->sa_restorer, src->sa_restorer);
ASSIGN(dst->sa_flags, src->sa_flags);
ASSIGN(dst->sa_mask, src->sa_mask);
return dst;
}
static struct sigaction$linux *CoerceSigactionToLinux(
struct sigaction$linux *dst, const struct sigaction *src) {
if (!dst) return NULL;
memset(dst, 0, sizeof(*dst));
ASSIGN(dst->sa_handler, src->sa_handler);
ASSIGN(dst->sa_restorer, src->sa_restorer);
ASSIGN(dst->sa_flags, src->sa_flags);
ASSIGN(dst->sa_mask, src->sa_mask);
return dst;
}
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 OpMmap(struct Machine *m, int64_t virt, size_t size, int prot,
int flags, int fd, int64_t off) {
void *real;
flags = XlatMapFlags(flags);
if (fd != -1 && (fd = XlatFd(m, fd)) == -1) return -1;
real = mmap(NULL, size, prot, flags & ~MAP_FIXED, fd, off);
if (real == MAP_FAILED) return -1;
if (!(flags & MAP_FIXED)) {
if (0 <= virt && virt < 0x400000) virt = 0x400000;
if ((virt = FindPml4t(m->cr3, virt, size)) == -1) return -1;
}
CHECK_NE(-1, RegisterMemory(m, virt, real, size));
return virt;
}
static int OpMunmap(struct Machine *m, int64_t addr, uint64_t size) {
return FreePml4t(m->cr3, addr, size, free, munmap);
}
static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) {
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;
}
static void *GetDirectBuf(struct Machine *m, int64_t addr, size_t *size) {
void *page;
*size = MIN(*size, 0x1000 - (addr & 0xfff));
if (!(page = FindReal(m, addr))) return MAP_FAILED;
return page;
}
static struct iovec *GetDirectIov(struct Machine *m, int64_t addr, int *len) {
int i;
size_t n, size;
struct iovec *iov;
if (!__builtin_mul_overflow(sizeof(*iov), *len, &n) && n <= 0x7ffff000) {
if ((iov = malloc(n))) {
VirtualSend(m, iov, addr, n);
SetReadAddr(m, addr, n);
for (i = 0; i < *len; ++i) {
size = iov[i].iov_len;
if ((iov[i].iov_base = GetDirectBuf(
m, (int64_t)(intptr_t)iov[i].iov_base, &size)) == MAP_FAILED) {
free(iov);
return (struct iovec *)efault();
}
if (size < iov[i].iov_len) {
iov[i].iov_len = size;
*len = i + 1;
break;
}
}
return iov;
} else {
return (struct iovec *)-1;
}
} else {
return (struct iovec *)eoverflow();
}
}
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 path, int flags,
int mode) {
int fd, i;
flags = XlatOpenFlags(flags);
if ((dirfd = XlatAfd(m, dirfd)) == -1) return -1;
if ((i = MachineFdAdd(&m->fds)) == -1) return -1;
if ((fd = openat(dirfd, LoadStr(m, path), flags, mode)) != -1) {
m->fds.p[i].cb = &kMachineFdCbHost;
m->fds.p[i].fd = fd;
fd = i;
} else {
MachineFdRemove(&m->fds, i);
}
return fd;
}
static int OpPipe(struct Machine *m, int64_t pipefds_addr) {
void *p[2];
uint8_t b[8];
int rc, i, j, *pipefds;
if ((i = MachineFdAdd(&m->fds)) == -1) return -1;
if ((j = MachineFdAdd(&m->fds)) == -1) return -1;
if ((rc = pipe((pipefds = BeginStoreNp(m, pipefds_addr, 8, p, b)))) != -1) {
EndStoreNp(m, pipefds_addr, 8, p, b);
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];
} else {
MachineFdRemove(&m->fds, i);
MachineFdRemove(&m->fds, j);
}
return rc;
}
static int OpDup(struct Machine *m, int fd) {
int i, rc;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if ((i = MachineFdAdd(&m->fds)) == -1) return -1;
if ((rc = dup(fd)) != -1) {
m->fds.p[i].cb = &kMachineFdCbHost;
m->fds.p[i].fd = rc;
rc = i;
} else {
MachineFdRemove(&m->fds, i);
}
return rc;
}
static int OpDup2(struct Machine *m, int fd, int newfd) {
int i, rc;
if ((fd = XlatFd(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 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 addraddr,
int64_t addrsizeaddr, int flags) {
int i, rc;
void *addr;
uint8_t b[4];
uint32_t addrsize;
if ((fd = XlatFd(m, fd)) == -1) return -1;
VirtualSend(m, b, addrsizeaddr, 4);
SetReadAddr(m, addrsizeaddr, 4);
addrsize = Read32(b);
if (!(addr = malloc(addrsize))) return -1;
if ((i = rc = MachineFdAdd(&m->fds)) != -1) {
if ((rc = accept4(fd, addr, &addrsize, XlatSocketFlags(flags))) != -1) {
Write32(b, addrsize);
VirtualRecv(m, addrsizeaddr, b, 4);
VirtualRecv(m, addraddr, addr, addrsize);
SetWriteAddr(m, addraddr, addrsize);
m->fds.p[i].cb = &kMachineFdCbHost;
m->fds.p[i].fd = rc;
rc = i;
} else {
MachineFdRemove(&m->fds, i);
}
}
free(addr);
return rc;
}
static int OpConnectBind(struct Machine *m, int fd, intptr_t addraddr,
uint32_t addrsize,
int impl(int, const void *, uint32_t)) {
int rc;
void *addr;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if (!(addr = malloc(addrsize))) return -1;
VirtualSend(m, addr, addraddr, addrsize);
SetReadAddr(m, addraddr, addrsize);
rc = impl(fd, addr, addrsize);
free(addr);
return rc;
}
static int OpBind(struct Machine *m, int fd, intptr_t addraddr,
uint32_t addrsize) {
return OpConnectBind(m, fd, addraddr, addrsize, bind);
}
static int OpConnect(struct Machine *m, int fd, int64_t addraddr,
uint32_t addrsize) {
return OpConnectBind(m, fd, addraddr, addrsize, 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(optname)) == -1) return -1;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if (!(optval = malloc(optvalsize))) return -1;
VirtualSend(m, optval, optvaladdr, optvalsize);
SetReadAddr(m, optvaladdr, optvalsize);
rc = setsockopt(fd, level, optname, optval, optvalsize);
free(optval);
return rc;
}
static ssize_t OpRead(struct Machine *m, int fd, int64_t addr, size_t size) {
void *data;
ssize_t rc;
if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf();
if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault();
if ((rc = m->fds.p[fd].cb->read(m->fds.p[fd].fd, data, size)) != -1) {
SetWriteAddr(m, addr, rc);
}
return rc;
}
static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) {
void *data;
ssize_t rc;
if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf();
if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault();
if ((rc = m->fds.p[fd].cb->write(m->fds.p[fd].fd, data, size)) != -1) {
SetReadAddr(m, addr, size);
}
return rc;
}
static int OpIoctl(struct Machine *m, int fd, uint64_t request,
int64_t memaddr) {
int rc;
struct winsize ws;
if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf();
switch (request) {
case TIOCGWINSZ_LINUX:
rc = (m->fds.p[fd].cb->ioctl)(m->fds.p[fd].fd, TIOCGWINSZ, &ws);
VirtualRecv(m, memaddr, &ws, sizeof(ws));
SetWriteAddr(m, memaddr, sizeof(ws));
return rc;
default:
return einval();
}
}
static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size,
int64_t offset) {
void *data;
ssize_t rc;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault();
if ((rc = pread(fd, data, size, offset)) != -1) SetWriteAddr(m, addr, rc);
return rc;
}
static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size,
int64_t offset) {
void *data;
ssize_t rc;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault();
if ((rc = pwrite(fd, data, size, offset)) != -1) SetReadAddr(m, addr, size);
return rc;
}
static ssize_t OpReadv(struct Machine *m, int fd, int64_t iovaddr, int iovlen) {
ssize_t rc;
struct iovec *iov;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1;
rc = readv(fd, iov, iovlen);
free(iov);
return rc;
}
static ssize_t OpWritev(struct Machine *m, int fd, int64_t iovaddr,
int iovlen) {
ssize_t rc;
struct iovec *iov;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1;
rc = writev(fd, iov, iovlen);
free(iov);
return rc;
}
static int64_t OpLseek(struct Machine *m, int fd, int64_t offset, int whence) {
if ((fd = XlatFd(m, fd)) == -1) return -1;
return lseek(fd, offset, whence);
}
static ssize_t OpFtruncate(struct Machine *m, int fd, int64_t size) {
if ((fd = XlatFd(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 = XlatAfd(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 st,
int flags) {
int rc;
void *stp[2];
uint8_t *stbuf;
flags = XlatAtf(flags);
if ((dirfd = XlatAfd(m, dirfd)) == -1) return -1;
if (!(stbuf = malloc(sizeof(struct stat)))) return enomem();
if ((rc = fstatat(dirfd, LoadStr(m, path),
BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf), flags)) !=
-1) {
EndStoreNp(m, st, sizeof(stbuf), stp, stbuf);
}
free(stbuf);
return rc;
}
static int OpFstat(struct Machine *m, int fd, int64_t st) {
int rc;
void *stp[2];
uint8_t *stbuf;
if ((fd = XlatFd(m, fd)) == -1) return -1;
if (!(stbuf = malloc(sizeof(struct stat)))) return enomem();
if ((rc = fstat(fd, BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf))) != -1) {
EndStoreNp(m, st, sizeof(stbuf), stp, stbuf);
}
free(stbuf);
return rc;
}
static int OpListen(struct Machine *m, int fd, int backlog) {
if ((fd = XlatFd(m, fd)) == -1) return -1;
return listen(fd, backlog);
}
static int OpShutdown(struct Machine *m, int fd, int how) {
if ((fd = XlatFd(m, fd)) == -1) return -1;
return shutdown(fd, how);
}
static int OpFsync(struct Machine *m, int fd) {
if ((fd = XlatFd(m, fd)) == -1) return -1;
return fsync(fd);
}
static int OpFdatasync(struct Machine *m, int fd) {
if ((fd = XlatFd(m, fd)) == -1) return -1;
return fdatasync(fd);
}
static int OpFchmod(struct Machine *m, int fd, uint32_t mode) {
if ((fd = XlatFd(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 = XlatFd(m, fd)) == -1) return -1;
return fcntl(fd, cmd, arg);
}
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 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 OpRename(struct Machine *m, int64_t src, int64_t dst) {
return rename(LoadStr(m, src), 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) {
return enosys();
}
static int OpExecve(struct Machine *m, int64_t programaddr, int64_t argvaddr,
int64_t envpaddr) {
return enosys();
}
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);
VirtualRecv(m, bufaddr, buf, n);
SetWriteAddr(m, bufaddr, n);
res = bufaddr;
} else {
res = -1;
}
free(buf);
return res;
}
static int OpSigaction(struct Machine *m, int sig, int64_t act, int64_t old) {
return 0;
int rc;
struct OpSigactionMemory {
struct sigaction act, old;
uint8_t b[sizeof(struct sigaction$linux)];
void *p[2];
} * mem;
if (!(mem = malloc(sizeof(*mem)))) return enomem();
if ((rc = sigaction(
XlatSignal(sig),
CoerceSigactionToCosmo(
&mem->act, LoadBuf(m, act, sizeof(struct sigaction$linux))),
&mem->old)) != -1) {
CoerceSigactionToLinux(BeginStoreNp(m, old, sizeof(mem->b), mem->p, mem->b),
&mem->old);
EndStoreNp(m, old, sizeof(mem->b), mem->p, mem->b);
}
free(mem);
return rc;
}
static int OpNanosleep(struct Machine *m, int64_t req, int64_t rem) {
int rc;
void *p[2];
uint8_t b[sizeof(struct timespec)];
if ((rc = nanosleep(LoadBuf(m, req, sizeof(b)),
BeginStoreNp(m, rem, sizeof(b), p, b))) != -1) {
EndStoreNp(m, rem, sizeof(b), p, b);
}
return rc;
}
static int OpSigsuspend(struct Machine *m, int64_t maskaddr) {
void *p;
sigset_t mask;
if (!(p = LoadBuf(m, maskaddr, 8))) return efault();
memset(&mask, 0, sizeof(mask));
memcpy(&mask, p, 8);
return sigsuspend(&mask);
}
static int OpClockGettime(struct Machine *m, int clockid, int64_t ts) {
int rc;
void *tsp[2];
uint8_t tsb[sizeof(struct timespec)];
if ((rc = clock_gettime(XlatClock(clockid),
BeginStoreNp(m, ts, sizeof(tsb), tsp, tsb))) != -1) {
EndStoreNp(m, ts, sizeof(tsb), tsp, tsb);
}
return rc;
}
static int OpGettimeofday(struct Machine *m, int64_t tv, int64_t tz) {
int rc;
void *tvp[2], *tzp[2];
uint8_t tvb[sizeof(struct timeval)];
uint8_t tzb[sizeof(struct timezone)];
if ((rc = gettimeofday(BeginStoreNp(m, tv, sizeof(tvb), tvp, tvb),
BeginStoreNp(m, tz, sizeof(tzb), tzp, tzb))) != -1) {
EndStoreNp(m, tv, sizeof(tvb), tvp, tvb);
EndStoreNp(m, tz, sizeof(tzb), tzp, tzb);
}
return rc;
}
static int OpPoll(struct Machine *m, int64_t fdsaddr, uint64_t nfds,
int32_t timeout_ms) {
int rc;
size_t n;
struct pollfd *fds;
if (!__builtin_mul_overflow(sizeof(*fds), nfds, &n) && n <= 0x7ffff000) {
if ((fds = malloc(n))) {
VirtualSend(m, fds, fdsaddr, n);
SetReadAddr(m, fdsaddr, n);
rc = poll(fds, nfds, timeout_ms);
free(fds);
return rc;
} else {
return enomem();
}
} else {
return einval();
}
}
static int OpSigprocmask(struct Machine *m, int how, int64_t setaddr,
int64_t oldsetaddr) {
int rc;
sigset_t *set, oldset, ss;
if (setaddr) {
set = &ss;
memset(set, 0, sizeof(ss));
VirtualSend(m, set, setaddr, 8);
} else {
set = NULL;
}
if ((rc = sigprocmask(XlatSig(how), set, &oldset)) != -1) {
if (setaddr) VirtualRecv(m, setaddr, set, 8);
if (oldsetaddr) VirtualRecv(m, oldsetaddr, &oldset, 8);
}
return rc;
}
static int DoOpen(struct Machine *m, int64_t path, int flags, int mode) {
return OpOpenat(m, AT_FDCWD_LINUX, 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, AT_FDCWD_LINUX, path, mode, 0);
}
static int DoStat(struct Machine *m, int64_t path, int64_t st) {
return OpFstatat(m, AT_FDCWD_LINUX, path, st, 0);
}
static int DoLstat(struct Machine *m, int64_t path, int64_t st) {
return OpFstatat(m, AT_FDCWD_LINUX, path, st, 0x0400);
}
static int DoAccept(struct Machine *m, int fd, int64_t addraddr,
int64_t addrsizeaddr) {
return OpAccept4(m, fd, addraddr, addrsizeaddr, 0);
}
void OpSyscall(struct Machine *m, uint32_t rde) {
uint64_t i, ax, di, si, dx, r0, r8, r9;
ax = Read64(m->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(0x00D, OpSigaction(m, di, si, dx));
SYSCALL(0x00E, OpSigprocmask(m, di, si, dx));
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, sched_yield());
SYSCALL(0x01C, OpMadvise(m, di, si, dx));
SYSCALL(0x020, OpDup(m, di));
SYSCALL(0x021, OpDup2(m, di, si));
SYSCALL(0x022, pause());
SYSCALL(0x023, OpNanosleep(m, di, si));
SYSCALL(0x024, getitimer(di, PNN(si)));
SYSCALL(0x025, alarm(di));
SYSCALL(0x026, setitimer(di, PNN(si), P(dx)));
SYSCALL(0x027, getpid());
SYSCALL(0x028, sendfile(di, si, P(dx), r0));
SYSCALL(0x029, OpSocket(m, di, si, dx));
SYSCALL(0x02A, OpConnect(m, di, si, dx));
SYSCALL(0x02B, DoAccept(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, getsockname(di, PNN(si), PNN(dx)));
SYSCALL(0x034, getpeername(di, PNN(si), PNN(dx)));
SYSCALL(0x036, OpSetsockopt(m, di, si, dx, r0, r8));
SYSCALL(0x037, getsockopt(di, si, dx, PNN(r0), PNN(r8)));
SYSCALL(0x039, OpFork(m));
SYSCALL(0x03B, OpExecve(m, di, si, dx));
SYSCALL(0x03D, wait4(di, P(si), dx, P(r0)));
SYSCALL(0x03E, kill(di, si));
SYSCALL(0x03F, uname(P(di)));
SYSCALL(0x048, OpFcntl(m, di, si, dx));
SYSCALL(0x049, flock(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, getrlimit(di, P(si)));
SYSCALL(0x062, getrusage(di, P(si)));
SYSCALL(0x063, sysinfo(PNN(di)));
SYSCALL(0x064, times(PNN(di)));
SYSCALL(0x066, getuid());
SYSCALL(0x068, getgid());
SYSCALL(0x06E, getppid());
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, utimes(P(di), P(si)));
SYSCALL(0x09E, arch_prctl(di, si));
SYSCALL(0x0BA, gettid());
SYSCALL(0x0CB, sched_setaffinity(di, si, P(dx)));
SYSCALL(0x0DD, fadvise(di, si, dx, r0));
SYSCALL(0x0E4, OpClockGettime(m, di, si));
SYSCALL(0x101, OpOpenat(m, di, si, dx, r0));
SYSCALL(0x102, mkdirat(XlatAfd(m, di), P(si), dx));
SYSCALL(0x104, fchownat(XlatAfd(m, di), P(si), dx, r0, XlatAtf(r8)));
SYSCALL(0x105, futimesat(XlatAfd(m, di), P(si), P(dx)));
SYSCALL(0x106, OpFstatat(m, di, si, dx, r0));
SYSCALL(0x107, unlinkat(XlatAfd(m, di), P(si), XlatAtf(dx)));
SYSCALL(0x108, renameat(XlatAfd(m, di), P(si), XlatAfd(m, dx), P(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(XlatAfd(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(0xE7, HaltMachine(m, di | 0x100));
default:
DEBUGF("missing syscall %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;
}