From f49618ca9d674ec7e596118f85027c4b503862ac Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Sun, 31 Mar 2019 00:49:57 -0700 Subject: Add fesvr; only globally install fesvr headers/libs --- fesvr/syscall.cc | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 fesvr/syscall.cc (limited to 'fesvr/syscall.cc') diff --git a/fesvr/syscall.cc b/fesvr/syscall.cc new file mode 100644 index 0000000..6e8baf6 --- /dev/null +++ b/fesvr/syscall.cc @@ -0,0 +1,394 @@ +// See LICENSE for license details. + +#include "syscall.h" +#include "htif.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::placeholders; + +#define RISCV_AT_FDCWD -100 + +struct riscv_stat +{ + uint64_t dev; + uint64_t ino; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint64_t rdev; + uint64_t __pad1; + uint64_t size; + uint32_t blksize; + uint32_t __pad2; + uint64_t blocks; + uint64_t atime; + uint64_t __pad3; + uint64_t mtime; + uint64_t __pad4; + uint64_t ctime; + uint64_t __pad5; + uint32_t __unused4; + uint32_t __unused5; + + riscv_stat(const struct stat& s) + : dev(s.st_dev), ino(s.st_ino), mode(s.st_mode), nlink(s.st_nlink), + uid(s.st_uid), gid(s.st_gid), rdev(s.st_rdev), __pad1(0), + size(s.st_size), blksize(s.st_blksize), __pad2(0), + blocks(s.st_blocks), atime(s.st_atime), __pad3(0), + mtime(s.st_mtime), __pad4(0), ctime(s.st_ctime), __pad5(0), + __unused4(0), __unused5(0) {} +}; + +syscall_t::syscall_t(htif_t* htif) + : htif(htif), memif(&htif->memif()), table(2048) +{ + table[17] = &syscall_t::sys_getcwd; + table[25] = &syscall_t::sys_fcntl; + table[34] = &syscall_t::sys_mkdirat; + table[35] = &syscall_t::sys_unlinkat; + table[37] = &syscall_t::sys_linkat; + table[38] = &syscall_t::sys_renameat; + table[46] = &syscall_t::sys_ftruncate; + table[48] = &syscall_t::sys_faccessat; + table[49] = &syscall_t::sys_chdir; + table[56] = &syscall_t::sys_openat; + table[57] = &syscall_t::sys_close; + table[62] = &syscall_t::sys_lseek; + table[63] = &syscall_t::sys_read; + table[64] = &syscall_t::sys_write; + table[67] = &syscall_t::sys_pread; + table[68] = &syscall_t::sys_pwrite; + table[79] = &syscall_t::sys_fstatat; + table[80] = &syscall_t::sys_fstat; + table[93] = &syscall_t::sys_exit; + table[1039] = &syscall_t::sys_lstat; + table[2011] = &syscall_t::sys_getmainvars; + + register_command(0, std::bind(&syscall_t::handle_syscall, this, _1), "syscall"); + + int stdin_fd = dup(0), stdout_fd0 = dup(1), stdout_fd1 = dup(1); + if (stdin_fd < 0 || stdout_fd0 < 0 || stdout_fd1 < 0) + throw std::runtime_error("could not dup stdin/stdout"); + + fds.alloc(stdin_fd); // stdin -> stdin + fds.alloc(stdout_fd0); // stdout -> stdout + fds.alloc(stdout_fd1); // stderr -> stdout +} + +std::string syscall_t::do_chroot(const char* fn) +{ + if (!chroot.empty() && *fn == '/') + return chroot + fn; + return fn; +} + +std::string syscall_t::undo_chroot(const char* fn) +{ + if (chroot.empty()) + return fn; + if (strncmp(fn, chroot.c_str(), chroot.size()) == 0 + && (chroot.back() == '/' || fn[chroot.size()] == '/')) + return fn + chroot.size() - (chroot.back() == '/'); + return "/"; +} + +void syscall_t::handle_syscall(command_t cmd) +{ + if (cmd.payload() & 1) // test pass/fail + { + htif->exitcode = cmd.payload(); + if (htif->exit_code()) + std::cerr << "*** FAILED *** (tohost = " << htif->exit_code() << ")" << std::endl; + return; + } + else // proxied system call + dispatch(cmd.payload()); + + cmd.respond(1); +} + +reg_t syscall_t::sys_exit(reg_t code, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + htif->exitcode = code << 1 | 1; + return 0; +} + +static reg_t sysret_errno(sreg_t ret) +{ + return ret == -1 ? -errno : ret; +} + +reg_t syscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector buf(len); + ssize_t ret = read(fds.lookup(fd), &buf[0], len); + reg_t ret_errno = sysret_errno(ret); + if (ret > 0) + memif->write(pbuf, ret, &buf[0]); + return ret_errno; +} + +reg_t syscall_t::sys_pread(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector buf(len); + ssize_t ret = pread(fds.lookup(fd), &buf[0], len, off); + reg_t ret_errno = sysret_errno(ret); + if (ret > 0) + memif->write(pbuf, ret, &buf[0]); + return ret_errno; +} + +reg_t syscall_t::sys_write(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector buf(len); + memif->read(pbuf, len, &buf[0]); + reg_t ret = sysret_errno(write(fds.lookup(fd), &buf[0], len)); + return ret; +} + +reg_t syscall_t::sys_pwrite(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector buf(len); + memif->read(pbuf, len, &buf[0]); + reg_t ret = sysret_errno(pwrite(fds.lookup(fd), &buf[0], len, off)); + return ret; +} + +reg_t syscall_t::sys_close(reg_t fd, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + if (close(fds.lookup(fd)) < 0) + return sysret_errno(-1); + fds.dealloc(fd); + return 0; +} + +reg_t syscall_t::sys_lseek(reg_t fd, reg_t ptr, reg_t dir, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + return sysret_errno(lseek(fds.lookup(fd), ptr, dir)); +} + +reg_t syscall_t::sys_fstat(reg_t fd, reg_t pbuf, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + struct stat buf; + reg_t ret = sysret_errno(fstat(fds.lookup(fd), &buf)); + if (ret != (reg_t)-1) + { + riscv_stat rbuf(buf); + memif->write(pbuf, sizeof(rbuf), &rbuf); + } + return ret; +} + +reg_t syscall_t::sys_fcntl(reg_t fd, reg_t cmd, reg_t arg, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + return sysret_errno(fcntl(fds.lookup(fd), cmd, arg)); +} + +reg_t syscall_t::sys_ftruncate(reg_t fd, reg_t len, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + return sysret_errno(ftruncate(fds.lookup(fd), len)); +} + +reg_t syscall_t::sys_lstat(reg_t pname, reg_t len, reg_t pbuf, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + + struct stat buf; + reg_t ret = sysret_errno(lstat(do_chroot(&name[0]).c_str(), &buf)); + riscv_stat rbuf(buf); + if (ret != (reg_t)-1) + { + riscv_stat rbuf(buf); + memif->write(pbuf, sizeof(rbuf), &rbuf); + } + return ret; +} + +#define AT_SYSCALL(syscall, fd, name, ...) \ + (syscall(fds.lookup(fd), int(fd) == RISCV_AT_FDCWD ? do_chroot(name).c_str() : (name), __VA_ARGS__)) + +reg_t syscall_t::sys_openat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t mode, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + int fd = sysret_errno(AT_SYSCALL(openat, dirfd, &name[0], flags, mode)); + if (fd < 0) + return sysret_errno(-1); + return fds.alloc(fd); +} + +reg_t syscall_t::sys_fstatat(reg_t dirfd, reg_t pname, reg_t len, reg_t pbuf, reg_t flags, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + + struct stat buf; + reg_t ret = sysret_errno(AT_SYSCALL(fstatat, dirfd, &name[0], &buf, flags)); + if (ret != (reg_t)-1) + { + riscv_stat rbuf(buf); + memif->write(pbuf, sizeof(rbuf), &rbuf); + } + return ret; +} + +reg_t syscall_t::sys_faccessat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + return sysret_errno(AT_SYSCALL(faccessat, dirfd, &name[0], mode, 0)); +} + +reg_t syscall_t::sys_renameat(reg_t odirfd, reg_t popath, reg_t olen, reg_t ndirfd, reg_t pnpath, reg_t nlen, reg_t a6) +{ + std::vector opath(olen), npath(nlen); + memif->read(popath, olen, &opath[0]); + memif->read(pnpath, nlen, &npath[0]); + return sysret_errno(renameat(fds.lookup(odirfd), int(odirfd) == RISCV_AT_FDCWD ? do_chroot(&opath[0]).c_str() : &opath[0], + fds.lookup(ndirfd), int(ndirfd) == RISCV_AT_FDCWD ? do_chroot(&npath[0]).c_str() : &npath[0])); +} + +reg_t syscall_t::sys_linkat(reg_t odirfd, reg_t poname, reg_t olen, reg_t ndirfd, reg_t pnname, reg_t nlen, reg_t flags) +{ + std::vector oname(olen), nname(nlen); + memif->read(poname, olen, &oname[0]); + memif->read(pnname, nlen, &nname[0]); + return sysret_errno(linkat(fds.lookup(odirfd), int(odirfd) == RISCV_AT_FDCWD ? do_chroot(&oname[0]).c_str() : &oname[0], + fds.lookup(ndirfd), int(ndirfd) == RISCV_AT_FDCWD ? do_chroot(&nname[0]).c_str() : &nname[0], + flags)); +} + +reg_t syscall_t::sys_unlinkat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + return sysret_errno(AT_SYSCALL(unlinkat, dirfd, &name[0], flags)); +} + +reg_t syscall_t::sys_mkdirat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector name(len); + memif->read(pname, len, &name[0]); + return sysret_errno(AT_SYSCALL(mkdirat, dirfd, &name[0], mode)); +} + +reg_t syscall_t::sys_getcwd(reg_t pbuf, reg_t size, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector buf(size); + char* ret = getcwd(&buf[0], size); + if (ret == NULL) + return sysret_errno(-1); + std::string tmp = undo_chroot(&buf[0]); + if (size <= tmp.size()) + return -ENOMEM; + memif->write(pbuf, tmp.size() + 1, &tmp[0]); + return tmp.size() + 1; +} + +reg_t syscall_t::sys_getmainvars(reg_t pbuf, reg_t limit, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + std::vector args = htif->target_args(); + std::vector words(args.size() + 3); + words[0] = args.size(); + words[args.size()+1] = 0; // argv[argc] = NULL + words[args.size()+2] = 0; // envp[0] = NULL + + size_t sz = (args.size() + 3) * sizeof(words[0]); + for (size_t i = 0; i < args.size(); i++) + { + words[i+1] = sz + pbuf; + sz += args[i].length() + 1; + } + + std::vector bytes(sz); + memcpy(&bytes[0], &words[0], sizeof(words[0]) * words.size()); + for (size_t i = 0; i < args.size(); i++) + strcpy(&bytes[words[i+1] - pbuf], args[i].c_str()); + + if (bytes.size() > limit) + return -ENOMEM; + + memif->write(pbuf, bytes.size(), &bytes[0]); + return 0; +} + +reg_t syscall_t::sys_chdir(reg_t path, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) +{ + size_t size = 0; + while (memif->read_uint8(path + size++)) + ; + std::vector buf(size); + for (size_t offset = 0;; offset++) + { + buf[offset] = memif->read_uint8(path + offset); + if (!buf[offset]) + break; + } + return sysret_errno(chdir(buf.data())); +} + +void syscall_t::dispatch(reg_t mm) +{ + reg_t magicmem[8]; + memif->read(mm, sizeof(magicmem), magicmem); + + reg_t n = magicmem[0]; + if (n >= table.size() || !table[n]) + throw std::runtime_error("bad syscall #" + std::to_string(n)); + + magicmem[0] = (this->*table[n])(magicmem[1], magicmem[2], magicmem[3], magicmem[4], magicmem[5], magicmem[6], magicmem[7]); + + memif->write(mm, sizeof(magicmem), magicmem); +} + +reg_t fds_t::alloc(int fd) +{ + reg_t i; + for (i = 0; i < fds.size(); i++) + if (fds[i] == -1) + break; + + if (i == fds.size()) + fds.resize(i+1); + + fds[i] = fd; + return i; +} + +void fds_t::dealloc(reg_t fd) +{ + fds[fd] = -1; +} + +int fds_t::lookup(reg_t fd) +{ + if (int(fd) == RISCV_AT_FDCWD) + return AT_FDCWD; + return fd >= fds.size() ? -1 : fds[fd]; +} + +void syscall_t::set_chroot(const char* where) +{ + char buf1[PATH_MAX], buf2[PATH_MAX]; + + if (getcwd(buf1, sizeof(buf1)) == NULL + || chdir(where) != 0 + || getcwd(buf2, sizeof(buf2)) == NULL + || chdir(buf1) != 0) + { + fprintf(stderr, "could not chroot to %s\n", where); + exit(-1); + } + + chroot = buf2; +} -- cgit v1.1