Commit Diff


commit - /dev/null
commit + 074dbafb1fd0553fd2e83ff789a6d4b8ee5c71a5
blob - /dev/null
blob + b4943239cdbae64207c5335fedf98807b744e273 (mode 644)
Binary files /dev/null and .Makefile.swp differ
blob - /dev/null
blob + 33c1fb532c831eddaf2d07ce83f3a43d2de493ca (mode 644)
Binary files /dev/null and .rvemu.c.swp differ
blob - /dev/null
blob + e1198275f127c7dbff1e756da68980c4b937adf7 (mode 644)
Binary files /dev/null and .test.S.swp differ
blob - /dev/null
blob + e1dcf57e9f918953e13413b837526d8c5992b7ff (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,19 @@
+.POSIX:
+
+CROSS = riscv32-unknown-linux-musl
+CFLAGS = -fno-PIC -O3
+LDFLAGS = -s -Wl,--image-base,0x78000000 -no-pie -static -lpthread -lutil
+
+all: rvemu test.elf
+
+clean:
+	rm -f rvemu test.o test.elf *.core
+
+run: rvemu test.elf
+	./rvemu test.elf
+
+test.o: test.S
+	${CROSS}-as -o test.o test.S
+
+test.elf: test.o test.ld
+	${CROSS}-ld -s -T test.ld -o test.elf test.o
blob - /dev/null
blob + a11261750e131eaf4e04ca1386ecc82b41631c7b (mode 644)
--- /dev/null
+++ rvemu.c
@@ -0,0 +1,1443 @@
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <libelf.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <assert.h>
+#include <sched.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+typedef uint8_t  u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t   i8;
+typedef int16_t  i16;
+typedef int32_t  i32;
+typedef int64_t  i64;
+
+typedef unsigned int uint;
+
+#define DEBUG 0
+
+#if DEBUG
+# define eprintf(...) fprintf (stderr, __VA_ARGS__)
+#else
+# define eprintf(...)
+#endif
+
+struct cpu {
+	u32 regs[31];
+	u32 pc;
+};
+
+static u32 cpu_get (const struct cpu *cpu, u8 reg)
+{
+	return reg == 0 ? 0 : cpu->regs[reg - 1];
+}
+
+static void cpu_set (struct cpu *cpu, u8 reg, u32 val)
+{
+	if (reg != 0)
+		cpu->regs[reg - 1] = val;
+}
+
+static u32 cpu_fetch (struct cpu *cpu)
+{
+	const u32 i = *(const u32 *)(size_t)cpu->pc;
+	//eprintf ("%08x: %08x\n", cpu->pc, i);
+	cpu->pc += 4;
+	return i;
+}
+
+static int map_errno (int err)
+{
+	const int errnos[] = {
+		[EPERM]		= 1,
+		[ENOENT]	= 2,
+		[ESRCH]		= 3,
+		[EINTR]		= 4,
+		[EIO]		= 5,
+		[ENXIO]		= 6,
+		[E2BIG]		= 7,
+		[ENOEXEC]	= 8,
+		[EBADF]		= 9,
+		[ECHILD]	= 10,
+		[EAGAIN]	= 11,
+		[ENOMEM]	= 12,
+		[EACCES]	= 13,
+		[EFAULT]	= 14,
+		[ENOTBLK]	= 15,
+		[EBUSY]		= 16,
+		[EEXIST]	= 17,
+		[EXDEV]		= 18,
+		[ENODEV]	= 19,
+		[ENOTDIR]	= 20,
+		[EISDIR]	= 21,
+		[EINVAL]	= 22,
+		[ENFILE]	= 23,
+		[EMFILE]	= 24,
+		[ENOTTY]	= 25,
+		[ETXTBSY]	= 26,
+		[EFBIG]		= 27,
+		[ENOSPC]	= 28,
+		[ESPIPE]	= 29,
+		[EROFS]		= 30,
+		[EMLINK]	= 31,
+		[EPIPE]		= 32,
+		[EDOM]		= 33,
+		[ERANGE]	= 34,
+		[EDEADLK]	= 35,
+		[ENAMETOOLONG]	= 36,
+		[ENOLCK]	= 37,
+		[ENOSYS]	= 38,
+	};
+	return errnos[err];
+}
+
+static int map (int x) {
+	return x < 0 ? -map_errno (errno) : x;
+}
+
+/*
+struct linux_stat {
+	u64 dev;
+	u64 ino;
+	u32 mode;
+	u32 nlink;
+	u32 uid;
+	u32 gid;
+	u64 rdev;
+	u64 __pad1;
+	u32 size;
+	u32 blksize;
+	u32 __pad2;
+	u32 blocks;
+	u32 atime;
+	u32 atime_ns;
+	u32 mtime;
+	u32 mtime_ns;
+	u32 ctime;
+	u32 ctime_ns;
+	u32 __unused4;
+	u32 __unused5;
+};
+*/
+
+struct linux_stat64 {
+	u64 dev;
+	u64 ino;
+	u32 mode;
+	u32 nlink;
+	u32 uid;
+	u32 gid;
+	u64 rdev;
+	u64 __pad1;
+	u64 size;
+	u32 blksize;
+	u32 __pad2;
+	u64 blocks;
+	u32 atime;
+	u32 atime_ns;
+	u32 mtime;
+	u32 mtime_ns;
+	u32 ctime;
+	u32 ctime_ns;
+	u32 __unused4;
+	u32 __unused5;
+};
+
+struct linux_utsname {
+	char sysname[65];
+	char nodename[65];
+	char release[65];
+	char version[65];
+	char machine[65];
+};
+
+static void stat_to_linux_stat (struct linux_stat64 *lst, const struct stat *st)
+{
+	memset (lst, 0, sizeof (*lst));
+	lst->dev	= st->st_dev;
+	lst->ino	= st->st_dev;
+	lst->mode	= st->st_dev;
+	lst->nlink	= st->st_dev;
+	lst->uid	= st->st_dev;
+	lst->gid	= st->st_dev;
+	lst->rdev	= st->st_dev;
+	lst->size	= st->st_dev;
+	lst->blksize	= st->st_dev;
+	lst->blocks	= st->st_dev;
+	lst->atime	= st->st_atim.tv_sec;
+	lst->atime_ns	= st->st_atim.tv_nsec;
+	lst->mtime	= st->st_mtim.tv_sec;
+	lst->mtime_ns	= st->st_mtim.tv_nsec;
+	lst->ctime	= st->st_ctim.tv_sec;
+	lst->ctime_ns	= st->st_ctim.tv_nsec;
+}
+
+static void utsname_to_linux_utsname (struct linux_utsname *lun, const struct utsname *un)
+{
+	memset (lun, 0, sizeof (*lun));
+	strlcpy (lun->sysname,	un->sysname, 65);
+	strlcpy (lun->nodename,	un->nodename, 65);
+	strlcpy (lun->release,	un->release, 65);
+	strlcpy (lun->version,	un->version, 65);
+	strlcpy (lun->machine,	un->machine, 65);
+}
+
+static int enosys (const char *sys)
+{
+	warnx ("unimplemented syscall: %s", sys);
+	return -map_errno (ENOSYS);
+}
+
+static int my_execve (const char *path, char **argv, char **envp)
+{
+	return -enosys ("execve");
+}
+
+#define ptr(T, x) ((T *)(size_t)(x))
+#define str(x) ptr (const char, x)
+static void ecall (struct cpu *cpu)
+{
+	struct linux_stat64 lst;
+	struct linux_utsname lun;
+	struct stat st;
+	struct utsname un;
+	void *ptr;
+	const u32 a0 = cpu_get (cpu, 10);
+	const u32 a1 = cpu_get (cpu, 11);
+	const u32 a2 = cpu_get (cpu, 12);
+	const u32 a3 = cpu_get (cpu, 13);
+	const u32 a4 = cpu_get (cpu, 14);
+	const u32 a5 = cpu_get (cpu, 15);
+	const u32 a7 = cpu_get (cpu, 17);
+	u32 ret;
+	switch (a7) {
+	case 17: // getcwd
+		ret = enosys ("getcwd");
+		break;
+	case 23: // dup
+		ret = map (dup ((int)a0));
+		break;
+	case 24: // dup3
+		ret = map (dup3 ((int)a0, (int)a1, (int)a2));
+		break;
+	case 25: // fcntl
+		ret = enosys ("fcntl");
+		break;
+	case 29: // ioctl
+		ret = enosys ("ioctl");
+		break;
+	case 32: // flock
+		ret = map (flock ((int)a0, (int)a1));
+		break;
+	case 33: // mknodat
+		ret = map (mknodat ((int)a0, str (a1), (mode_t)a2, (dev_t)a3));
+
+	case 34: // mkdirat
+		ret = map (mkdirat ((int)a0, str (a1), (mode_t)a2));
+		break;
+	case 35: // unlinkat
+		ret = map (unlinkat ((int)a0, str (a1), (int)a2));
+		break;
+	case 36: // symlinkat
+		ret = map (symlinkat (str (a0), (int)a1, str (a2)));
+		break;
+	case 37: // linkat
+		ret = map (linkat ((int)a0, str (a1), (int)a2, str (a3), (int)a4));
+		break;
+	case 38: // renameat
+		ret = map (renameat ((int)a0, str (a1), (int)a2, str (a3)));
+		break;
+	case 39: // umount
+		ret = enosys ("umount");
+		break;
+	case 40: // mount
+		ret = enosys ("mount");
+		break;
+	case 41: // pivot_root
+		ret = enosys ("pivot_root");
+		break;
+	case 42: // nfsservctl
+		ret = enosys ("nfsservctl");
+		break;
+	case 43: // statfs
+		ret = enosys ("statfs");
+		break;
+	case 44: // fstatfs
+		ret = enosys ("fstatfs");
+		break;
+	case 45: // truncate
+		ret = map (truncate (str (a0), (off_t)a1));
+		break;
+		break;
+	case 46: // ftruncate
+		ret = map (ftruncate ((int)a0, (off_t)a1));
+		break;
+	case 47: // fallocate
+		ret = enosys ("fallocate");
+		break;
+	case 48: // faccessat
+		ret = map (faccessat ((int)a0, str (a1), (int)a2, (int)a3));
+		break;
+	case 49: // chdir
+		ret = map (chdir (str (a0)));
+		break;
+	case 50: // fchdir
+		ret = map (fchdir ((int)a0));
+		break;
+	case 51: // chroot
+		ret = map (chroot (str (a0)));
+		break;
+	case 52: // fchmod
+		ret = map (fchmod ((int)a0, (mode_t)a1));
+		break;
+	case 53: // fchmodat
+		ret = map (fchmodat ((int)a0, str (a1), (mode_t)a2, (int)a3));
+		break;
+	case 54: // fchownat
+		ret = map (fchownat ((int)a0, str (a1), (uid_t)a2, (gid_t)a3, (int)a4));
+		break;
+	case 55: // fchown
+		ret = map (fchown ((int)a0, (uid_t)a1, (gid_t)a2));
+		break;
+	case 56: // openat
+		ret = map (openat ((int)a0, str (a1), (int)a2, (int)a3));
+		break;
+	case 57: // close
+		ret = map (close ((int)a0));
+		break;
+	case 58: // vhangup
+		ret = enosys ("vhangup");
+		break;
+	case 59: // pipe2
+		ret = map (pipe2 (ptr (int, a0), (int)a1));
+		break;
+	case 60: // quotactl
+		ret = enosys ("quotactl");
+		break;
+	case 61: // getdents64
+		ret = enosys ("getdents64");
+		break;
+	case 62: // lseek
+		ret = map (lseek ((int)a0, (off_t)a1, (int)a2));
+		break;
+	case 63: // read
+		ret = map (read ((int)a0, ptr (void, a1), (size_t)a2));
+		break;
+	case 64: // write
+		ret = map (write ((int)a0, ptr (const void, a1), (size_t)a2));
+		break;
+	case 65: // readv
+		ret = enosys ("readv");
+		break;
+	case 66: // writev
+		ret = enosys ("writev");
+		break;
+	case 67: // pread
+		ret = map (pread ((int)a0, ptr (void, a1), (size_t)a2, (off_t)a3));
+		break;
+	case 68: // pwrite
+		ret = map (pwrite ((int)a0, ptr (const void, a1), (size_t)a2, (off_t)a3));
+		break;
+	case 69: // preadv
+		ret = enosys ("preadv");
+		break;
+	case 70: // pwritev
+		ret = enosys ("pwritev");
+		break;
+	case 71: // pwritev
+		ret = enosys ("sendfile");
+		break;
+	case 72: // pselect6
+		ret = enosys ("pselect6");
+		break;
+	case 73: // ppoll
+		ret = enosys ("ppoll");
+		break;
+	case 74: // signalfd4
+		ret = enosys ("signalfd4");
+		break;
+	case 75: // vmsplice
+		ret = enosys ("vmsplice");
+		break;
+	case 76: // splice
+		ret = enosys ("splice");
+		break;
+	case 77: // tee
+		ret = enosys ("tee");
+		break;
+	case 78: // readlinkat
+		ret = map (readlinkat ((int)a0, str (a1), ptr (char, a2), (size_t)a3));
+		break;
+	case 79: // fstatat
+		ret = map (fstatat ((int)a0, str (a1), &st, (int)a3));
+		stat_to_linux_stat (&lst, &st);
+		memcpy (ptr (void, a2), &lst, sizeof (lst));
+		break;
+	case 80: // fstat
+		ret = map (fstat ((int)a0, &st));
+		stat_to_linux_stat (&lst, &st);
+		memcpy (ptr (void, a1), &lst, sizeof (lst));
+		break;
+	case 81: // sync
+		sync ();
+		ret = 0;
+		break;
+	case 82: // fsync
+		ret = map (fsync ((int)a0));
+		break;
+	case 83: // fdatasync
+		ret = map (fdatasync ((int)a0));
+		break;
+	case 84: // sync_file_range
+		ret = enosys ("sync_file_range");
+		break;
+	case 85: // timerfd_create
+		ret = enosys ("timerfd_create");
+		break;
+	case 86: // timerfd_settime
+		ret = enosys ("timerfd_settime");
+		break;
+	case 87: // timerfd_gettime
+		ret = enosys ("timerfd_gettime");
+		break;
+	case 88: // utimensat
+		ret = enosys ("utimensat");
+		break;
+	case 89: // acct
+		ret = acct (str (a0));
+		break;
+	case 90: // capget
+		ret = enosys ("capget");
+		break;
+	case 91: // capset
+		ret = enosys ("capset");
+		break;
+	case 92: // personality
+		ret = enosys ("personality");
+		break;
+	case 93: // exit
+		exit (a0);
+		ret = -1;
+		break;
+	case 94: // exit_group
+		ret = enosys ("exit_group");
+		break;
+	case 95: // waitid
+		ret = enosys ("waitid");
+		break;
+	case 96: // set_tid_address
+		ret = enosys ("set_tid_address");
+		break;
+	case 97: // unshare
+		ret = enosys ("unshare");
+		break;
+	case 98: // futex
+		ret = enosys ("futex");
+		break;
+	case 99: // set_robust_limit
+		ret = enosys ("set_robust_limit");
+		break;
+	case 100: // get_robust_limit
+		ret = enosys ("get_robust_limit");
+		break;
+	case 101: // nanosleep
+		ret = enosys ("nanosleep");
+		break;
+	case 102: // gettimer
+		ret = enosys ("gettimer");
+		break;
+	case 103: // settimer
+		ret = enosys ("settimer");
+		break;
+	case 104: // kexec_load
+		ret = enosys ("kexec_load");
+		break;
+	case 105: // kexec_init_module
+		ret = enosys ("kexec_init_module");
+		break;
+	case 106: // kexec_delete_module
+		ret = enosys ("kexec_delete_module");
+		break;
+	case 107: // timer_create
+		ret = enosys ("timer_create");
+		break;
+	case 108: // timer_gettime
+		ret = enosys ("timer_gettime");
+		break;
+	case 109: // timer_getoverrun
+		ret = enosys ("timer_getoverrun");
+		break;
+	case 110: // timer_settime
+		ret = enosys ("timer_settime");
+		break;
+	case 111: // timer_delete
+		ret = enosys ("timer_delete");
+		break;
+	case 112: // clock_settime
+		ret = enosys ("clock_settime");
+		break;
+	case 113: // clock_gettime
+		ret = enosys ("clock_gettime");
+		break;
+	case 114: // clock_getres
+		ret = enosys ("clock_getres");
+		break;
+	case 115: // clock_nanosleep
+		ret = enosys ("clock_nanosleep");
+		break;
+	case 116: // syslog
+		ret = enosys ("syslog");
+		break;
+	case 117: // ptrace
+		ret = enosys ("ptrace");
+		break;
+	case 118: // sched_setparam
+		ret = enosys ("sched_setparam");
+		break;
+	case 119: // sched_setscheduler
+		ret = enosys ("sched_setscheduler");
+		break;
+	case 120: // sched_getscheduler
+		ret = enosys ("sched_getscheduler");
+		break;
+	case 121: // sched_getparam
+		ret = enosys ("sched_getparam");
+		break;
+	case 122: // sched_setaffinity
+		ret = enosys ("sched_setaffinity");
+		break;
+	case 123: // sched_getaffinity
+		ret = enosys ("sched_getaffinity");
+		break;
+	case 124: // sched_yield
+		ret = map (sched_yield ());
+		break;
+	case 125: // sched_get_priority_max
+		ret = map (sched_get_priority_max ((int)a0));
+		break;
+	case 126: // sched_get_priority_min
+		ret = map (sched_get_priority_min ((int)a0));
+		break;
+	case 127: // sched_rr_get_interval
+		ret = enosys ("sched_rr_get_interval");
+		break;
+	case 128: // restart_syscall
+		ret = enosys ("restart_syscall");
+		break;
+	case 129: // kill
+		ret = map (kill ((pid_t)a0, (int)a1));
+		break;
+	case 130: // tkill
+		ret = enosys ("tkill");
+		break;
+	case 131: // tgkill
+		ret = enosys ("tgkill");
+		break;
+	case 132: // signalstack
+		ret = enosys ("signalstack");
+		break;
+	case 133: // sigaction
+		ret = enosys ("sigaction");
+		break;
+	case 134: // sigaction
+		ret = enosys ("sigaction");
+		break;
+	case 135: // rt_sigprocmask
+		ret = enosys ("rt_sigprocmask");
+		break;
+	case 136: // sigpending
+		ret = enosys ("sigpending");
+		break;
+	case 137: // sigtimedwait
+		ret = enosys ("sigtimedwait");
+		break;
+	case 138: // sigqueueinfo
+		ret = enosys ("sigqueueinfo");
+		break;
+	case 139: // sigreturn
+		ret = enosys ("sigreturn");
+		break;
+	case 140: // setpriority
+		ret = map (setpriority ((int)a0, (id_t)a1, (int)a2));
+		break;
+	case 141: // getpriority
+		ret = map (getpriority ((int)a0, (id_t)a1));
+		break;
+	case 142: // reboot
+		ret = enosys ("reboot");
+		break;
+	case 143: // setregid
+		ret = map (setregid ((gid_t)a0, (gid_t)a1));
+		break;
+	case 144: // setgid
+		ret = map (setgid ((gid_t)a0));
+		break;
+	case 145: // setreuid
+		ret = map (setreuid ((uid_t)a0, (uid_t)a1));
+		break;
+	case 146: // setuid
+		ret = map (setuid ((uid_t)a0));
+		break;
+	case 147: // setresuid
+		ret = map (setresuid ((uid_t)a0, (uid_t)a1, (uid_t)a2));
+		break;
+	case 148: // getresuid
+		ret = map (getresuid (ptr (uid_t, a0), ptr (uid_t, a1), ptr (uid_t, a2)));
+		break;
+	case 149: // setresgid
+		ret = map (setresgid ((gid_t)a0, (gid_t)a1, (gid_t)a2));
+		break;
+	case 150: // getresgid
+		ret = map (getresgid (ptr (gid_t, a0), ptr (gid_t, a1), ptr (gid_t, a2)));
+		break;
+	case 151: // setfsuid
+		ret = enosys ("setfsuid");
+		break;
+	case 152: // setfsgid
+		ret = enosys ("setfsgid");
+		break;
+	case 153: // times
+		ret = enosys ("times");
+		break;
+	case 154: // setpgid
+		ret = map (setpgid ((pid_t)a0, (pid_t)a1));
+		break;
+	case 155: // getpgid
+		ret = map (getpgid ((pid_t)a0));
+		break;
+	case 156: // getsid
+		ret = map (getsid ((pid_t)a0));
+		break;
+	case 158: // setsid
+		ret = map (setsid ());
+		break;
+	case 159: // setgroups
+		ret = enosys ("setgroups");
+		break;
+	case 160: // uname
+		ret = map (uname (&un));
+		utsname_to_linux_utsname (&lun, &un);
+		memcpy (ptr (void, a0), &lun, sizeof (lun));
+		break;
+	case 161: // sethostname
+		ret = enosys ("sethostname");
+		break;
+	case 162: // setdomainname
+		ret = enosys ("setdomainname");
+		break;
+	case 163: // getrlimit
+		ret = enosys ("getrlimit");
+		break;
+	case 164: // setrlimit
+		ret = enosys ("setrlimit");
+		break;
+	case 165: // getrusage
+		ret = enosys ("getrusage");
+		break;
+	case 166: // umask
+		ret = map (umask ((mode_t)a0));
+		break;
+	case 167: // prctl
+		ret = enosys ("prctl");
+		break;
+	case 168: // getcpu
+		ret = enosys ("getcpu");
+		break;
+	case 169: // gettimeofday
+		ret = enosys ("gettimeofday");
+		break;
+	case 170: // settimeofday
+		ret = enosys ("settimeofday");
+		break;
+	case 171: // adjtimex
+		ret = enosys ("adjtimex");
+		break;
+	case 172: // getpid
+		ret = map (getpid ());
+		break;
+	case 174: // getuid
+		ret = map (getuid ());
+		break;
+	case 175: // geteuid
+		ret = map (geteuid ());
+		break;
+	case 176: // getgid
+		ret = map (getgid ());
+		break;
+	case 177: // getegid
+		ret = map (getegid ());
+		break;
+	case 178: // gettid
+		ret = enosys ("gettid");
+		break;
+	case 179: // sysinfo
+		ret = enosys ("sysinfo");
+		break;
+	case 180:
+	case 181:
+	case 182:
+	case 183:
+	case 184:
+	case 185:
+		ret = enosys ("mq_*");
+		break;
+	case 186: // msgget
+		ret = map (msgget ((key_t)a0, (int)a1));
+		break;
+	case 187: // msgctl
+		ret = enosys ("msgctl");
+		break;
+	case 188: // msgrcv
+		ret = map (msgrcv ((int)a0, ptr (void, a1), (size_t)a2, (long)a3, (int)a4));
+		break;
+	case 189: // msgsnd
+		ret = map (msgsnd ((int)a0, ptr (const void, a1), (size_t)a2, (int)a3));
+		break;
+	case 190: // semget
+		ret = map (semget ((key_t)a0, (int)a1, (int)a2));
+		break;
+	case 191: // semctl
+		ret = enosys ("semctl");
+		break;
+	case 192: // semtimedop
+		ret = enosys ("semtimedop");
+		break;
+	case 193: // semop
+		ret = enosys ("semop");
+		break;
+	case 194:
+	case 195:
+	case 196:
+	case 197:
+		ret = enosys ("shm*");
+		break;
+	case 198:
+	case 199:
+	case 200:
+	case 201:
+	case 202:
+	case 203:
+	case 204:
+	case 205:
+	case 206:
+	case 207:
+	case 208:
+	case 209:
+	case 210:
+	case 211:
+	case 212:
+		ret = enosys ("socket api");
+		break;
+	case 213: // readahead
+		ret = enosys ("readahead");
+		break;
+	case 214: // brk
+		ret = map (brk ((void *)(size_t)a0));
+		break;
+	case 215: // munmap
+		ret = map (munmap (ptr (void, a0), (size_t)a1));
+		break;
+	case 216: // mremap
+		ret = enosys ("mremap");
+		break;
+	case 217: // add_key
+		ret = enosys ("add_key");
+		break;
+	case 218: // request_key
+		ret = enosys ("request_key");
+		break;
+	case 219: // keyctl
+		ret = enosys ("keyctl");
+		break;
+	case 220: // clone
+		ret = enosys ("clone");
+		break;
+	case 221: // execve
+		ret = my_execve (str (a0), ptr (char *, a1), ptr (char *, a2));
+		break;
+	case 222: // mmap
+		ptr = mmap (ptr (void, a0), (size_t)a1, (int)a2, (int)a3, (int)a4, (off_t)a5);
+		if (ptr == NULL) {
+			ret = -map_errno (errno);
+		} else if ((size_t)ptr > INT32_MAX) {
+			ret = -map_errno (EINVAL);
+		} else {
+			ret = (size_t)ptr;
+		}
+		break;
+	case 223: // fadvise64
+		ret = enosys ("fadvise64");
+		break;
+	case 224: // swapon
+		ret = enosys ("swapon");
+		break;
+	case 225: // swapoff
+		ret = enosys ("swapoff");
+		break;
+	case 226: // mprotect
+		ret = map (mprotect (ptr (void, a0), (size_t)a1, (int)a2));
+		break;
+	case 227: // msync
+		ret = map (msync (ptr (void, a0), (size_t)a1, (int)a2));
+		break;
+	case 228: // mlock
+		ret = map (mlock (ptr (void, a0), (size_t)a1));
+		break;
+	case 229: // munlock
+		ret = map (munlock (ptr (void, a0), (size_t)a1));
+		break;
+	case 230: // mlockall
+		ret = map (mlockall ((int)a0));
+		break;
+	case 231: // munlockall
+		ret = map (munlockall ());
+		break;
+	case 232: // mincore
+		ret = enosys ("mincore");
+		break;
+	case 233: // madvise
+		ret = map (madvise (ptr (void, a0), (size_t)a1, (int)a2));
+		break;
+	case 234: // remap_file_pages
+		ret = enosys ("remap_file_pages");
+		break;
+	case 235: // mbind
+		ret = enosys ("mbind");
+		break;
+	case 236: // get_mempolicy
+		ret = enosys ("get_mempolicy");
+		break;
+	case 237: // set_mempolicy
+		ret = enosys ("set_mempolicy");
+		break;
+	case 238: // migrate_pages
+		ret = enosys ("migrate_pages");
+		break;
+	case 239: // move_pages
+		ret = enosys ("move_pages");
+		break;
+	case 240: // rt_tgsigqueueinfo
+		ret = enosys ("rt_tgsigqueueinfo");
+		break;
+	case 241: // perf_event_open
+		ret = enosys ("perf_event_open");
+		break;
+	case 242: // accept4
+		ret = enosys ("accept4");
+		break;
+	case 243: // recvmsg
+		ret = enosys ("recvmsg");
+		break;
+	case 244:
+	case 245:
+	case 246:
+	case 247:
+	case 248:
+	case 249:
+	case 250:
+	case 251:
+	case 252:
+	case 253:
+	case 254:
+	case 255:
+	case 256:
+	case 257:
+	case 258:
+	case 259:
+		ret = enosys ("arch-specific");
+		break;
+	case 260:
+		ret = enosys ("wait4");
+		break;
+	case 261: // prlimit64
+		ret = enosys ("prlimit64");
+		break;
+	case 262: // fanotify_init
+		ret = enosys ("fanotify_init");
+		break;
+	case 263: // fanotify_mark
+		ret = enosys ("fanotify_mark");
+		break;
+	case 264: // name_to_handle_at
+		ret = enosys ("name_to_handle_at");
+		break;
+	case 265: // open_by_handle_at
+		ret = enosys ("open_by_handle_at");
+		break;
+	case 266: // clock_adjtime
+		ret = enosys ("clock_adjtime");
+		break;
+	case 267: // syncfs
+		ret = enosys ("syncfs");
+		break;
+	case 268: // setns
+		ret = enosys ("setns");
+		break;
+	case 269: // sendmsg
+		ret = enosys ("sendmsg");
+		break;
+	case 270: // process_vm_readv
+		ret = enosys ("process_vm_readv");
+		break;
+	case 271: // process_vm_writev
+		ret = enosys ("process_vm_writev");
+		break;
+	case 272: // kcmp
+		ret = enosys ("kcmp");
+		break;
+	case 273: // finit_module
+		ret = enosys ("finit_module");
+		break;
+	case 274: // sched_setattr
+		ret = enosys ("sched_setattr");
+		break;
+	case 275: // sched_getattr
+		ret = enosys ("sched_getattr");
+		break;
+	case 276: // renameat2
+		ret = enosys ("renameat2");
+		break;
+	case 277: // seccomp
+		ret = enosys ("seccomp");
+		break;
+	case 278: // getrandom
+		ret = enosys ("getrandom");
+		break;
+	case 279: // memfd_create
+		ret = enosys ("seccomp");
+		break;
+	case 280: // bpf
+		ret = enosys ("bpf");
+		break;
+	case 281: // execveat
+		ret = enosys ("execveat");
+		break;
+	case 282: // userfaultfd
+		ret = enosys ("usefaultfd");
+		break;
+	case 283: // membarrier
+		ret = enosys ("membarrier");
+		break;
+	case 284: // mlock2
+		ret = enosys ("mlock2");
+		break;
+	case 285: // copy_file_range
+		ret = enosys ("copy_file_range");
+		break;
+	case 286: // preadv2
+		ret = enosys ("preadv2");
+		break;
+	case 287: // pwritev2
+		ret = enosys ("pwritev2");
+		break;
+	case 288: // pkey_mprotect
+		ret = enosys ("pkey_mprotect");
+		break;
+	case 289: // pkey_alloc
+		ret = enosys ("pkey_alloc");
+		break;
+	case 290: // pkey_free
+		ret = enosys ("pkey_free");
+		break;
+	case 291: // statx
+		ret = enosys ("statx");
+		break;
+	case 292: // io_pgetevents
+		ret = enosys ("io_pgetevents");
+		break;
+	case 293: // rseq
+		ret = enosys ("rseq");
+		break;
+	case 294: // kexec_file_load
+		ret = enosys ("kexec_file_load");
+		break;
+	case 1024: // open
+		ret = map (open ((const char *)(size_t)a0, (int)a1, (int)a2));
+		break;
+	case 1025: // link
+		ret = map (link (str (a0), str (a1)));
+		break;
+	case 1026: // unlink
+		ret = map (unlink (str (a0)));
+		break;
+	case 1030: // mkdir
+		ret = map (mkdir (str (a0), (mode_t)a1));
+		break;
+	case 1033: // access
+		ret = map (access (str (a0), (int)a1));
+		break;
+	case 1038: // stat
+		ret = map (stat (str (a0), &st));
+		stat_to_linux_stat (&lst, &st);
+		memcpy (ptr (void, a1), &lst, sizeof (lst));
+		break;
+	case 1039: // lstat
+		ret = map (lstat (str (a0), &st));
+		stat_to_linux_stat (&lst, &st);
+		memcpy (ptr (void, a1), &lst, sizeof (lst));
+		break;
+	case 1062: // time
+		ret = enosys ("time");
+		break;
+	case 2011: // getmainvars
+		ret = enosys ("getmainvars");
+		break;
+	case 2048:
+		fprintf (stderr, "DEBUG: %u\n", a0);
+		ret = 0;
+		break;
+	default:
+		warnx ("%08x: unimplemented syscall %u", cpu->pc - 4, a7);
+		ret = -map_errno (ENOSYS);
+		break;
+	}
+	cpu_set (cpu, 10, ret);
+}
+
+static void cpu_exec (struct cpu *cpu, u32 instr)
+{
+	const char *name;
+	const u8 rd = (instr >> 7) & 0x1f;
+	const u8 funct3 = (instr >> 12) & 0x7;
+	const u8 rs1 = (instr >> 15) & 0x1f;
+	const u8 rs2 = (instr >> 20) & 0x1f;
+	const u8 funct7 = instr >> 25;
+	const u32 imm_i = (i32)instr >> 20;
+	const u32 imm_s = ((i32)instr >> 25 << 5) | ((instr >> 7) & 0x1f);
+	const u32 imm_b = (instr >> 31 << 12)
+		| (((instr >> 25) & 0x3f) << 5)
+		| (((instr >> 8) & 0xf) << 1)
+		| (((instr >> 7) & 1) << 11);
+	const u32 imm_u = instr & 0xfffff000;
+	const u32 imm_j = ((i32)instr >> 31 << 20)
+		| (((instr >> 21) & 0x3ff) << 1)
+		| (((instr >> 20) & 1) << 11)
+		| (((instr >> 12) & 0xff) << 12);
+
+	u32 a, b, c;
+	switch (instr & 0x7f) {
+	case 0b0110111: // lui rd, uimm
+		eprintf ("lui x%u, %u\n", (uint)rd, imm_u);
+		cpu_set (cpu, rd, imm_u);
+		break;
+	case 0b0010111: // auipc rd, uimm
+		eprintf ("auipc x%u, %u\n", (uint)rd, imm_u);
+		cpu_set (cpu, rd, cpu->pc + imm_u - 4);
+		break;
+	case 0b1101111: // jal rd, jimm
+		eprintf ("jal x%u, %d\n", (uint)rd, (int)imm_j);
+		cpu_set (cpu, rd, cpu->pc);
+		cpu->pc += imm_j - 4;
+		break;
+	case 0b1100111: // jalr rd, rs1, iimm
+		if (funct3 != 0b000)
+			goto ud;
+		eprintf ("jalr x%u, x%u, %d\n", (uint)rd, (uint)rs1, (int)imm_i);
+		cpu_set (cpu, rd, cpu->pc);
+		cpu->pc = cpu_get (cpu, rs1);
+		break;
+	case 0b1100011: // bcc rs1, rs2, bimm
+		a = cpu_get (cpu, rs1);
+		b = cpu_get (cpu, rs2);
+		switch (funct3) {
+		case 0b000:
+			name = "beq";
+			c = a == b;
+			break;
+		case 0b001:
+			name = "bne";
+			c = a != b;
+		case 0b100:
+			name = "blt";
+			c = (i32)a < (i32)b;
+			break;
+		case 0b101:
+			name = "bge";
+			c = (i32)a >= (i32)b;
+			break;
+		case 0b110:
+			name = "bltu";
+			c = a < b;
+			break;
+		case 0b111:
+			name = "bgeu";
+			c = a >= b;
+			break;
+		default:
+			goto ud;
+		}
+		eprintf ("%s x%u, x%u, %d\n", name, (uint)rs1, (uint)rs2, (int)imm_b);
+		if (c)
+			cpu->pc += imm_b - 4;
+		break;
+	case 0b0000011: // lx rd, iimm(rs1)
+		a = cpu_get (cpu, rs1) + imm_i;
+		switch (funct3) {
+		case 0b000: // lb
+			name = "lb";
+			b = (i32)*(const i8 *)(size_t)a;
+			break;
+		case 0b001: // lh
+			name = "lh";
+			b = (i32)*(const i16 *)(size_t)a;
+			break;
+		case 0b010: // lw
+			name = "lw";
+			b = *(const u32 *)(size_t)a;
+			break;
+		case 0b100: // lbu
+			name = "lbu";
+			b = (u32)*(const u8 *)(size_t)a;
+			break;
+		case 0b101: // lhu
+			name = "lhu";
+			b = (u32)*(const u16 *)(size_t)a;
+			break;
+		default:
+			goto ud;
+		}
+		eprintf ("%s x%u, %d(x%u)\n", name, (uint)rd, (int)imm_i, (uint)rs1);
+		cpu_set (cpu, rd, b);
+		break;
+	case 0b0100011: // sx rs2, simm(rs1)
+		a = cpu_get (cpu, rs2);
+		b = cpu_get (cpu, rs1) + imm_s;
+		switch (funct3) {
+		case 0b000: // sb
+			name = "sb";
+			*(u8 *)(size_t)b = a;
+			break;
+		case 0b001: // sh
+			name = "sh";
+			*(u16 *)(size_t)b = a;
+			break;
+		case 0b010: // sw
+			name = "sw";
+			*(u32 *)(size_t)b = a;
+			break;
+		default:
+			goto ud;
+		}
+		eprintf ("%s x%u, %d(x%u)\n", name, (uint)rs2, (int)imm_s, (uint)rs1);
+		break;
+	case 0b0010011: // alui rd, rs1, iimm
+		a = cpu_get (cpu, rs1);
+		b = imm_i;
+		switch (funct3) {
+		case 0b000: // addi
+			name = "addi";
+			c = a + b;
+			break;
+		case 0b001: // slli
+			name = "slli";
+			c = a << (b & 0x1f);
+			break;
+		case 0b010: // slti
+			name = "slti";
+			c = (i32)a < (i32)b;
+			break;
+		case 0b011: // sltiu
+			name = "sltiu";
+			c = a < b;
+			break;
+		case 0b100: // xori
+			name = "xori";
+			c = a ^ b;
+			break;
+		case 0b101: // srli/srai
+			if ((instr >> 30) & 1) {
+				name = "srai";
+				c = (i32)a >> b;
+			} else {
+				name = "srli";
+				c = a >> b;
+			}
+			break;
+		case 0b110: // ori
+			name = "ori";
+			c = a | b;
+			break;
+		case 0b111: // andi
+			name = "andi";
+			c = a & b;
+			break;
+		default:
+			goto ud;
+		}
+		eprintf ("%s x%u, x%u, %d\n", name, (uint)rd, (uint)rs1, (int)imm_i);
+		cpu_set (cpu, rd, c);
+		break;
+	case 0b0110011: // alu rd, rs1, rs2
+		a = cpu_get (cpu, rs1);
+		b = cpu_get (cpu, rs2);
+		c = (instr >> 30) & 1;
+		switch (funct3) {
+		case 0b000: // add/sub
+			if (c) {
+				name = "sub";
+				a -= b;
+			} else {
+				name = "add";
+				a += b;
+			}
+			break;
+		case 0b001: // sll
+			name = "sll";
+			a <<= b;
+			break;
+		case 0b010: // slt
+			name = "slt";
+			a = (i32)a < (i32)b;
+			break;
+		case 0b011: // sltu
+			name = "sltu";
+			a = a < b;
+			break;
+		case 0b100: // xor
+			name = "xor";
+			a ^= b;
+			break;
+		case 0b101: // srl/sra
+			if (c) {
+				name = "sra";
+				a = (i32)a >> b;
+			} else {
+				name = "srl";
+				a >>= b;
+			}
+			break;
+		case 0b110: // or
+			name = "or";
+			a |= b;
+			break;
+		case 0b111: // and
+			name = "and";
+			a &= b;
+			break;
+		}
+		eprintf ("%s x%u, x%u, x%u\n", name, (uint)rd, (uint)rs1, (uint)rs2);
+		cpu_set (cpu, rd, a);
+		break;
+	case 0b0001111: // fence/fence.tso/pause
+		eprintf ("fence\n");
+		break;
+	case 0b1110011: // ecall/ebreak
+		eprintf (
+			"ecall a0=%u, a1=%u, a2=%u, a3=%u, a4=%u, a5=%u, a7=%u\n",
+			cpu_get (cpu, 10),
+			cpu_get (cpu, 11),
+			cpu_get (cpu, 12),
+			cpu_get (cpu, 13),
+			cpu_get (cpu, 14),
+			cpu_get (cpu, 15),
+			cpu_get (cpu, 17)
+		);
+		ecall (cpu);
+		break;
+	default:
+	ud:
+		errx (1, "%08x: invalid instruction: %08x", cpu->pc - 4, instr);
+	}
+}
+
+static void load_image (struct cpu *cpu, const char *filename)
+{
+	Elf32_Ehdr ehdr;
+	int fd;
+
+	fd = open (filename, O_RDONLY);
+	if (fd < 0)
+		err (1, "open('%s')", filename);
+
+	if (read (fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr))
+		err (1, "read('%s')", filename);
+
+	if (!IS_ELF (ehdr))
+		errx (1, "not ELF");
+
+	if (ehdr.e_ident[EI_CLASS] != ELFCLASS32)
+		errx (1, "not 32bit");
+
+	if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
+		errx (1, "not little endian");
+
+	//if (ehdr.e_ident[EI_OSABI] != ELFOSABI_LINUX)
+	//	errx (1, "not linux");
+
+	if (ehdr.e_machine != EM_RISCV)
+		errx (1, "invalid e_machine");
+
+	cpu->pc = ehdr.e_entry;
+
+	for (unsigned i = 0; i < ehdr.e_phnum; ++i) {
+		Elf32_Phdr phdr;
+		u32 flags = 0;
+		void *ptr;
+
+		if (phdr.p_flags & PF_R)
+			flags |= PROT_READ;
+		if (phdr.p_flags & PF_W)
+			flags |= PROT_WRITE;
+		if (phdr.p_flags & PF_X)
+			flags |= PROT_EXEC;
+
+		lseek (fd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET);
+		if (read (fd, &phdr, ehdr.e_phentsize) != ehdr.e_phentsize)
+			err (1, "failed to read program header %u", i);
+
+		switch (phdr.p_type) {
+		case PT_NULL:
+			break;
+		case PT_LOAD:
+			eprintf ("loading %08x into %08x flags %x memsz %u\n",
+				phdr.p_offset, phdr.p_vaddr, phdr.p_flags, phdr.p_memsz);
+			if (phdr.p_filesz == phdr.p_memsz) {
+				ptr = mmap (
+					(void *)(size_t)phdr.p_vaddr,
+					phdr.p_memsz,
+					flags,
+					MAP_PRIVATE | MAP_COPY,
+					fd,
+					phdr.p_offset
+				);
+			} else if (phdr.p_filesz == 0) {
+				ptr = mmap (
+					(void *)(size_t)phdr.p_vaddr,
+					phdr.p_memsz,
+					flags,
+					MAP_ANON | MAP_PRIVATE,
+					-1,
+					0
+				);
+			} else {
+				errx (1, "%u: p_filesz: %u, p_memsz: %u",
+					i, phdr.p_filesz, phdr.p_memsz);
+			}
+
+			if ((size_t)ptr != (size_t)phdr.p_vaddr)
+				err (1, "%u: failed to map program header", i);
+			break;
+		case PT_INTERP:
+		case PT_DYNAMIC:
+			errx (1, "shared objects not supported");
+		default:
+			//warnx ("%u: ignoring program header: %x",
+			//	i, (unsigned)phdr.p_type);
+			break;
+		}
+	}
+	close (fd);
+}
+
+static void cpu_push (struct cpu *cpu, u32 val)
+{
+	cpu_set (cpu, 2, cpu_get (cpu, 2) - 4);
+	*(u32 *)(size_t)cpu_get (cpu, 2) = val;
+}
+
+static void cpu_push_str (struct cpu *cpu, const char *ptr)
+{
+	if ((size_t)ptr > 0x80000000) {
+		const size_t len = strlen (ptr);
+		char *nptr = sbrk (len + 1);
+		memcpy (nptr, ptr, len + 1);
+		assert ((size_t)nptr < 0x80000000);
+		ptr = nptr;
+	}
+	cpu_push (cpu, (u32)(size_t)ptr);
+}
+
+static void setup_stack (
+	struct cpu *cpu,
+	u32 stack_size,
+	int argc,
+	char **argv,
+	int envc,
+	char **envp
+) {
+	void *ptr;
+	void *stack_bottom;
+
+	// set stack pointer
+	cpu_set (cpu, 2, 0x80000000);
+
+	stack_bottom = (void *)(size_t)(0x80000000 - stack_size);
+	ptr = mmap (
+		stack_bottom,
+		stack_size,
+		PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANON | MAP_STACK,
+		-1,
+		0
+	);
+
+	if (ptr != stack_bottom)
+		err (1, "failed to map stack");
+
+	cpu_push (cpu, 0);
+	for (int i = envc - 1; i >= 0; --i)
+		cpu_push_str (cpu, envp[i]);
+	cpu_push (cpu, 0);
+	for (int i = argc; i >= 0; --i)
+		cpu_push_str (cpu, argv[i]);
+	cpu_push (cpu, argc);
+}
+
+int main (int argc, char *argv[], char *envp[])
+{
+	struct cpu cpu;
+	const char *filename;
+	struct rlimit rl;
+	size_t stack_size;
+	int fd, option, envc;
+	void *ptr;
+
+	while ((option = getopt (argc, argv, "")) != -1) {
+		switch (option) {
+		default:
+			return 1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1)
+		errx (1, "usage: rvemu file");
+
+	filename = argv[0];
+	memset (&cpu, 0, sizeof (cpu));
+	load_image (&cpu, filename);
+
+	if (getrlimit (RLIMIT_STACK, &rl) == 0) {
+		stack_size = rl.rlim_cur;
+	} else {
+		stack_size = 1 << 20;
+	}
+
+	for (envc = 0; envp[envc] != NULL; ++envc);
+
+	setup_stack (&cpu, stack_size, argc, argv, envc, envp);
+
+	while (1) {
+		const u32 instr = cpu_fetch (&cpu);
+		cpu_exec (&cpu, instr);
+	}
+
+	return 0;
+}
blob - /dev/null
blob + 2ebd0fad6472bfc86aec71f22929eb1e170bfa13 (mode 644)
--- /dev/null
+++ test.S
@@ -0,0 +1,66 @@
+.section .text
+.global _start
+.type _start, %function
+_start:
+	addi sp, sp, -4
+
+	li t0, 10
+	sw t0, 0(sp)
+
+	li a0, 0x42
+	jal printhex
+
+	li a0, 1
+	mv a1, sp
+	li a2, 1
+	li a7, 64
+	ecall
+
+	li a0, 0
+	li a7, 93
+	ecall
+
+# printhex(u32 a0)
+printhex:
+	addi sp, sp, -12
+	sw s0, 8(sp)
+	sw s1, 4(sp)
+	li s0, 28
+	mv s1, a0
+
+.Loop:
+	srl t0, s1, s0
+	andi t0, t0, 0xf
+
+	li t1, 10
+	blt t0, t1, .Ldec
+	addi t0, t0, 55
+	j .Lprint
+
+.Ldec:
+	addi t0, t0, 48
+
+.Lprint:
+	sb t0, 0(sp)
+	li a0, 1
+	mv a1, sp
+	li a2, 2
+	li a7, 64
+	ecall
+
+	beq s0, zero, .Lret
+	addi s0, s0, -4
+	j .Loop
+
+.Lret:
+	lw s1, 4(sp)
+	lw s0, 8(sp)
+	addi sp, sp, 12
+	ret
+
+
+.section .rodata
+str:	.string "Hello World\n"
+
+.section .data
+buf:	.space 1
blob - /dev/null
blob + a5826bdff3660ddabb245f576fe5539b00f2243e (mode 644)
--- /dev/null
+++ test.ld
@@ -0,0 +1,23 @@
+ENTRY(_start)
+
+SECTIONS {
+	. = 1M;
+
+	.text : ALIGN(4K) {
+		*(.text*)
+	}
+
+	.rodata : ALIGN(4K) {
+		*(.rodata*)
+	}
+
+	.data : ALIGN(4K) {
+		*(.data*)
+	}
+
+	.bss : ALIGN(4K) {
+		*(COMMON)
+		*(.bss*)
+	}
+
+}